org.usergrid.security.shiro.Realm.java Source code

Java tutorial

Introduction

Here is the source code for org.usergrid.security.shiro.Realm.java

Source

/*******************************************************************************
 * Copyright 2012 Apigee Corporation
 * 
 * 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.usergrid.security.shiro;

import static org.apache.commons.lang.StringUtils.isBlank;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_LOGIN_ALLOWED;
import static org.usergrid.persistence.cassandra.CassandraService.MANAGEMENT_APPLICATION_ID;
import static org.usergrid.security.shiro.utils.SubjectUtils.getPermissionFromPath;
import static org.usergrid.utils.StringUtils.stringOrSubstringAfterFirst;
import static org.usergrid.utils.StringUtils.stringOrSubstringBeforeFirst;

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

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.CredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.usergrid.management.AccountCreationProps;
import org.usergrid.management.ApplicationInfo;
import org.usergrid.management.ManagementService;
import org.usergrid.management.OrganizationInfo;
import org.usergrid.management.UserInfo;
import org.usergrid.persistence.Entity;
import org.usergrid.persistence.EntityManager;
import org.usergrid.persistence.EntityManagerFactory;
import org.usergrid.persistence.Results;
import org.usergrid.persistence.Results.Level;
import org.usergrid.persistence.SimpleEntityRef;
import org.usergrid.persistence.entities.Role;
import org.usergrid.persistence.entities.User;
import org.usergrid.security.shiro.credentials.AccessTokenCredentials;
import org.usergrid.security.shiro.credentials.AdminUserAccessToken;
import org.usergrid.security.shiro.credentials.AdminUserPassword;
import org.usergrid.security.shiro.credentials.ApplicationAccessToken;
import org.usergrid.security.shiro.credentials.ApplicationUserAccessToken;
import org.usergrid.security.shiro.credentials.ClientCredentials;
import org.usergrid.security.shiro.credentials.OrganizationAccessToken;
import org.usergrid.security.shiro.credentials.PrincipalCredentials;
import org.usergrid.security.shiro.principals.AdminUserPrincipal;
import org.usergrid.security.shiro.principals.ApplicationGuestPrincipal;
import org.usergrid.security.shiro.principals.ApplicationPrincipal;
import org.usergrid.security.shiro.principals.ApplicationUserPrincipal;
import org.usergrid.security.shiro.principals.OrganizationPrincipal;
import org.usergrid.security.shiro.principals.PrincipalIdentifier;
import org.usergrid.security.tokens.TokenInfo;
import org.usergrid.security.tokens.TokenService;

import com.google.common.collect.HashBiMap;

public class Realm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(Realm.class);

    public final static String ROLE_SERVICE_ADMIN = "service-admin";
    public final static String ROLE_ADMIN_USER = "admin-user";
    public final static String ROLE_ORGANIZATION_ADMIN = "organization-admin";
    public final static String ROLE_APPLICATION_ADMIN = "application-admin";
    public final static String ROLE_APPLICATION_USER = "application-user";

    private EntityManagerFactory emf;
    private ManagementService management;
    private TokenService tokens;

    @Value("${" + PROPERTIES_SYSADMIN_LOGIN_ALLOWED + "}")
    private boolean superUserEnabled;
    @Value("${" + AccountCreationProps.PROPERTIES_SYSADMIN_LOGIN_NAME + ":admin}")
    private String superUser;

    public Realm() {
        setCredentialsMatcher(new AllowAllCredentialsMatcher());
        setPermissionResolver(new CustomPermissionResolver());
    }

    public Realm(CacheManager cacheManager) {
        super(cacheManager);
        setCredentialsMatcher(new AllowAllCredentialsMatcher());
        setPermissionResolver(new CustomPermissionResolver());
    }

    public Realm(CredentialsMatcher matcher) {
        super(new AllowAllCredentialsMatcher());
        setPermissionResolver(new CustomPermissionResolver());
    }

    public Realm(CacheManager cacheManager, CredentialsMatcher matcher) {
        super(cacheManager, new AllowAllCredentialsMatcher());
        setPermissionResolver(new CustomPermissionResolver());
    }

    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        if (!(credentialsMatcher instanceof AllowAllCredentialsMatcher)) {
            logger.debug("Replacing {} with AllowAllCredentialsMatcher", credentialsMatcher);
            credentialsMatcher = new AllowAllCredentialsMatcher();
        }
        super.setCredentialsMatcher(credentialsMatcher);
    }

    @Override
    public void setPermissionResolver(PermissionResolver permissionResolver) {
        if (!(permissionResolver instanceof CustomPermissionResolver)) {
            logger.debug("Replacing {} with AllowAllCredentialsMatcher", permissionResolver);
            permissionResolver = new CustomPermissionResolver();
        }
        super.setPermissionResolver(permissionResolver);
    }

    @Autowired
    public void setEntityManagerFactory(EntityManagerFactory emf) {
        this.emf = emf;
    }

    @Autowired
    public void setManagementService(ManagementService management) {
        this.management = management;
    }

    @Autowired
    public void setTokenService(TokenService tokens) {
        this.tokens = tokens;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        PrincipalCredentialsToken pcToken = (PrincipalCredentialsToken) token;

        if (pcToken.getCredentials() == null) {
            throw new CredentialsException("Missing credentials");
        }

        boolean authenticated = false;

        PrincipalIdentifier principal = pcToken.getPrincipal();
        PrincipalCredentials credentials = pcToken.getCredentials();

        if (credentials instanceof ClientCredentials) {
            authenticated = true;
        } else if ((principal instanceof AdminUserPrincipal) && (credentials instanceof AdminUserPassword)) {
            authenticated = true;
        } else if ((principal instanceof AdminUserPrincipal) && (credentials instanceof AdminUserAccessToken)) {
            authenticated = true;
        } else if ((principal instanceof ApplicationUserPrincipal)
                && (credentials instanceof ApplicationUserAccessToken)) {
            authenticated = true;
        } else if ((principal instanceof ApplicationPrincipal) && (credentials instanceof ApplicationAccessToken)) {
            authenticated = true;
        } else if ((principal instanceof OrganizationPrincipal)
                && (credentials instanceof OrganizationAccessToken)) {
            authenticated = true;
        }

        if (principal != null) {
            if (!principal.isActivated()) {
                throw new AuthenticationException("Unactivated identity");
            }
            if (principal.isDisabled()) {
                throw new AuthenticationException("Disabled identity");
            }
        }

        if (!authenticated) {
            throw new AuthenticationException("Unable to authenticate");
        }

        logger.debug("Authenticated: {}", principal);

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(pcToken.getPrincipal(),
                pcToken.getCredentials(), getName());
        return info;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        Map<UUID, String> organizationSet = HashBiMap.create();
        Map<UUID, String> applicationSet = HashBiMap.create();
        OrganizationInfo organization = null;
        ApplicationInfo application = null;

        for (PrincipalIdentifier principal : principals.byType(PrincipalIdentifier.class)) {

            if (principal instanceof OrganizationPrincipal) {
                // OrganizationPrincipals are usually only through OAuth
                // They have access to a single organization

                organization = ((OrganizationPrincipal) principal).getOrganization();

                role(info, principal, ROLE_ORGANIZATION_ADMIN);
                role(info, principal, ROLE_APPLICATION_ADMIN);

                grant(info, principal, "organizations:access:" + organization.getUuid());
                organizationSet.put(organization.getUuid(), organization.getName());

                Map<UUID, String> applications = null;
                try {
                    applications = management.getApplicationsForOrganization(organization.getUuid());
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if ((applications != null) && !applications.isEmpty()) {
                    grant(info, principal, "applications:admin,access,get,put,post,delete:"
                            + StringUtils.join(applications.keySet(), ','));

                    applicationSet.putAll(applications);
                }

            } else if (principal instanceof ApplicationPrincipal) {
                // ApplicationPrincipal are usually only through OAuth
                // They have access to a single application

                role(info, principal, ROLE_APPLICATION_ADMIN);

                application = ((ApplicationPrincipal) principal).getApplication();
                grant(info, principal, "applications:admin,access,get,put,post,delete:" + application.getId());
                applicationSet.put(application.getId(), application.getName());

            } else if (principal instanceof AdminUserPrincipal) {
                // AdminUserPrincipals are through basic auth and sessions
                // They have access to organizations and organization
                // applications

                UserInfo user = ((AdminUserPrincipal) principal).getUser();

                if (superUserEnabled && (superUser != null) && superUser.equals(user.getUsername())) {
                    // The system user has access to everything

                    role(info, principal, ROLE_SERVICE_ADMIN);
                    role(info, principal, ROLE_ORGANIZATION_ADMIN);
                    role(info, principal, ROLE_APPLICATION_ADMIN);
                    role(info, principal, ROLE_ADMIN_USER);

                    grant(info, principal, "system:access");

                    grant(info, principal, "organizations:admin,access,get,put,post,delete:*");
                    grant(info, principal, "applications:admin,access,get,put,post,delete:*");
                    grant(info, principal, "organizations:admin,access,get,put,post,delete:*:/**");
                    grant(info, principal, "applications:admin,access,get,put,post,delete:*:/**");
                    grant(info, principal, "users:access:*");

                    grant(info, principal, getPermissionFromPath(MANAGEMENT_APPLICATION_ID, "access"));

                    grant(info, principal,
                            getPermissionFromPath(MANAGEMENT_APPLICATION_ID, "get,put,post,delete", "/**"));

                } else {

                    // For regular service users, we find what organizations
                    // they're associated with
                    // An service user can be associated with multiple
                    // organizations

                    grant(info, principal, getPermissionFromPath(MANAGEMENT_APPLICATION_ID, "access"));

                    // admin users cannot access the management app directly
                    // so open all permissions
                    grant(info, principal,
                            getPermissionFromPath(MANAGEMENT_APPLICATION_ID, "get,put,post,delete", "/**"));

                    role(info, principal, ROLE_ADMIN_USER);

                    try {

                        Map<UUID, String> userOrganizations = management
                                .getOrganizationsForAdminUser(user.getUuid());

                        if (userOrganizations != null) {
                            for (UUID id : userOrganizations.keySet()) {
                                grant(info, principal, "organizations:admin,access,get,put,post,delete:" + id);
                            }
                            organizationSet.putAll(userOrganizations);

                            Map<UUID, String> userApplications = management
                                    .getApplicationsForOrganizations(userOrganizations.keySet());
                            if ((userApplications != null) && !userApplications.isEmpty()) {
                                grant(info, principal, "applications:admin,access,get,put,post,delete:"
                                        + StringUtils.join(userApplications.keySet(), ','));
                                applicationSet.putAll(userApplications);
                            }

                            role(info, principal, ROLE_ORGANIZATION_ADMIN);
                            role(info, principal, ROLE_APPLICATION_ADMIN);
                        }

                    } catch (Exception e) {
                        logger.error("Unable to construct admin user permissions", e);
                    }
                }
            } else if (principal instanceof ApplicationUserPrincipal) {

                role(info, principal, ROLE_APPLICATION_USER);

                UUID applicationId = ((ApplicationUserPrincipal) principal).getApplicationId();

                AccessTokenCredentials tokenCredentials = ((ApplicationUserPrincipal) principal)
                        .getAccessTokenCredentials();
                TokenInfo token = null;
                if (tokenCredentials != null) {
                    try {
                        token = tokens.getTokenInfo(tokenCredentials.getToken());
                    } catch (Exception e) {
                        logger.error("Unable to retrieve token info", e);
                    }
                    logger.debug("Token: {}", token);
                }

                grant(info, principal, getPermissionFromPath(applicationId, "access"));

                /*
                 * grant(info, principal, getPermissionFromPath(applicationId,
                 * "get,put,post,delete", "/users/${user}",
                 * "/users/${user}/feed", "/users/${user}/activities",
                 * "/users/${user}/groups", "/users/${user}/following/*",
                 * "/users/${user}/following/user/*"));
                 */

                EntityManager em = emf.getEntityManager(applicationId);
                try {
                    String appName = (String) em.getProperty(em.getApplicationRef(), "name");
                    applicationSet.put(applicationId, appName);
                    application = new ApplicationInfo(applicationId, appName);
                } catch (Exception e) {
                }

                try {
                    Set<String> permissions = em.getRolePermissions("default");
                    grant(info, principal, applicationId, permissions);
                } catch (Exception e) {
                    logger.error("Unable to get user default role permissions", e);
                }

                UserInfo user = ((ApplicationUserPrincipal) principal).getUser();
                try {
                    Set<String> permissions = em.getUserPermissions(user.getUuid());
                    grant(info, principal, applicationId, permissions);
                } catch (Exception e) {
                    logger.error("Unable to get user permissions", e);
                }

                try {
                    Set<String> rolenames = em.getUserRoles(user.getUuid());
                    grantAppRoles(info, em, applicationId, token, principal, rolenames);
                } catch (Exception e) {
                    logger.error("Unable to get user role permissions", e);
                }

                try {
                    //TODO TN.  This is woefully inefficient, but temporary.  Introduce cassandra backed shiro caching so this only ever happens once.
                    //See USERGRID-779 for details
                    Results r = em.getCollection(new SimpleEntityRef(User.ENTITY_TYPE, user.getUuid()), "groups",
                            null, 1000, Level.IDS, false);
                    if (r != null) {

                        Set<String> rolenames = new HashSet<String>();

                        for (UUID groupId : r.getIds()) {

                            Results roleResults = em.getCollection(new SimpleEntityRef(Role.ENTITY_TYPE, groupId),
                                    "roles", null, 1000, Level.CORE_PROPERTIES, false);

                            for (Entity entity : roleResults.getEntities()) {
                                rolenames.add(entity.getName());
                            }

                        }

                        grantAppRoles(info, em, applicationId, token, principal, rolenames);
                    }

                } catch (Exception e) {
                    logger.error("Unable to get user group role permissions", e);
                }

            } else if (principal instanceof ApplicationGuestPrincipal) {
                role(info, principal, ROLE_APPLICATION_USER);

                UUID applicationId = ((ApplicationGuestPrincipal) principal).getApplicationId();

                EntityManager em = emf.getEntityManager(applicationId);
                try {
                    String appName = (String) em.getProperty(em.getApplicationRef(), "name");
                    applicationSet.put(applicationId, appName);
                    application = new ApplicationInfo(applicationId, appName);
                } catch (Exception e) {
                }

                grant(info, principal, getPermissionFromPath(applicationId, "access"));

                try {
                    Set<String> permissions = em.getRolePermissions("guest");
                    grant(info, principal, applicationId, permissions);
                } catch (Exception e) {
                    logger.error("Unable to get user default role permissions", e);
                }
            }
        }

        // Store additional information in the request session to speed up
        // looking up organization info

        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        session.setAttribute("applications", applicationSet);
        session.setAttribute("organizations", organizationSet);
        if (organization != null) {
            session.setAttribute("organization", organization);
        }
        if (application != null) {
            session.setAttribute("application", application);
        }

        return info;
    }

    /**
     * Grant all permissions for the role names on this application
     * @param info
     * @param em
     * @param applicationId
     * @param token
     * @param principal
     * @param rolenames
     * @throws Exception
     */
    private void grantAppRoles(SimpleAuthorizationInfo info, EntityManager em, UUID applicationId, TokenInfo token,
            PrincipalIdentifier principal, Set<String> rolenames) throws Exception {
        Map<String, Role> app_roles = em.getRolesWithTitles(rolenames);

        for (String rolename : rolenames) {
            if ((app_roles != null) && (token != null)) {
                Role role = app_roles.get(rolename);
                if ((role != null) && (role.getInactivity() > 0) && (token.getInactive() > role.getInactivity())) {
                    continue;
                }
            }
            Set<String> permissions = em.getRolePermissions(rolename);
            grant(info, principal, applicationId, permissions);
            role(info, principal,
                    "application-role:".concat(applicationId.toString()).concat(":").concat(rolename));
        }
    }

    public static void grant(SimpleAuthorizationInfo info, PrincipalIdentifier principal, String permission) {
        logger.debug("Principal {} granted permission: {}", principal, permission);
        info.addStringPermission(permission);
    }

    public static void role(SimpleAuthorizationInfo info, PrincipalIdentifier principal, String role) {
        logger.debug("Principal {} added to role: {}", principal, role);
        info.addRole(role);
    }

    private static void grant(SimpleAuthorizationInfo info, PrincipalIdentifier principal, UUID applicationId,
            Set<String> permissions) {
        if (permissions != null) {
            for (String permission : permissions) {
                if (isNotBlank(permission)) {
                    String operations = "*";
                    if (permission.indexOf(':') != -1) {
                        operations = stringOrSubstringBeforeFirst(permission, ':');
                    }
                    if (isBlank(operations)) {
                        operations = "*";
                    }
                    permission = stringOrSubstringAfterFirst(permission, ':');
                    permission = "applications:" + operations + ":" + applicationId + ":" + permission;
                    grant(info, principal, permission);
                }
            }
        }
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof PrincipalCredentialsToken;
    }
}