org.opendatakit.security.spring.UserDetailsServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.opendatakit.security.spring.UserDetailsServiceImpl.java

Source

/*
 * Copyright (C) 2010 University of Washington
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.opendatakit.security.spring;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import org.opendatakit.persistence.Datastore;
import org.opendatakit.persistence.exception.ODKDatastoreException;
import org.opendatakit.persistence.table.RegisteredUsersTable;
import org.opendatakit.persistence.table.UserGrantedAuthority;
import org.opendatakit.security.User;
import org.opendatakit.security.UserService;
import org.opendatakit.security.common.GrantedAuthorityName;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * Implementation of a user details service that fetches data from the {@link RegisteredUsersTable}
 * to report on registered users.
 * 
 * @author mitchellsundt@gmail.com
 *
 */
public class UserDetailsServiceImpl implements UserDetailsService, InitializingBean {

    public enum PasswordType {
        BasicAuth, DigestAuth, Random
    };

    public enum CredentialType {
        Username, Email, Token // Out-of-band (Oauth) or Oauth2 token
    };

    private Datastore datastore;
    private UserService userService;
    private PasswordType passwordType = PasswordType.Random;
    private CredentialType credentialType = CredentialType.Username;
    private Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();

    public UserDetailsServiceImpl() {
    }

    public Datastore getDatastore() {
        return datastore;
    }

    public void setDatastore(Datastore datastore) {
        this.datastore = datastore;
    }

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void setPasswordType(String type) {
        if (PasswordType.BasicAuth.name().equals(type)) {
            passwordType = PasswordType.BasicAuth;
        } else if (PasswordType.DigestAuth.name().equals(type)) {
            passwordType = PasswordType.DigestAuth;
        } else if (PasswordType.Random.name().equals(type)) {
            passwordType = PasswordType.Random;
        } else {
            throw new IllegalArgumentException("Unrecognized PasswordType");
        }
    }

    public void setPasswordType(PasswordType type) {
        this.passwordType = type;
    }

    public void setCredentialType(String type) {
        if (CredentialType.Username.name().equals(type)) {
            credentialType = CredentialType.Username;
        } else if (CredentialType.Email.name().equals(type)) {
            credentialType = CredentialType.Email;
        } else if (CredentialType.Token.name().equals(type)) {
            credentialType = CredentialType.Token;
        } else {
            throw new IllegalArgumentException("Unrecognized CredentialType");
        }
    }

    public void setCredentialType(CredentialType type) {

        this.credentialType = type;
    }

    public void setAuthorities(List<SimpleGrantedAuthority> authorities) {
        this.authorities.clear();
        for (SimpleGrantedAuthority a : authorities) {
            this.authorities.add(a);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (datastore == null) {
            throw new IllegalStateException("datastore must be specified");
        }
        if (userService == null) {
            throw new IllegalStateException("userService must be specified");
        }
    }

    private Set<GrantedAuthority> getGrantedAuthorities(String uriUser) throws ODKDatastoreException {
        User user = userService.getDaemonAccountUser();
        Set<GrantedAuthority> grantedAuthorities = UserGrantedAuthority.getGrantedAuthorities(uriUser, datastore,
                user);
        grantedAuthorities.add(new SimpleGrantedAuthority(GrantedAuthorityName.USER_IS_REGISTERED.name()));
        grantedAuthorities.addAll(authorities);
        return grantedAuthorities;
    }

    @Override
    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException, DataAccessException {
        if (name == null) {
            throw new IllegalStateException("Username cannot be null");
        }

        User user = userService.getDaemonAccountUser();

        final String uriUser;
        final String password;
        final String salt;
        final Set<GrantedAuthority> grantedAuthorities;
        final boolean isEnabled = true;
        final boolean isCredentialNonExpired = true;
        try {
            if (credentialType == CredentialType.Username) {
                RegisteredUsersTable registeredUsersTable;
                // first call from digest, basic or forms-based auth
                if (name.startsWith(RegisteredUsersTable.UID_PREFIX)) {
                    registeredUsersTable = RegisteredUsersTable.getUserByUri(name, datastore, user);
                    if (registeredUsersTable == null) {
                        throw new UsernameNotFoundException("UID " + name + " is not recognized.");
                    }
                } else {
                    registeredUsersTable = RegisteredUsersTable.getUniqueUserByUsername(name, datastore, user);
                    if (registeredUsersTable == null) {
                        throw new UsernameNotFoundException(
                                "User " + name + " is not registered or the registered users table is corrupt.");
                    }
                }
                uriUser = registeredUsersTable.getUri();

                // Along with BasicUsingDigest* classes, we allow both types of authentication to use the
                // same DB field for password.
                switch (passwordType) {
                case BasicAuth:
                    // password = registeredUsersTable.getBasicAuthPassword();
                    // salt = registeredUsersTable.getBasicAuthSalt();
                    // break;
                case DigestAuth:
                    password = registeredUsersTable.getDigestAuthPassword();
                    salt = UUID.randomUUID().toString();
                    break;
                default:
                    throw new AuthenticationCredentialsNotFoundException(
                            "Password type " + passwordType.toString() + " cannot be interpretted");
                }

                grantedAuthorities = getGrantedAuthorities(registeredUsersTable.getUri());
                if (password == null) {
                    throw new AuthenticationCredentialsNotFoundException("User " + name
                            + " does not have a password configured. You must close and re-open your browser to clear this error.");
                }
            } else {
                // OAuth2 token...
                // there is no password for an OAuth2 credential
                if (passwordType != PasswordType.Random) {
                    throw new AuthenticationCredentialsNotFoundException(
                            "Password type " + passwordType.toString() + " cannot be interpretted");
                }
                // set password and salt to unguessable strings...
                password = UUID.randomUUID().toString();
                salt = UUID.randomUUID().toString();

                // try to find user in registered users table...
                RegisteredUsersTable eUser = RegisteredUsersTable.getUniqueUserByEmail(name, datastore, user);
                if (eUser != null) {
                    uriUser = eUser.getUri();
                    grantedAuthorities = getGrantedAuthorities(eUser.getUri());
                } else {
                    throw new UsernameNotFoundException("User " + name + " is not registered");
                }
            }
        } catch (ODKDatastoreException e) {
            throw new TransientDataAccessResourceException("persistence layer problem", e);
        }

        return new OdkServerUser(uriUser, password, salt, "-undefined-", isEnabled, true, isCredentialNonExpired,
                true, grantedAuthorities);
    }
}