com.parallax.server.blocklyprop.security.CloudSessionAuthenticationRealm.java Source code

Java tutorial

Introduction

Here is the source code for com.parallax.server.blocklyprop.security.CloudSessionAuthenticationRealm.java

Source

/*
 * Copyright (c) 2019 Parallax Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files (the Software?), to deal in the Software without
 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED AS IS?, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.parallax.server.blocklyprop.security;

import com.parallax.client.cloudsession.exceptions.EmailNotConfirmedException;
import com.parallax.client.cloudsession.exceptions.InsufficientBucketTokensException;
import com.parallax.client.cloudsession.exceptions.UnknownUserException;
import com.parallax.client.cloudsession.exceptions.UserBlockedException;
import com.parallax.client.cloudsession.objects.User;

import com.parallax.server.blocklyprop.services.impl.SecurityServiceImpl;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAccount;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Interface to the Cloud Session server authentication service
 * <p>
 * Additional notes:
 * 
 * An AuthorizingRealm extends the AuthenticatingRealm's capabilities by adding
 * Authorization (access control) support.
 * 
 * This implementation will perform all role and permission checks automatically
 * (and subclasses do not have to write this logic) as long as the 
 * getAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) method
 * returns an AuthorizationInfo. Please see that method's JavaDoc for an
 * in-depth explanation.
 * 
 * If you find that you do not want to utilize the AuthorizationInfo construct,
 * you are of course free to subclass the AuthenticatingRealm directly instead
 * and implement the remaining Realm interface methods directly. You might do
 * this if you want have better control over how the Role and Permission checks
 * occur for your specific data source. However, using AuthorizationInfo
 * (and its default implementation SimpleAuthorizationInfo) is sufficient in the
 * large majority of Realm cases.
 * 
 * @author Michel
 */
public class CloudSessionAuthenticationRealm extends AuthorizingRealm {

    // Get a handle to a logger for this class
    private static Logger LOG = LoggerFactory.getLogger(CloudSessionAuthenticationRealm.class);

    /**
     * Convenience implementation that returns 
     * getAuthenticationTokenClass().isAssignableFrom( token.getClass() ); 
     * <p>
     * Can be overridden by subclasses for more complex token checking.
     * <p>
     * Notes: Most configurations will only need to set a different class via
     * setAuthenticationTokenClass(java.lang.Class<? extends org.apache.shiro.authc.AuthenticationToken>),
     * as opposed to overriding this method.
     * 
     * @param token  the token being submitted for authentication.
     * @return true if this authentication realm can process the submitted token
     * instance of the class, false otherwise.
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return true;
    }

    /**
     * Retrieves the AuthorizationInfo for the given principals from the
     * underlying data store.
     * <p>
     * When returning an instance from this method, you might want to consider
     * using an instance of SimpleAuthorizationInfo, as it is suitable in 
     * most cases.
     * 
     * @param principals  the primary identifying principals of the 
     * AuthorizationInfo that should be retrieved.
     * @return the AuthorizationInfo associated with this principals.
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        LOG.debug("Authorization info");
        AuthorizationInfo authorizationInfo = new SimpleAccount();

        LOG.info("AuthInfo() details: {}", authorizationInfo.getRoles().size());
        return authorizationInfo;
    }

    /**
     * Retrieves authentication data from an implementation-specific data source
     * (RDBMS, LDAP, etc) for the given authentication token.
     * <p>
     * For most data sources, this means just 'pulling' authentication data for
     * an associated subject/user and nothing more and letting Shiro do the
     * rest. But in some systems, this method could actually perform EIS 
     * specific log-in logic in addition to just retrieving data - it is up to
     * the Realm implementation.
     * <p>
     * A null return value means that no account could be associated with the
     * specified token.
        
     * @param token
     * The authentication token containing the user's principal and credentials.
     *
     * @return
     * Returns an AuthenticationInfo object containing account data resulting
     * from the authentication ONLY if the lookup is successful (i.e. account
     * exists and is valid, etc.)
     *
     * @throws AuthenticationException
     * if there is an error acquiring data or  performing realm-specific
     * authentication logic for the specified token
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        LOG.info("Obtaining authentication info");

        /*  Any leading and/or trailing white space contained in the credentials
         * (password) has been stripped out before it gets here.
         */
        try {
            if (token instanceof OAuthToken) {
                // Principal = email
                // Credentials = authenticator
                LOG.info("Authentication is using OAuth");
                return new SimpleAccount(token.getPrincipal(), token.getCredentials(), "CloudSession");
            } else {
                LOG.info("Authentication is using local login authority");

                // Principal = login
                String principal = (String) token.getPrincipal();

                // Credentials = password
                String credentials = new String((char[]) token.getCredentials());

                LOG.info("Authenticating user '{}'", principal);

                // Thia can throw a NullPointerException
                User user = SecurityServiceImpl.authenticateLocalUserStatic(principal, credentials);

                if (user == null) {
                    LOG.info("No exception but user object is null");
                    return null;
                }

                LOG.info("User {} is authenticated", principal);

                try {
                    return new SimpleAccount(token.getPrincipal(), token.getCredentials(), "CloudSession");
                } catch (Throwable t) {
                    LOG.error("Unexpected exception creating account object", t);
                }
            }
            throw new AuthenticationException("Unable to authenticate token");
        } catch (UnknownUserException ex) {
            LOG.warn("Authentication failed. Message: {}", ex.getMessage());
            throw new AuthenticationException(ex.getMessage());
        } catch (UserBlockedException ex) {
            LOG.warn("Blocked user {}", ex);
            throw new AuthenticationException(ex.getMessage());
        } catch (EmailNotConfirmedException ex) {
            LOG.warn("Authentication failed. Message: {}", ex.getMessage());
            throw new AuthenticationException("EmailNotConfirmed");
        } catch (InsufficientBucketTokensException ex) {
            LOG.info("Insufficient bucket tokens: {}", ex.getMessage());
            throw new AuthenticationException(ex.getMessage());
        } catch (NullPointerException npe) {
            LOG.warn("NullPointer", npe);
            throw new AuthenticationException(npe.getMessage());
        } catch (Throwable t) {
            // This is a catchall exception handler that kicks the can back
            // to the caller
            LOG.warn("Throwable", t);
        }

        return null;
    }

}