/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * * Copyright 2006 - 2016 Pentaho Corporation. All rights reserved. */ package org.pentaho.platform.security.userroledao.jackrabbit; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.collections.map.LRUMap; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.AuthorizableExistsException; import org.apache.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.security.authentication.CryptedSimpleCredentials; import org.apache.jackrabbit.core.security.principal.PrincipalImpl; import org.apache.jackrabbit.core.security.user.PentahoUserManagerImpl; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.NameFactory; import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; import org.pentaho.platform.api.engine.security.userroledao.IPentahoRole; import org.pentaho.platform.api.engine.security.userroledao.IPentahoUser; import org.pentaho.platform.api.engine.security.userroledao.IUserRoleDao; import org.pentaho.platform.api.engine.security.userroledao.NotFoundException; import org.pentaho.platform.api.mt.ITenant; import org.pentaho.platform.api.mt.ITenantedPrincipleNameResolver; import org.pentaho.platform.api.repository2.unified.IRepositoryDefaultAclHandler; import org.pentaho.platform.api.repository2.unified.RepositoryFile; import org.pentaho.platform.api.repository2.unified.RepositoryFileAcl; import org.pentaho.platform.api.repository2.unified.RepositoryFileAcl.Builder; import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission; import org.pentaho.platform.api.repository2.unified.RepositoryFileSid; import org.pentaho.platform.api.repository2.unified.RepositoryFileSid.Type; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.core.system.TenantUtils; import org.pentaho.platform.repository2.unified.IRepositoryFileAclDao; import org.pentaho.platform.repository2.unified.IRepositoryFileDao; import org.pentaho.platform.repository2.unified.ServerRepositoryPaths; import org.pentaho.platform.repository2.unified.jcr.ILockHelper; import org.pentaho.platform.repository2.unified.jcr.IPathConversionHelper; import org.pentaho.platform.repository2.unified.jcr.JcrRepositoryFileAclUtils; import org.pentaho.platform.repository2.unified.jcr.JcrRepositoryFileUtils; import org.pentaho.platform.repository2.unified.jcr.JcrTenantUtils; import org.pentaho.platform.repository2.unified.jcr.PentahoJcrConstants; import org.pentaho.platform.repository2.unified.jcr.sejcr.CredentialsStrategySessionFactory; import org.pentaho.platform.security.userroledao.PentahoRole; import org.pentaho.platform.security.userroledao.PentahoUser; import org.pentaho.platform.security.userroledao.messages.Messages; import org.springframework.security.core.userdetails.UserCache; import org.springframework.security.core.userdetails.cache.NullUserCache; import javax.jcr.Credentials; import javax.jcr.NamespaceException; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Set; public abstract class AbstractJcrBackedUserRoleDao implements IUserRoleDao { static NameFactory NF = NameFactoryImpl.getInstance(); static Name P_PRINCIPAL_NAME = NF.create( Name.NS_REP_URI, "principalName" ); //$NON-NLS-1$ protected ITenantedPrincipleNameResolver tenantedUserNameUtils; protected ITenantedPrincipleNameResolver tenantedRoleNameUtils; String pPrincipalName = "rep:principalName"; //$NON-NLS-1$ IRepositoryFileAclDao repositoryFileAclDao; IRepositoryFileDao repositoryFileDao; String defaultTenant; String authenticatedRoleName; String tenantAdminRoleName; String repositoryAdminUsername; IPathConversionHelper pathConversionHelper; IRepositoryDefaultAclHandler defaultAclHandler; ILockHelper lockHelper; List<String> systemRoles; List<String> extraRoles; HashMap<String, PentahoUserManagerImpl> userMgrMap = new HashMap<String, PentahoUserManagerImpl>(); private LRUMap userCache = new LRUMap( 4096 ); private UserCache userDetailsCache = new NullUserCache(); private boolean useJackrabbitUserCache = true; public AbstractJcrBackedUserRoleDao( ITenantedPrincipleNameResolver userNameUtils, ITenantedPrincipleNameResolver roleNameUtils, String authenticatedRoleName, String tenantAdminRoleName, String repositoryAdminUsername, IRepositoryFileAclDao repositoryFileAclDao, IRepositoryFileDao repositoryFileDao, final IPathConversionHelper pathConversionHelper, final ILockHelper lockHelper, final IRepositoryDefaultAclHandler defaultAclHandler, final List<String> systemRoles, final List<String> extraRoles, UserCache userDetailsCache ) throws NamespaceException { this.tenantedUserNameUtils = userNameUtils; this.tenantedRoleNameUtils = roleNameUtils; this.authenticatedRoleName = authenticatedRoleName; this.tenantAdminRoleName = tenantAdminRoleName; this.repositoryAdminUsername = repositoryAdminUsername; this.repositoryFileAclDao = repositoryFileAclDao; this.repositoryFileDao = repositoryFileDao; this.pathConversionHelper = pathConversionHelper; this.lockHelper = lockHelper; this.defaultAclHandler = defaultAclHandler; this.systemRoles = systemRoles; this.extraRoles = extraRoles; this.userDetailsCache = userDetailsCache; } public void setRoleMembers( Session session, final ITenant theTenant, final String roleName, final String[] memberUserNames ) throws RepositoryException, NotFoundException { List<IPentahoUser> currentRoleMembers = getRoleMembers( session, theTenant, roleName ); String[] usersToBeRemoved = findRemovedUsers( currentRoleMembers, memberUserNames ); // If we are unassigning a user or users from the Administrator role, we need to check if this is a logged in user // or a user designated as a system user. If it is then we // will display a message to the user. if ( ( oneOfUserIsMySelf( usersToBeRemoved ) || oneOfUserIsDefaultAdminUser( usersToBeRemoved ) ) && tenantAdminRoleName.equals( roleName ) ) { throw new RepositoryException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0009_USER_REMOVE_FAILED_YOURSELF_OR_DEFAULT_ADMIN_USER" ) ); } // If this is the last user from the Administrator role, we will not let the user remove. if ( tenantAdminRoleName.equals( roleName ) && ( currentRoleMembers != null && currentRoleMembers.size() > 0 ) && memberUserNames.length == 0 ) { throw new RepositoryException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0001_LAST_ADMIN_ROLE", tenantAdminRoleName ) ); } Group jackrabbitGroup = getJackrabbitGroup( theTenant, roleName, session ); if ( ( jackrabbitGroup == null ) || !TenantUtils.isAccessibleTenant( theTenant == null ? tenantedRoleNameUtils.getTenant( jackrabbitGroup .getID() ) : theTenant ) ) { throw new NotFoundException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0002_ROLE_NOT_FOUND" ) ); } HashMap<String, User> currentlyAssignedUsers = new HashMap<String, User>(); Iterator<Authorizable> currentMembers = jackrabbitGroup.getMembers(); while ( currentMembers.hasNext() ) { Authorizable member = currentMembers.next(); if ( member instanceof User ) { currentlyAssignedUsers.put( member.getID(), (User) member ); } } HashMap<String, User> finalCollectionOfAssignedUsers = new HashMap<String, User>(); if ( memberUserNames != null ) { ITenant tenant = theTenant == null ? JcrTenantUtils.getTenant( roleName, false ) : theTenant; for ( String user : memberUserNames ) { User jackrabbitUser = getJackrabbitUser( tenant, user, session ); if ( jackrabbitUser != null ) { finalCollectionOfAssignedUsers.put( getTenantedUserNameUtils().getPrincipleId( tenant, user ), jackrabbitUser ); } } } ArrayList<String> usersToRemove = new ArrayList<String>( currentlyAssignedUsers.keySet() ); usersToRemove.removeAll( finalCollectionOfAssignedUsers.keySet() ); ArrayList<String> usersToAdd = new ArrayList<String>( finalCollectionOfAssignedUsers.keySet() ); usersToAdd.removeAll( currentlyAssignedUsers.keySet() ); for ( String userId : usersToRemove ) { jackrabbitGroup.removeMember( currentlyAssignedUsers.get( userId ) ); purgeUserFromCache( userId ); } for ( String userId : usersToAdd ) { jackrabbitGroup.addMember( finalCollectionOfAssignedUsers.get( userId ) ); // Purge the UserDetails cache purgeUserFromCache( userId ); } } private void setUserRolesForNewUser( Session session, final ITenant theTenant, final String userName, final String[] roles ) throws RepositoryException, NotFoundException { Set<String> roleSet = new HashSet<String>(); if ( roles != null ) { roleSet.addAll( Arrays.asList( roles ) ); } roleSet.add( authenticatedRoleName ); User jackrabbitUser = getJackrabbitUser( theTenant, userName, session ); if ( ( jackrabbitUser == null ) || !TenantUtils.isAccessibleTenant( theTenant == null ? tenantedUserNameUtils .getTenant( jackrabbitUser.getID() ) : theTenant ) ) { throw new NotFoundException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0003_USER_NOT_FOUND" ) ); } HashMap<String, Group> finalCollectionOfAssignedGroups = new HashMap<String, Group>(); ITenant tenant = theTenant == null ? JcrTenantUtils.getTenant( userName, true ) : theTenant; for ( String role : roleSet ) { Group jackrabbitGroup = getJackrabbitGroup( tenant, role, session ); if ( jackrabbitGroup != null ) { finalCollectionOfAssignedGroups.put( tenantedRoleNameUtils.getPrincipleId( tenant, role ), jackrabbitGroup ); } } ArrayList<String> groupsToAdd = new ArrayList<String>( finalCollectionOfAssignedGroups.keySet() ); for ( String groupId : groupsToAdd ) { finalCollectionOfAssignedGroups.get( groupId ).addMember( jackrabbitUser ); // Purge the UserDetails cache purgeUserFromCache( userName ); } } private void purgeUserFromCache( String userName ) { userDetailsCache.removeUserFromCache( getTenantedUserNameUtils().getPrincipleName( userName ) ); } private boolean oneOfUserIsMySelf( String[] users ) { for ( int i = 0; i < users.length; i++ ) { if ( isMyself( users[ i ] ) ) { return true; } } return false; } private boolean oneOfUserIsDefaultAdminUser( String[] users ) { for ( int i = 0; i < users.length; i++ ) { if ( isDefaultAdminUser( users[ i ] ) ) { return true; } } return false; } private boolean isMyself( String userName ) { return PentahoSessionHolder.getSession().getName().equals( userName ); } private boolean isDefaultAdminUser( String userName ) { String defaultAdminUser = PentahoSystem.get( String.class, "singleTenantAdminUserName", PentahoSessionHolder.getSession() ); if ( defaultAdminUser != null ) { return defaultAdminUser.equals( userName ); } return false; } private boolean adminRoleExist( String[] newRoles ) { return Arrays.asList( newRoles ).contains( tenantAdminRoleName ); } public void setUserRoles( Session session, final ITenant theTenant, final String userName, final String[] roles ) throws RepositoryException, NotFoundException { if ( ( isMyself( userName ) || isDefaultAdminUser( userName ) ) && !adminRoleExist( roles ) ) { throw new RepositoryException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0005_YOURSELF_OR_DEFAULT_ADMIN_USER" ) ); } Set<String> roleSet = new HashSet<String>(); if ( roles != null ) { roleSet.addAll( Arrays.asList( roles ) ); } roleSet.add( authenticatedRoleName ); User jackrabbitUser = getJackrabbitUser( theTenant, userName, session ); if ( ( jackrabbitUser == null ) || !TenantUtils.isAccessibleTenant( theTenant == null ? tenantedUserNameUtils .getTenant( jackrabbitUser.getID() ) : theTenant ) ) { throw new NotFoundException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0003_USER_NOT_FOUND" ) ); } HashMap<String, Group> currentlyAssignedGroups = new HashMap<String, Group>(); Iterator<Group> currentGroups = jackrabbitUser.memberOf(); while ( currentGroups.hasNext() ) { Group currentGroup = currentGroups.next(); currentlyAssignedGroups.put( currentGroup.getID(), currentGroup ); } HashMap<String, Group> finalCollectionOfAssignedGroups = new HashMap<String, Group>(); ITenant tenant = theTenant == null ? JcrTenantUtils.getTenant( userName, true ) : theTenant; for ( String role : roleSet ) { Group jackrabbitGroup = getJackrabbitGroup( tenant, role, session ); if ( jackrabbitGroup != null ) { finalCollectionOfAssignedGroups.put( tenantedRoleNameUtils.getPrincipleId( tenant, role ), jackrabbitGroup ); } } ArrayList<String> groupsToRemove = new ArrayList<String>( currentlyAssignedGroups.keySet() ); groupsToRemove.removeAll( finalCollectionOfAssignedGroups.keySet() ); ArrayList<String> groupsToAdd = new ArrayList<String>( finalCollectionOfAssignedGroups.keySet() ); groupsToAdd.removeAll( currentlyAssignedGroups.keySet() ); for ( String groupId : groupsToRemove ) { currentlyAssignedGroups.get( groupId ).removeMember( jackrabbitUser ); } for ( String groupId : groupsToAdd ) { finalCollectionOfAssignedGroups.get( groupId ).addMember( jackrabbitUser ); } // Purge the UserDetails cache purgeUserFromCache( userName ); } public IPentahoRole createRole( Session session, final ITenant theTenant, final String roleName, final String description, final String[] memberUserNames ) throws AuthorizableExistsException, RepositoryException { ITenant tenant = theTenant; String role = roleName; if ( tenant == null ) { tenant = JcrTenantUtils.getTenant( roleName, false ); role = JcrTenantUtils.getPrincipalName( roleName, false ); } if ( tenant == null || tenant.getId() == null ) { tenant = JcrTenantUtils.getCurrentTenant(); } if ( !TenantUtils.isAccessibleTenant( tenant ) ) { throw new NotFoundException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0006_TENANT_NOT_FOUND", theTenant.getId() ) ); } String roleId = tenantedRoleNameUtils.getPrincipleId( tenant, role ); UserManager tenantUserMgr = getUserManager( tenant, session ); // Intermediate path will always be an empty string. The path is already provided while creating a user manager tenantUserMgr.createGroup( new PrincipalImpl( roleId ), "" ); //$NON-NLS-1$ setRoleMembers( session, tenant, role, memberUserNames ); setRoleDescription( session, tenant, role, description ); return getRole( session, theTenant, roleName ); } public IPentahoUser createUser( Session session, final ITenant theTenant, final String userName, final String password, final String description, final String[] roles ) throws AuthorizableExistsException, RepositoryException { ITenant tenant = theTenant; String user = userName; if ( tenant == null ) { tenant = JcrTenantUtils.getTenant( userName, true ); user = JcrTenantUtils.getPrincipalName( userName, true ); } if ( tenant == null || tenant.getId() == null ) { tenant = JcrTenantUtils.getCurrentTenant(); } if ( !TenantUtils.isAccessibleTenant( tenant ) ) { throw new NotFoundException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0006_TENANT_NOT_FOUND", theTenant.getId() ) ); } String userId = tenantedUserNameUtils.getPrincipleId( tenant, user ); UserManager tenantUserMgr = getUserManager( tenant, session ); tenantUserMgr.createUser( userId, password, new PrincipalImpl( userId ), "" ); //$NON-NLS-1$ session.save(); /** * This call is absolutely necessary. setUserRolesForNewUser will never inspect what roles this user is a part of. * Since this is a new user it will not be a part of new roles **/ setUserRolesForNewUser( session, tenant, user, roles ); setUserDescription( session, tenant, user, description ); session.save(); createUserHomeFolder( tenant, user, session ); session.save(); this.userDetailsCache.removeUserFromCache( userName ); return getUser( session, tenant, userName ); } public void deleteRole( Session session, final IPentahoRole role ) throws NotFoundException, RepositoryException { if ( canDeleteRole( session, role ) ) { final List<IPentahoUser> roleMembers = this.getRoleMembers( session, role.getTenant(), role.getName() ); Group jackrabbitGroup = getJackrabbitGroup( role.getTenant(), role.getName(), session ); if ( jackrabbitGroup != null && TenantUtils.isAccessibleTenant( tenantedRoleNameUtils.getTenant( jackrabbitGroup.getID() ) ) ) { jackrabbitGroup.remove(); } else { throw new NotFoundException( "" ); //$NON-NLS-1$ } for ( IPentahoUser roleMember : roleMembers ) { purgeUserFromCache( roleMember.getUsername() ); } } else { throw new RepositoryException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0007_ATTEMPTED_SYSTEM_ROLE_DELETE" ) ); } } public void deleteUser( Session session, final IPentahoUser user ) throws NotFoundException, RepositoryException { if ( canDeleteUser( session, user ) ) { User jackrabbitUser = getJackrabbitUser( user.getTenant(), user.getUsername(), session ); if ( jackrabbitUser != null && TenantUtils.isAccessibleTenant( tenantedUserNameUtils.getTenant( jackrabbitUser.getID() ) ) ) { // [BISERVER-9215] Adding new user with same user name as a previously deleted user, defaults to all // previous // roles Iterator<Group> currentGroups = jackrabbitUser.memberOf(); while ( currentGroups.hasNext() ) { currentGroups.next().removeMember( jackrabbitUser ); } purgeUserFromCache( user.getUsername() ); // [BISERVER-9215] jackrabbitUser.remove(); } else { throw new NotFoundException( "" ); //$NON-NLS-1$ } } else { throw new RepositoryException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0004_LAST_USER_NEEDED_IN_ROLE", tenantAdminRoleName ) ); } } public List<IPentahoRole> getRoles( Session session ) throws RepositoryException { return getRoles( session, JcrTenantUtils.getCurrentTenant() ); } @VisibleForTesting IPentahoUser convertToPentahoUser( User jackrabbitUser ) throws RepositoryException { if ( getUserCache().containsKey( jackrabbitUser.getID() ) ) { return (IPentahoUser) getUserCache().get( jackrabbitUser.getID() ); } IPentahoUser pentahoUser = null; Value[] propertyValues = null; String description = null; try { propertyValues = jackrabbitUser.getProperty( "description" ); //$NON-NLS-1$ description = propertyValues.length > 0 ? propertyValues[ 0 ].getString() : null; } catch ( Exception ex ) { // CHECKSTYLES IGNORE } Credentials credentials = jackrabbitUser.getCredentials(); String password = null; if ( credentials instanceof CryptedSimpleCredentials ) { password = new String( ( (CryptedSimpleCredentials) credentials ).getPassword() ); } pentahoUser = new PentahoUser( getTenantedUserNameUtils().getTenant( jackrabbitUser.getID() ), getTenantedUserNameUtils() .getPrincipleName( jackrabbitUser.getID() ), password, description, !jackrabbitUser.isDisabled() ); if ( isUseJackrabbitUserCache() ) { getUserCache().put( jackrabbitUser.getID(), pentahoUser ); } return pentahoUser; } private IPentahoRole convertToPentahoRole( Group jackrabbitGroup ) throws RepositoryException { IPentahoRole role = null; Value[] propertyValues = null; String description = null; try { propertyValues = jackrabbitGroup.getProperty( "description" ); //$NON-NLS-1$ description = propertyValues.length > 0 ? propertyValues[ 0 ].getString() : null; } catch ( Exception ex ) { // CHECKSTYLES IGNORE } role = new PentahoRole( tenantedRoleNameUtils.getTenant( jackrabbitGroup.getID() ), tenantedRoleNameUtils .getPrincipleName( jackrabbitGroup.getID() ), description ); return role; } public List<IPentahoUser> getUsers( Session session ) throws RepositoryException { return getUsers( session, JcrTenantUtils.getCurrentTenant() ); } public void setRoleDescription( Session session, final ITenant theTenant, final String roleName, final String description ) throws NotFoundException, RepositoryException { Group jackrabbitGroup = getJackrabbitGroup( theTenant, roleName, session ); if ( jackrabbitGroup != null && TenantUtils.isAccessibleTenant( theTenant == null ? tenantedRoleNameUtils .getTenant( jackrabbitGroup.getID() ) : theTenant ) ) { if ( description == null ) { jackrabbitGroup.removeProperty( "description" ); //$NON-NLS-1$ } else { jackrabbitGroup .setProperty( "description", session.getValueFactory().createValue( description ) ); //$NON-NLS-1$ } } else { throw new NotFoundException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0002_ROLE_NOT_FOUND" ) ); } } public void setUserDescription( Session session, final ITenant theTenant, final String userName, final String description ) throws NotFoundException, RepositoryException { User jackrabbitUser = getJackrabbitUser( theTenant, userName, session ); if ( ( jackrabbitUser == null ) || !TenantUtils.isAccessibleTenant( theTenant == null ? tenantedUserNameUtils .getTenant( jackrabbitUser.getID() ) : theTenant ) ) { throw new NotFoundException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0003_USER_NOT_FOUND" ) ); } if ( description == null ) { jackrabbitUser.removeProperty( "description" ); //$NON-NLS-1$ } else { jackrabbitUser.setProperty( "description", session.getValueFactory().createValue( description ) ); //$NON-NLS-1$ } } public void setPassword( Session session, final ITenant theTenant, final String userName, final String password ) throws NotFoundException, RepositoryException { User jackrabbitUser = getJackrabbitUser( theTenant, userName, session ); if ( ( jackrabbitUser == null ) || !TenantUtils.isAccessibleTenant( theTenant == null ? tenantedUserNameUtils .getTenant( jackrabbitUser.getID() ) : theTenant ) ) { throw new NotFoundException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0003_USER_NOT_FOUND" ) ); } jackrabbitUser.changePassword( password ); /** * BISERVER-9906 Clear cache after changing password */ purgeUserFromCache( userName ); userCache.remove( jackrabbitUser.getID() ); } public ITenantedPrincipleNameResolver getTenantedUserNameUtils() { return tenantedUserNameUtils; } public ITenantedPrincipleNameResolver getTenantedRoleNameUtils() { return tenantedRoleNameUtils; } public List<IPentahoRole> getRoles( Session session, ITenant tenant ) throws RepositoryException, NamespaceException { return getRoles( session, tenant, false ); } public List<IPentahoRole> getRoles( Session session, ITenant theTenant, boolean includeSubtenants ) throws RepositoryException { ArrayList<IPentahoRole> roles = new ArrayList<>(); if ( theTenant == null || theTenant.getId() == null ) { theTenant = JcrTenantUtils.getTenant(); } if ( TenantUtils.isAccessibleTenant( theTenant ) ) { UserManager userMgr = getUserManager( theTenant, session ); pPrincipalName = getJcrName( session ); Iterator<Authorizable> it = userMgr.findAuthorizables( pPrincipalName, null, UserManager.SEARCH_TYPE_GROUP ); while ( it.hasNext() ) { Group group = (Group) it.next(); IPentahoRole pentahoRole = convertToPentahoRole( group ); // Exclude the system role from the list of roles to be returned back if ( !extraRoles.contains( pentahoRole.getName() ) ) { if ( includeSubtenants ) { roles.add( pentahoRole ); } else { if ( pentahoRole.getTenant() != null && pentahoRole.getTenant().equals( theTenant ) ) { roles.add( pentahoRole ); } } } } } return roles; } /** * Our Sessions should be wrapped in a JDK Dynamic Proxy. Unfortunately some Jackrabbit code is written against the * SessionImpl concrete class. This utility unwraps the proxy. * * @param session * @return */ protected static SessionImpl getSessionImpl( Session session ) { if ( session instanceof SessionImpl ) { return (SessionImpl) session; } else if ( Proxy.isProxyClass( session.getClass() ) ) { InvocationHandler invocationHandler = Proxy.getInvocationHandler( session ); if ( invocationHandler instanceof CredentialsStrategySessionFactory.LogoutSuppressingInvocationHandler ) { return ( (CredentialsStrategySessionFactory.LogoutSuppressingInvocationHandler) invocationHandler ) .getSession(); } } throw new IllegalStateException( "Session is not a SessionImpl or a proxy of one." ); } private static String getJcrName( Session session ) throws NamespaceException { return getSessionImpl( session ).getJCRName( P_PRINCIPAL_NAME ); } public List<IPentahoUser> getUsers( Session session, ITenant tenant ) throws RepositoryException { return getUsers( session, tenant, false ); } public List<IPentahoUser> getUsers( Session session, ITenant theTenant, boolean includeSubtenants ) throws RepositoryException { ArrayList<IPentahoUser> users = new ArrayList<IPentahoUser>(); if ( theTenant == null || theTenant.getId() == null ) { theTenant = JcrTenantUtils.getTenant(); } if ( TenantUtils.isAccessibleTenant( theTenant ) ) { UserManager userMgr = getUserManager( theTenant, session ); pPrincipalName = getJcrName( session ); Iterator<Authorizable> it = userMgr.findAuthorizables( pPrincipalName, null, UserManager.SEARCH_TYPE_USER ); while ( it.hasNext() ) { User user = (User) it.next(); IPentahoUser pentahoUser = convertToPentahoUser( user ); if ( includeSubtenants ) { users.add( pentahoUser ); } else { if ( pentahoUser.getTenant() != null && pentahoUser.getTenant().equals( theTenant ) ) { users.add( pentahoUser ); } } } } return users; } public IPentahoRole getRole( Session session, final ITenant tenant, final String name ) throws RepositoryException { Group jackrabbitGroup = getJackrabbitGroup( tenant, name, session ); return jackrabbitGroup != null && TenantUtils.isAccessibleTenant( tenant == null ? tenantedRoleNameUtils.getTenant( jackrabbitGroup.getID() ) : tenant ) ? convertToPentahoRole( jackrabbitGroup ) : null; } private PentahoUserManagerImpl getUserManager( ITenant theTenant, Session session ) throws RepositoryException { Properties tenantProperties = new Properties(); tenantProperties.put( PentahoUserManagerImpl.PARAM_USERS_PATH, PentahoUserManagerImpl.USERS_PATH + theTenant.getRootFolderAbsolutePath() ); tenantProperties.put( PentahoUserManagerImpl.PARAM_GROUPS_PATH, PentahoUserManagerImpl.GROUPS_PATH + theTenant.getRootFolderAbsolutePath() ); return new PentahoUserManagerImpl( getSessionImpl( session ), session.getUserID(), tenantProperties ); } public IPentahoUser getUser( Session session, final ITenant tenant, final String name ) throws RepositoryException { User jackrabbitUser = getJackrabbitUser( tenant, name, session ); return jackrabbitUser != null && TenantUtils.isAccessibleTenant( tenant == null ? tenantedUserNameUtils.getTenant( jackrabbitUser.getID() ) : tenant ) ? convertToPentahoUser( jackrabbitUser ) : null; } private Group getJackrabbitGroup( ITenant theTenant, String name, Session session ) throws RepositoryException { Group jackrabbitGroup = null; String roleId = name; String roleName = name; ITenant tenant = theTenant; if ( tenant == null ) { tenant = JcrTenantUtils.getTenant( roleName, false ); roleName = JcrTenantUtils.getPrincipalName( roleName, false ); } if ( tenant == null || tenant.getId() == null ) { tenant = JcrTenantUtils.getCurrentTenant(); } if ( tenant == null || tenant.getId() == null ) { tenant = JcrTenantUtils.getDefaultTenant(); } roleId = tenantedRoleNameUtils.getPrincipleId( tenant, roleName ); UserManager userMgr = getUserManager( tenant, session ); Authorizable authorizable = userMgr.getAuthorizable( roleId ); if ( authorizable instanceof Group ) { jackrabbitGroup = (Group) authorizable; } return jackrabbitGroup; } private User getJackrabbitUser( ITenant theTenant, String name, Session session ) throws RepositoryException { User jackrabbitUser = null; String userId = name; String userName = name; ITenant tenant = theTenant; if ( tenant == null ) { tenant = JcrTenantUtils.getTenant( userName, true ); userName = JcrTenantUtils.getPrincipalName( userName, true ); } if ( tenant == null || tenant.getId() == null ) { tenant = JcrTenantUtils.getCurrentTenant(); } if ( tenant == null || tenant.getId() == null ) { tenant = JcrTenantUtils.getDefaultTenant(); } if ( tenant != null ) { userId = tenantedUserNameUtils.getPrincipleId( tenant, userName ); UserManager userMgr = getUserManager( tenant, session ); Authorizable authorizable = userMgr.getAuthorizable( userId ); if ( authorizable instanceof User ) { jackrabbitUser = (User) authorizable; } } return jackrabbitUser; } protected boolean tenantExists( String tenantName ) { return tenantName != null && tenantName.trim().length() > 0; } public List<IPentahoUser> getRoleMembers( Session session, final ITenant theTenant, final String roleName ) throws RepositoryException { List<IPentahoUser> users = new ArrayList<IPentahoUser>(); Group jackrabbitGroup = getJackrabbitGroup( theTenant, roleName, session ); if ( ( jackrabbitGroup != null ) && TenantUtils.isAccessibleTenant( theTenant == null ? tenantedRoleNameUtils .getTenant( jackrabbitGroup.getID() ) : theTenant ) ) { Iterator<Authorizable> authorizables = jackrabbitGroup.getMembers(); while ( authorizables.hasNext() ) { Authorizable authorizable = authorizables.next(); if ( authorizable instanceof User ) { users.add( convertToPentahoUser( (User) authorizable ) ); } } } return users; } public List<IPentahoRole> getUserRoles( Session session, final ITenant theTenant, final String userName ) throws RepositoryException { ArrayList<IPentahoRole> roles = new ArrayList<IPentahoRole>(); User jackrabbitUser = getJackrabbitUser( theTenant, userName, session ); if ( ( jackrabbitUser != null ) && TenantUtils.isAccessibleTenant( theTenant == null ? tenantedUserNameUtils.getTenant( jackrabbitUser.getID() ) : theTenant ) ) { Iterator<Group> groups = jackrabbitUser.memberOf(); while ( groups.hasNext() ) { IPentahoRole role = convertToPentahoRole( groups.next() ); // Exclude the extra role from the list of roles to be returned back if ( !extraRoles.contains( role.getName() ) ) { roles.add( role ); } } } return roles; } private RepositoryFile createUserHomeFolder( ITenant theTenant, String username, Session session ) throws RepositoryException { Builder aclsForUserHomeFolder = null; Builder aclsForTenantHomeFolder = null; if ( theTenant == null ) { theTenant = JcrTenantUtils.getTenant( username, true ); username = JcrTenantUtils.getPrincipalName( username, true ); } if ( theTenant == null || theTenant.getId() == null ) { theTenant = JcrTenantUtils.getCurrentTenant(); } if ( theTenant == null || theTenant.getId() == null ) { theTenant = JcrTenantUtils.getDefaultTenant(); } RepositoryFile userHomeFolder = null; String userId = tenantedUserNameUtils.getPrincipleId( theTenant, username ); final RepositoryFileSid userSid = new RepositoryFileSid( userId ); RepositoryFile tenantHomeFolder = null; RepositoryFile tenantRootFolder = null; RepositoryFileSid ownerSid = null; // Get the Tenant Root folder. If the Tenant Root folder does not exist then exit. tenantRootFolder = JcrRepositoryFileUtils.getFileByAbsolutePath( session, ServerRepositoryPaths .getTenantRootFolderPath( theTenant ), pathConversionHelper, lockHelper, false, null ); if ( tenantRootFolder != null ) { // Try to see if Tenant Home folder exist tenantHomeFolder = JcrRepositoryFileUtils.getFileByAbsolutePath( session, ServerRepositoryPaths .getTenantHomeFolderPath( theTenant ), pathConversionHelper, lockHelper, false, null ); if ( tenantHomeFolder == null ) { String ownerId = tenantedUserNameUtils.getPrincipleId( theTenant, username ); ownerSid = new RepositoryFileSid( ownerId, Type.USER ); String tenantAuthenticatedRoleId = tenantedRoleNameUtils.getPrincipleId( theTenant, authenticatedRoleName ); RepositoryFileSid tenantAuthenticatedRoleSid = new RepositoryFileSid( tenantAuthenticatedRoleId, Type.ROLE ); aclsForTenantHomeFolder = new RepositoryFileAcl.Builder( userSid ).ace( tenantAuthenticatedRoleSid, EnumSet .of( RepositoryFilePermission.READ ) ); aclsForUserHomeFolder = new RepositoryFileAcl.Builder( userSid ).ace( ownerSid, EnumSet.of( RepositoryFilePermission.ALL ) ); tenantHomeFolder = internalCreateFolder( session, tenantRootFolder.getId(), new RepositoryFile.Builder( ServerRepositoryPaths .getTenantHomeFolderName() ).folder( true ).title( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.usersFolderDisplayName" ) ).build(), aclsForTenantHomeFolder.build(), "tenant home folder" ); //$NON-NLS-1$ } else { String ownerId = tenantedUserNameUtils.getPrincipleId( theTenant, username ); ownerSid = new RepositoryFileSid( ownerId, Type.USER ); aclsForUserHomeFolder = new RepositoryFileAcl.Builder( userSid ).ace( ownerSid, EnumSet.of( RepositoryFilePermission.ALL ) ); } // now check if user's home folder exist userHomeFolder = JcrRepositoryFileUtils.getFileByAbsolutePath( session, ServerRepositoryPaths.getUserHomeFolderPath( theTenant, username ), pathConversionHelper, lockHelper, false, null ); if ( userHomeFolder == null ) { userHomeFolder = internalCreateFolder( session, tenantHomeFolder.getId(), new RepositoryFile.Builder( username ).folder( true ).build(), aclsForUserHomeFolder.build(), "user home folder" ); //$NON-NLS-1$ } } return userHomeFolder; } private RepositoryFile internalCreateFolder( final Session session, final Serializable parentFolderId, final RepositoryFile folder, final RepositoryFileAcl acl, final String versionMessage ) throws RepositoryException { PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants( session ); JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary( session, pentahoJcrConstants, parentFolderId ); Node folderNode = JcrRepositoryFileUtils.createFolderNode( session, pentahoJcrConstants, parentFolderId, folder ); // we must create the acl during checkout JcrRepositoryFileAclUtils.createAcl( session, pentahoJcrConstants, folderNode.getIdentifier(), acl == null ? defaultAclHandler.createDefaultAcl( folder ) : acl ); session.save(); if ( folder.isVersioned() ) { JcrRepositoryFileUtils.checkinNearestVersionableNodeIfNecessary( session, pentahoJcrConstants, folderNode, versionMessage ); } JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary( session, pentahoJcrConstants, parentFolderId, Messages.getInstance().getString( "JcrRepositoryFileDao.USER_0001_VER_COMMENT_ADD_FOLDER", folder.getName(), ( parentFolderId == null ? "root" : parentFolderId.toString() ) ) ); //$NON-NLS-1$ //$NON-NLS-2$ return JcrRepositoryFileUtils.nodeToFile( session, pentahoJcrConstants, pathConversionHelper, lockHelper, folderNode ); } /** * Checks to see if the removal of the received roles and users would cause the system to have no login associated * with the Admin role. This check is to be made before any changes take place * * @return Error message if invalid or null if ok * @throws RepositoryException */ private boolean canDeleteUser( Session session, final IPentahoUser user ) throws RepositoryException { boolean userHasAdminRole = false; List<IPentahoRole> roles = getUserRoles( null, user.getUsername() ); for ( IPentahoRole role : roles ) { if ( tenantAdminRoleName.equals( role.getName() ) ) { userHasAdminRole = true; break; } } if ( ( isMyself( user.getUsername() ) || isDefaultAdminUser( user.getUsername() ) ) && userHasAdminRole ) { throw new RepositoryException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0008_UNABLE_TO_DELETE_USER_IS_YOURSELF_OR_DEFAULT_ADMIN_USER" ) ); } if ( userHasAdminRole ) { List<IPentahoUser> usersWithAdminRole = getRoleMembers( session, null, tenantAdminRoleName ); if ( usersWithAdminRole == null ) { throw new RepositoryException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0004_LAST_USER_NEEDED_IN_ROLE", tenantAdminRoleName ) ); } if ( usersWithAdminRole.size() > 1 ) { return true; } else if ( usersWithAdminRole.size() == 1 ) { return false; } else { throw new RepositoryException( Messages.getInstance().getString( "AbstractJcrBackedUserRoleDao.ERROR_0004_LAST_USER_NEEDED_IN_ROLE", tenantAdminRoleName ) ); } } return true; } private boolean canDeleteRole( Session session, final IPentahoRole role ) { return !( role != null && systemRoles.contains( role.getName() ) ); } private String[] findRemovedUsers( List<IPentahoUser> savedUsers, String[] toBeSaved ) { List<String> usersToBeRemoved = new ArrayList<String>(); List<String> toBeSavedUsers = Arrays.asList( toBeSaved ); for ( int i = 0; i < savedUsers.size(); i++ ) { if ( toBeSavedUsers != null && toBeSaved.length > 0 ) { if ( !toBeSavedUsers.contains( savedUsers.get( i ).getUsername() ) ) { usersToBeRemoved.add( savedUsers.get( i ).getUsername() ); } } else { usersToBeRemoved.add( savedUsers.get( i ).getUsername() ); } } return usersToBeRemoved.toArray( new String[ 0 ] ); } public boolean isUseJackrabbitUserCache() { return useJackrabbitUserCache; } public void setUseJackrabbitUserCache( boolean useJackrabbitUserCache ) { this.useJackrabbitUserCache = useJackrabbitUserCache; } @VisibleForTesting LRUMap getUserCache() { return userCache; } }