/* * Copyright (c) 2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you 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.wso2.carbon.user.cassandra; import me.prettyprint.cassandra.model.BasicColumnDefinition; import me.prettyprint.cassandra.model.BasicColumnFamilyDefinition; import me.prettyprint.cassandra.serializers.CompositeSerializer; import me.prettyprint.cassandra.serializers.StringSerializer; import me.prettyprint.cassandra.service.CassandraHostConfigurator; import me.prettyprint.cassandra.service.ColumnSliceIterator; import me.prettyprint.cassandra.service.template.ColumnFamilyResult; import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate; import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate; import me.prettyprint.hector.api.Cluster; import me.prettyprint.hector.api.Keyspace; import me.prettyprint.hector.api.beans.Composite; import me.prettyprint.hector.api.beans.HColumn; import me.prettyprint.hector.api.beans.OrderedRows; import me.prettyprint.hector.api.beans.Row; import me.prettyprint.hector.api.ddl.ColumnFamilyDefinition; import me.prettyprint.hector.api.ddl.ColumnIndexType; import me.prettyprint.hector.api.ddl.ComparatorType; import me.prettyprint.hector.api.ddl.KeyspaceDefinition; import me.prettyprint.hector.api.exceptions.HectorException; import me.prettyprint.hector.api.factory.HFactory; import me.prettyprint.hector.api.mutation.Mutator; import me.prettyprint.hector.api.query.ColumnQuery; import me.prettyprint.hector.api.query.QueryResult; import me.prettyprint.hector.api.query.RangeSlicesQuery; import me.prettyprint.hector.api.query.SliceQuery; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.user.api.Properties; import org.wso2.carbon.user.api.RealmConfiguration; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.UserRealm; import org.wso2.carbon.user.core.UserStoreException; import org.wso2.carbon.user.core.claim.ClaimManager; import org.wso2.carbon.user.core.common.AbstractUserStoreManager; import org.wso2.carbon.user.core.common.RoleContext; import org.wso2.carbon.user.core.jdbc.JDBCRealmConstants; import org.wso2.carbon.user.core.profile.ProfileConfigurationManager; import org.wso2.carbon.user.core.tenant.Tenant; import org.wso2.carbon.user.core.util.DatabaseUtil; import org.wso2.carbon.user.core.util.UserCoreUtil; import javax.sql.DataSource; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.UUID; public class CassandraUserStoreManager extends AbstractUserStoreManager { private static final String TRUE = "TRUE"; private static final Log log = LogFactory.getLog(CassandraUserStoreManager.class); private final StringSerializer stringSerializer = StringSerializer.get(); protected DataSource jdbcDataSource = null; protected boolean useOnlyInternalRoles; protected Random random = new Random(); private Cluster cluster; private Keyspace keyspace; private String tenantIdString; private String domain = null; public CassandraUserStoreManager() { } public CassandraUserStoreManager(RealmConfiguration realmConfig, int tenantId) throws UserStoreException { this.realmConfig = realmConfig; Util.setRealmConfig(realmConfig); this.tenantIdString = Integer.toString(tenantId); this.tenantId = tenantId; // Set groups read/write configuration if (realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.READ_GROUPS_ENABLED) != null) { readGroupsEnabled = Boolean.parseBoolean(realmConfig .getUserStoreProperty(UserCoreConstants.RealmConfig.READ_GROUPS_ENABLED)); } if (realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.WRITE_GROUPS_ENABLED) != null) { writeGroupsEnabled = Boolean.parseBoolean(realmConfig .getUserStoreProperty(UserCoreConstants.RealmConfig.WRITE_GROUPS_ENABLED)); } else { if (!isReadOnly()) { writeGroupsEnabled = true; } } if (writeGroupsEnabled) { readGroupsEnabled = true; } /* * Initialize user roles cache as implemented in AbstractUserStoreManager */ initUserRolesCache(); Map<String, String> credentials = new HashMap<String, String>(); credentials.put(CFConstants.USERNAME_PROPERTY, realmConfig.getUserStoreProperty(CFConstants.USERNAME_XML_ATTRIB)); credentials.put(CFConstants.PASSWORD_PROPERTY, realmConfig.getUserStoreProperty(CFConstants.PASSWORD_XML_ATTRIB)); CassandraHostConfigurator hostConf = new CassandraHostConfigurator(); hostConf.setHosts(realmConfig.getUserStoreProperty(CFConstants.HOST_XML_ATTRIB)); hostConf.setPort(Integer.parseInt(realmConfig.getUserStoreProperty(CFConstants.PORT_XML_ATTRIB))); // set Cassandra specific properties cluster = HFactory.getOrCreateCluster(realmConfig.getUserStoreProperty(CFConstants.KEYSPACE_NAME_XML_ATTRIB), hostConf, credentials); keyspace = HFactory.createKeyspace(realmConfig.getUserStoreProperty(CFConstants.KEYSPACE_NAME_XML_ATTRIB), cluster); insertInitialData(keyspace); } public CassandraUserStoreManager(RealmConfiguration realmConfig, Map<String, Object> properties, ClaimManager claimManager, ProfileConfigurationManager profileManager, UserRealm realm, Integer tenantId) throws UserStoreException { this(realmConfig, tenantId); if (log.isDebugEnabled()) { log.debug("Started " + System.currentTimeMillis()); } this.claimManager = claimManager; this.userRealm = realm; dataSource = (DataSource) properties.get(UserCoreConstants.DATA_SOURCE); if (dataSource == null) { dataSource = DatabaseUtil.getRealmDataSource(realmConfig); } if (dataSource == null) { throw new UserStoreException("User Management Data Source is null"); } doInitialSetup(); this.persistDomain(); if (realmConfig.isPrimary()) { addInitialAdminData(Boolean.parseBoolean(realmConfig.getAddAdmin()), !isInitSetupDone()); } properties.put(UserCoreConstants.DATA_SOURCE, dataSource); if (log.isDebugEnabled()) { log.debug("The jdbcDataSource being used by JDBCUserStoreManager :: " + dataSource.hashCode()); } if (log.isDebugEnabled()) { log.debug("Ended " + System.currentTimeMillis()); } domain = realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME); /* * Initialize user roles cache as implemented in AbstractUserStoreManager */ initUserRolesCache(); } public void insertInitialData(Keyspace keyspace) throws UserStoreException { List<KeyspaceDefinition> keyspaceDefinitions = cluster.describeKeyspaces(); boolean foundKS = false; String keyspaceName = keyspace.getKeyspaceName(); for (KeyspaceDefinition keyspaceDefinition : keyspaceDefinitions) { if (keyspaceDefinition.getName().equals(keyspaceName)) { foundKS = true; } } if (!foundKS) { // add keyspace KeyspaceDefinition keyspaceDefinition = HFactory.createKeyspaceDefinition(keyspaceName); cluster.addKeyspace(keyspaceDefinition, true); // Holds the roles. ColumnFamilyDefinition roleCF = new BasicColumnFamilyDefinition(); roleCF.setName(CFConstants.UM_ROLES); roleCF.setKeyspaceName(keyspaceName); cluster.addColumnFamily(roleCF, true); // add user roles. Holds the roles per user. Mapped as (user_name, // tenant_id) -> role_name ColumnFamilyDefinition userRolesIndexCF = new BasicColumnFamilyDefinition(); userRolesIndexCF.setName(CFConstants.UM_USER_ROLE); userRolesIndexCF.setKeyspaceName(keyspaceName); cluster.addColumnFamily(userRolesIndexCF, true); // add user roles. Holds the users per role. Mapped as (role_name, // tenant_id) -> users ColumnFamilyDefinition rolesToUserIndex = new BasicColumnFamilyDefinition(); rolesToUserIndex.setName(CFConstants.UM_ROLE_USER_INDEX); rolesToUserIndex.setKeyspaceName(keyspaceName); cluster.addColumnFamily(rolesToUserIndex, true); // Holds the users. ColumnFamilyDefinition userCF = new BasicColumnFamilyDefinition(); userCF.setName(CFConstants.UM_USER); userCF.setKeyspaceName(keyspaceName); BasicColumnDefinition columnDefinition = new BasicColumnDefinition(); columnDefinition.setName(StringSerializer.get().toByteBuffer(CFConstants.UM_USER_NAME)); columnDefinition.setIndexName(CFConstants.UM_USER_NAME_INDEX); columnDefinition.setIndexType(ColumnIndexType.KEYS); columnDefinition.setValidationClass(ComparatorType.UTF8TYPE.getClassName()); userCF.addColumnDefinition(columnDefinition); cluster.addColumnFamily(userCF, true); // Holds user's attributes. ColumnFamilyDefinition claimsCF = new BasicColumnFamilyDefinition(); claimsCF.setName(CFConstants.UM_USER_ATTRIBUTE); claimsCF.setKeyspaceName(keyspaceName); cluster.addColumnFamily(claimsCF, true); } String msg = "Connected to Cassandra keyspace : " + keyspace.getKeyspaceName() + ". "; if (foundKS) { msg += " Keyspace already found. Not creating any column families or intialization data."; } else { msg += " Keyspace not found. Creating all column families and adding initialization data."; } log.info(msg); } /** * Checks if the role is existing the role store. */ @Override protected boolean doCheckExistingRole(String roleNameWithTenantDomain) throws UserStoreException { RoleContext roleContext = createRoleContext(roleNameWithTenantDomain); boolean isExisting = false; String roleName = roleContext.getRoleName(); Composite key = new Composite(); key.addComponent(roleName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); ColumnQuery<Composite, String, String> getCredentialQuery = HFactory.createColumnQuery(keyspace, CompositeSerializer.get(), stringSerializer, stringSerializer); getCredentialQuery.setColumnFamily(CFConstants.UM_ROLES).setKey(key).setName(CFConstants.UM_ROLE_NAME); HColumn<String, String> result = getCredentialQuery.execute().get(); if (result != null && result.getValue() != null) { isExisting = true; } return isExisting; } /** * Adds the user to the user store. */ @Override public void doAddUser(String userName, Object credential, String[] roleList, Map<String, String> claims, String profileName, boolean requirePasswordChange) throws UserStoreException { String userId = UUID.randomUUID().toString(); String saltValue = null; if (TRUE.equalsIgnoreCase(realmConfig.getUserStoreProperties().get(JDBCRealmConstants.STORE_SALTED_PASSWORDS))) { saltValue = Util.getSaltValue(); } String password = Util.preparePassword((String) credential, saltValue); if (doCheckExistingUser(userName)) { String message = "User with credentials " + userName + "exists"; UserStoreException userStoreException = new UserStoreException(message); log.error(message, userStoreException); throw userStoreException; } else { Mutator<Composite> mutator = HFactory.createMutator(keyspace, CompositeSerializer.get()); Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); // add user ID mutator.addInsertion(key, CFConstants.UM_USER, HFactory.createColumn(CFConstants.UM_USER_ID, userId, stringSerializer, stringSerializer)); mutator.addInsertion(key, CFConstants.UM_USER, HFactory.createColumn(CFConstants.UM_USER_NAME, userName, stringSerializer, stringSerializer)); mutator.addInsertion(key, CFConstants.UM_USER, HFactory.createColumn(CFConstants.UM_SECRET, password, stringSerializer, stringSerializer)); mutator.addInsertion(key, CFConstants.UM_USER, HFactory.createColumn(CFConstants.UM_SALT_VALUE, saltValue, stringSerializer, stringSerializer)); mutator.addInsertion(key, CFConstants.UM_USER, HFactory.createColumn(CFConstants.UM_REQUIRE_CHANGE_BOOLEAN, "false", stringSerializer, stringSerializer)); mutator.addInsertion(key, CFConstants.UM_USER, HFactory.createColumn(CFConstants.UM_TENANT_ID, tenantIdString, stringSerializer, stringSerializer)); mutator = addUserToRoleList(userName, roleList, mutator); if (claims != null) { mutator = addClaimsForUser(userId, claims, mutator); } try { mutator.execute(); if (log.isDebugEnabled()) { log.debug("Added user " + userName + " successfully"); } } catch (HectorException e) { // TODO- research and check how to identify cassandra failure // and handle it efficiently. throw new UserStoreException("Adding user failed.", e); } mutator.execute(); } } /** * Deletes a user by userName. */ @Override public void doDeleteUser(String userName) throws UserStoreException { Mutator<Composite> mutator = HFactory.createMutator(keyspace, CompositeSerializer.get()); String[] roles = doGetExternalRoleListOfUser(userName, ""); for (String role : roles) { Composite key = new Composite(); key.addComponent(role, stringSerializer); key.addComponent(tenantIdString, stringSerializer); ColumnFamilyTemplate<Composite, String> userCFTemplate = new ThriftColumnFamilyTemplate<Composite, String>( keyspace, CFConstants.UM_ROLE_USER_INDEX, CompositeSerializer.get(), StringSerializer.get()); try { userCFTemplate.deleteColumn(key, userName); } catch (HectorException e) { log.error("Error during deletion ", e); } } Composite userKey = new Composite(); userKey.addComponent(userName, stringSerializer); userKey.addComponent(tenantIdString, stringSerializer); mutator.addDeletion(userKey, CFConstants.UM_USER_ROLE, null, CompositeSerializer.get()); mutator.addDeletion(userKey, CFConstants.UM_USER, null, CompositeSerializer.get()); mutator.execute(); if (log.isDebugEnabled()) { log.debug("Deleted user " + userName + " successfully"); } } /** * Changes the password of the user. */ @Override public void doUpdateCredential(String userName, Object newCredential, Object oldCredential) throws UserStoreException { this.doUpdateCredentialByAdmin(userName, newCredential); } @Override public void doUpdateCredentialByAdmin(String userName, Object newCredential) throws UserStoreException { if (!checkUserPasswordValid(newCredential)) { throw new UserStoreException( "Credential not valid. Credential must be a non null string with following format, " + realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_JAVA_REG_EX)); } String saltValue = null; if (TRUE.equalsIgnoreCase(realmConfig.getUserStoreProperties().get(JDBCRealmConstants.STORE_SALTED_PASSWORDS))) { saltValue = Util.getSaltValue(); } String password = Util.preparePassword((String) newCredential, saltValue); Mutator<Composite> mutator = HFactory.createMutator(keyspace, CompositeSerializer.get()); Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); mutator.addInsertion(key, CFConstants.UM_USER, HFactory.createColumn(CFConstants.UM_SECRET, password, stringSerializer, stringSerializer)); mutator.addInsertion(key, CFConstants.UM_USER, HFactory.createColumn(CFConstants.UM_SALT_VALUE, saltValue, stringSerializer, stringSerializer)); try { mutator.execute(); if (log.isDebugEnabled()) { log.debug("Changed password for user " + userName + "successfully"); } } catch (HectorException e) { throw new UserStoreException("Change Password failed.", e); } } /** * Checks if the user is existing in the user store. */ @Override protected boolean doCheckExistingUser(String userName) throws UserStoreException { Boolean isExist = false; Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); ColumnQuery<Composite, String, String> getCredentialQuery = HFactory.createColumnQuery(keyspace, CompositeSerializer.get(), stringSerializer, stringSerializer); getCredentialQuery.setColumnFamily(CFConstants.UM_USER).setKey(key).setName(CFConstants.UM_USER_NAME); HColumn<String, String> result = getCredentialQuery.execute().get(); if (result != null && result.getValue() != null) { isExist = true; } return isExist; } /** * Adds a role to the role store. */ @Override public void doAddRole(String roleName, String[] userList, boolean shared) throws UserStoreException { Mutator<Composite> mutator = HFactory.createMutator(keyspace, CompositeSerializer.get()); Composite composite = new Composite(); composite.addComponent(roleName, stringSerializer); composite.addComponent(tenantIdString, stringSerializer); mutator.addInsertion(composite, CFConstants.UM_ROLES, HFactory.createColumn(CFConstants.UM_ROLE_NAME, roleName, stringSerializer, stringSerializer)); mutator.addInsertion(composite, CFConstants.UM_ROLES, HFactory.createColumn(CFConstants.UM_TENANT_ID, tenantIdString, stringSerializer, stringSerializer)); if (userList != null && userList.length > 0) { addRoleToUsersList(userList, roleName, mutator); } mutator.execute(); } /** * Deletes a role by role name from the role store. */ @Override public void doDeleteRole(String roleName) throws UserStoreException { Mutator<Composite> mutator = HFactory.createMutator(keyspace, CompositeSerializer.get()); String[] users = getUserListOfRole(roleName); for (String userName : users) { Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); ColumnFamilyTemplate<Composite, String> userCFTemplate = new ThriftColumnFamilyTemplate<Composite, String>( keyspace, CFConstants.UM_USER_ROLE, CompositeSerializer.get(), StringSerializer.get()); try { userCFTemplate.deleteColumn(key, roleName); } catch (HectorException e) { throw new UserStoreException("Exception occured when deleting Role", e); } } Composite roleKey = new Composite(); roleKey.addComponent(roleName, stringSerializer); roleKey.addComponent(tenantIdString, stringSerializer); mutator.addDeletion(roleKey, CFConstants.UM_ROLE_USER_INDEX, null, CompositeSerializer.get()); mutator.addDeletion(roleKey, CFConstants.UM_ROLES, null, CompositeSerializer.get()); try { mutator.execute(); } catch (HectorException e) { // TODO- research and check how to identify cassandra failure and // handle it efficiently. throw new UserStoreException("Role deletion failed.", e); } } /** * Updates the role name in the role store. */ @Override public void doUpdateRoleName(String roleName, String newRoleName) throws UserStoreException { doAddRole(newRoleName, getUserListOfRole(roleName), false); doDeleteRole(roleName); } /** * Maps the users to a role list. Adds the (username, tenantId) -> roleList * and (role, tenantId) -> userName * * @param userName The username of the user the roles need to be added to. * @param roleList The list of roles that needs to be mapped against the user. */ private void addUserToRoleList(String userName, String[] roleList) { Mutator<Composite> mutator = HFactory.createMutator(keyspace, CompositeSerializer.get()); if (roleList != null) { for (String role : roleList) { Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); mutator.addInsertion(key, CFConstants.UM_USER_ROLE, HFactory.createColumn(role, role)); Composite keyRole = new Composite(); keyRole.addComponent(role, stringSerializer); keyRole.addComponent(tenantIdString, stringSerializer); mutator.addInsertion(keyRole, CFConstants.UM_ROLE_USER_INDEX, HFactory.createColumn(userName, userName)); } mutator.execute(); } } /** * Maps the users to a role list. Adds the (username, tenantId) -> roleList * and (role, tenantId) -> userName * * @param userName The username of the user the roles need to be added to. * @param roleList The list of roles that needs to be mapped against the user. * @param mutator Passes the mutator and returns it with the insert statements. */ private Mutator<Composite> addUserToRoleList(String userName, String[] roleList, Mutator<Composite> mutator) { if (roleList != null && mutator != null) { for (String role : roleList) { Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); mutator.addInsertion(key, CFConstants.UM_USER_ROLE, HFactory.createColumn(role, role)); Composite keyRole = new Composite(); keyRole.addComponent(role, stringSerializer); keyRole.addComponent(tenantIdString, stringSerializer); mutator.addInsertion(keyRole, CFConstants.UM_ROLE_USER_INDEX, HFactory.createColumn(userName, userName)); } } return mutator; } /** * Maps the role to a user list. Adds the (username, tenantId) -> roleList * and (role, tenantId) -> userName * * @param userNames The username list of the user the role need to be added to. * @param roleName The role that needs to be mapped against the user list. * @param mutator Passes the mutator and returns it with the insert statements. */ private Mutator<Composite> addRoleToUsersList(String[] userNames, String roleName, Mutator<Composite> mutator) { if (userNames != null) { for (String userName : userNames) { Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); mutator.addInsertion(key, CFConstants.UM_USER_ROLE, HFactory.createColumn(roleName, roleName)); Composite keyRole = new Composite(); keyRole.addComponent(roleName, stringSerializer); keyRole.addComponent(tenantIdString, stringSerializer); mutator.addInsertion(keyRole, CFConstants.UM_ROLE_USER_INDEX, HFactory.createColumn(userName, userName)); } } return mutator; } /** * Authenticates a user given the user name and password against the user * store. */ @Override public boolean doAuthenticate(String userName, Object credential) throws UserStoreException { String password = (String) credential; boolean isAuthed = false; if (!checkUserNameValid(userName)) { log.error("Invalid Username"); return false; } if (!checkUserPasswordValid(credential)) { log.error("Invalid password"); return false; } if (UserCoreUtil.isRegistryAnnonymousUser(userName)) { log.error("Anonnymous user trying to login"); return false; } Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); ColumnFamilyTemplate<Composite, String> userCFTemplate = new ThriftColumnFamilyTemplate<Composite, String>( keyspace, CFConstants.UM_USER, CompositeSerializer.get(), StringSerializer.get()); ColumnFamilyResult<Composite, String> result = userCFTemplate.queryColumns(key); String saltVallue = result.getString(CFConstants.UM_SALT_VALUE); String storedPassword = result.getString(CFConstants.UM_SECRET); if (TRUE.equalsIgnoreCase(realmConfig.getUserStoreProperty(JDBCRealmConstants.STORE_SALTED_PASSWORDS))) { password = Util.preparePassword(password, saltVallue); if ((storedPassword != null) && (storedPassword.equals(password))) { isAuthed = true; } } return isAuthed; } /** * Lists the users in the user store. */ @Override protected String[] doListUsers(String filter, int maxItemLimit) throws UserStoreException { List<String> users = new ArrayList<String>(); int arrayLength = 0; if (maxItemLimit == 0) { return new String[0]; } int givenMax = UserCoreConstants.MAX_USER_ROLE_LIST; try { givenMax = Integer.parseInt(realmConfig .getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_MAX_USER_LIST)); } catch (Exception e) { givenMax = UserCoreConstants.MAX_USER_ROLE_LIST; if (log.isDebugEnabled()) { log.debug("Realm configuration maximum not set : Using User Core Constant value instead!", e); } } if (maxItemLimit < 0 || maxItemLimit > givenMax) { maxItemLimit = givenMax; } RangeSlicesQuery<String, String, String> rangeSliceQuery = HFactory.createRangeSlicesQuery(keyspace, stringSerializer, stringSerializer, stringSerializer); rangeSliceQuery.setColumnFamily(CFConstants.UM_USER); rangeSliceQuery.setRange(filter, null, false, Integer.MAX_VALUE); rangeSliceQuery.addEqualsExpression(CFConstants.UM_TENANT_ID, tenantIdString); // TODO - Need to check how to use the filter for range rangeSliceQuery.setKeys("", ""); rangeSliceQuery.setRowCount(maxItemLimit); QueryResult<OrderedRows<String, String, String>> result = rangeSliceQuery.execute(); if (result != null) { OrderedRows<String, String, String> rows = result.get(); if (rows.getCount() <= 0) { // reformatted to avoid nesting too many blocks return users.toArray(new String[arrayLength]); } arrayLength = rows.getCount(); Iterator<Row<String, String, String>> rowsIterator = rows.iterator(); while (rowsIterator.hasNext()) { Row<String, String, String> row = rowsIterator.next(); if (row.getColumnSlice().getColumnByName(CFConstants.UM_USER_ID).getValue() != null) { String name = row.getColumnSlice().getColumnByName(CFConstants.UM_USER_NAME).getValue(); // append the domain if exist name = UserCoreUtil.addDomainToName(name, domain); users.add(name); } } } return users.toArray(new String[arrayLength]); } /** * Get the role names in the roles store. */ @Override public String[] doGetRoleNames(String filter, int maxItemLimit) throws UserStoreException { List<String> roles = new ArrayList<String>(); if (maxItemLimit == 0) { return new String[0]; } int givenMax = UserCoreConstants.MAX_USER_ROLE_LIST; try { givenMax = Integer.parseInt(realmConfig .getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_MAX_ROLE_LIST)); } catch (Exception e) { givenMax = UserCoreConstants.MAX_USER_ROLE_LIST; if (log.isDebugEnabled()) { log.debug("Realm configuration maximum not set : Using User Core Constant value instead!", e); } } if (maxItemLimit < 0 || maxItemLimit > givenMax) { maxItemLimit = givenMax; } int arrayLength = 0; String domain = realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME); RangeSlicesQuery<String, String, String> rangeSliceQuery = HFactory.createRangeSlicesQuery(keyspace, stringSerializer, stringSerializer, stringSerializer); rangeSliceQuery.setColumnFamily(CFConstants.UM_ROLES); rangeSliceQuery.setRange(null, null, false, Integer.MAX_VALUE); rangeSliceQuery.addEqualsExpression(CFConstants.UM_TENANT_ID, tenantIdString); rangeSliceQuery.setKeys("", ""); rangeSliceQuery.setRowCount(maxItemLimit); QueryResult<OrderedRows<String, String, String>> result = rangeSliceQuery.execute(); if (result != null) { OrderedRows<String, String, String> rows = result.get(); if (rows.getCount() <= 0) { return roles.toArray(new String[arrayLength]); } arrayLength = rows.getCount(); Iterator<Row<String, String, String>> rowsIterator = rows.iterator(); while (rowsIterator.hasNext()) { Row<String, String, String> row = rowsIterator.next(); if (row.getColumnSlice().getColumnByName(CFConstants.UM_ROLE_NAME).getValue() != null) { String name = row.getColumnSlice().getColumnByName(CFConstants.UM_ROLE_NAME).getValue(); // append the domain if exist name = UserCoreUtil.addDomainToName(name, domain); roles.add(name); } } } return roles.toArray(new String[arrayLength]); } /** * Checks if user is existing in the user store. */ @Override public boolean isExistingUser(String userName) throws UserStoreException { if (CarbonConstants.REGISTRY_SYSTEM_USERNAME.equals(userName)) { return true; } return !(getExistingUserId(userName, CFConstants.DEFAULT_TYPE) == null); } /** * Check if role is existing in the role store. */ @Override public boolean isExistingRole(String roleName) throws UserStoreException { return doCheckExistingRole(roleName); } /** * Get the list of users mapped to a role. */ @Override public String[] doGetUserListOfRole(String roleName, String filter) throws UserStoreException { List<String> usersList = new ArrayList<String>(); Composite key = new Composite(); key.addComponent(roleName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); SliceQuery<Composite, String, String> query = HFactory .createSliceQuery(keyspace, CompositeSerializer.get(), StringSerializer.get(), StringSerializer.get()) .setKey(key).setColumnFamily(CFConstants.UM_ROLE_USER_INDEX); ColumnSliceIterator<Composite, String, String> iterator = new ColumnSliceIterator<Composite, String, String>( query, null, "\uFFFF", false); while (iterator.hasNext()) { HColumn<String, String> column = iterator.next(); usersList.add(column.getValue()); } return usersList.toArray(new String[usersList.size()]); } /** * Get the role list of a user. */ @Override public String[] getRoleListOfUser(String userName) throws UserStoreException { return doGetRoleListOfUser(userName, null); } private Mutator<Composite> addClaimsForUser(String userName, Map<String, String> claims, Mutator<Composite> mutator) { Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); // add claims for (Map.Entry<String, String> claimsVals : claims.entrySet()) { mutator.addInsertion(key, CFConstants.UM_USER_ATTRIBUTE, HFactory.createColumn(claimsVals.getKey(), claimsVals.getValue())); mutator.addInsertion(key, CFConstants.UM_USER_ATTRIBUTE, HFactory.createColumn(CFConstants.UM_TENANT_ID, tenantIdString)); } return mutator; } /** * Gets the external role list of a user. */ @Override public String[] doGetExternalRoleListOfUser(String userName, String filter) throws UserStoreException { List<String> roles = new ArrayList<String>(); int arrayLength = 0; Composite key = new Composite(); key.addComponent(userName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); SliceQuery<Composite, String, String> query = HFactory .createSliceQuery(keyspace, CompositeSerializer.get(), StringSerializer.get(), StringSerializer.get()) .setKey(key).setColumnFamily(CFConstants.UM_USER_ROLE); ColumnSliceIterator<Composite, String, String> iterator = new ColumnSliceIterator<Composite, String, String>( query, null, "\uFFFF", false); while (iterator.hasNext()) { HColumn<String, String> column = iterator.next(); roles.add(column.getValue()); } return roles.toArray(new String[arrayLength]); } /** * Update the role list of a user. */ @Override public void doUpdateRoleListOfUser(String userName, String[] deletedRoles, String[] newRoles) throws UserStoreException { RoleBreakdown breakdown; String[] roles; String[] sharedRoles; try { Mutator<Composite> mutator = HFactory.createMutator(keyspace, CompositeSerializer.get()); // if user name and role names are prefixed with domain name, remove // the domain name String[] userNames = userName.split(CarbonConstants.DOMAIN_SEPARATOR); if (userNames.length > 1) { userName = userNames[1]; } if (deletedRoles != null && deletedRoles.length > 0) { // if user name and role names are prefixed with domain name, // remove the domain name breakdown = getSharedRoleBreakdown(deletedRoles); roles = breakdown.getRoles(); sharedRoles = breakdown.getSharedRoles(); if (roles.length > 0) { Composite userKey = new Composite(); userKey.addComponent(userName, stringSerializer); userKey.addComponent(tenantIdString, stringSerializer); for (String role : roles) { Composite key = new Composite(); key.addComponent(role, stringSerializer); key.addComponent(tenantIdString, stringSerializer); ColumnFamilyTemplate<Composite, String> userCFTemplate = new ThriftColumnFamilyTemplate<Composite, String>( keyspace, CFConstants.UM_USER_ROLE, CompositeSerializer.get(), StringSerializer.get()); ColumnFamilyTemplate<Composite, String> roleCFTemplate = new ThriftColumnFamilyTemplate<Composite, String>( keyspace, CFConstants.UM_ROLE_USER_INDEX, CompositeSerializer.get(), StringSerializer.get()); try { roleCFTemplate.deleteColumn(mutator, key, userName); userCFTemplate.deleteColumn(mutator, userKey, role); } catch (HectorException e) { throw new UserStoreException("Ex eption occured when updating role list of a user", e); } } } if (sharedRoles != null && sharedRoles.length > 0) { //TODO TO-Be Completed } clearUserRolesCacheByTenant(this.tenantId); } if (newRoles != null && newRoles.length > 0) { // if user name and role names are prefixed with domain name, // remove the domain name breakdown = getSharedRoleBreakdown(newRoles); roles = breakdown.getRoles(); sharedRoles = breakdown.getSharedRoles(); if (roles.length > 0) { addUserToRoleList(userName, roles); } if (sharedRoles != null && sharedRoles.length > 0) { //TODO TO-Be Completed } } } catch (HectorException e) { throw new UserStoreException(e.getMessage(), e); } } /** * Update the user list mapped to a role. */ @Override public void doUpdateUserListOfRole(String roleName, String[] deletedUsers, String[] newUsers) throws UserStoreException { Mutator<Composite> mutator = HFactory.createMutator(keyspace, CompositeSerializer.get()); RoleContext ctx = createRoleContext(roleName); roleName = ctx.getRoleName(); boolean isShared = ctx.isShared(); if (!isShared) { //TODO TO BE Implemented } if (deletedUsers != null && deletedUsers.length > 0) { if (isShared) { //TODO TO BE Implemented } else { if (deletedUsers.length > 0) { Composite key = new Composite(); key.addComponent(roleName, stringSerializer); key.addComponent(tenantIdString, stringSerializer); for (String user : deletedUsers) { Composite userKey = new Composite(); userKey.addComponent(user, stringSerializer); userKey.addComponent(tenantIdString, stringSerializer); ColumnFamilyTemplate<Composite, String> userCFTemplate = new ThriftColumnFamilyTemplate<Composite, String>( keyspace, CFConstants.UM_USER_ROLE, CompositeSerializer.get(), StringSerializer.get()); ColumnFamilyTemplate<Composite, String> roleCFTemplate = new ThriftColumnFamilyTemplate<Composite, String>( keyspace, CFConstants.UM_ROLE_USER_INDEX, CompositeSerializer.get(), StringSerializer.get()); try { roleCFTemplate.deleteColumn(mutator, key, user); userCFTemplate.deleteColumn(mutator, userKey, roleName); } catch (HectorException e) { log.error(e.getMessage(), e); throw new UserStoreException("Error during the updating of a user's role list"); } } } } } // need to clear user roles cache upon roles update clearUserRolesCacheByTenant(this.tenantId); if (newUsers != null && newUsers.length > 0) { if (isShared) { //TODO TO BE Implemented } else { addRoleToUsersList(newUsers, roleName, mutator); } } mutator.execute(); } /** * Break the provided role list based on whether roles are shared or not * * @param rolesList * @return */ private RoleBreakdown getSharedRoleBreakdown(String[] rolesList) { List<String> roles = new ArrayList<String>(); List<Integer> tenantIds = new ArrayList<Integer>(); List<String> sharedRoles = new ArrayList<String>(); List<Integer> sharedTenantIds = new ArrayList<Integer>(); for (String role : rolesList) { String[] deletedRoleNames = role.split(CarbonConstants.DOMAIN_SEPARATOR); if (deletedRoleNames.length > 1) { role = deletedRoleNames[1]; } CassandraRoleContext ctx = (CassandraRoleContext) createRoleContext(role); role = ctx.getRoleName(); int roleTenantId = ctx.getTenantId(); boolean isShared = ctx.isShared(); if (isShared) { sharedRoles.add(role); sharedTenantIds.add(roleTenantId); } else { roles.add(role); tenantIds.add(roleTenantId); } } RoleBreakdown breakdown = new RoleBreakdown(); // Non shared roles and tenant ids breakdown.setRoles(roles.toArray(new String[roles.size()])); breakdown.setTenantIds(tenantIds.toArray(new Integer[tenantIds.size()])); // Shared roles and tenant ids breakdown.setSharedRoles(sharedRoles.toArray(new String[sharedRoles.size()])); breakdown.setSharedTenantids(sharedTenantIds.toArray(new Integer[sharedTenantIds.size()])); return breakdown; } /** * Role context. */ @Override protected RoleContext createRoleContext(String roleName) { CassandraRoleContext searchCtx = new CassandraRoleContext(); String[] roleNameParts = roleName.split(UserCoreConstants.TENANT_DOMAIN_COMBINER); String nullString = "null"; if (roleNameParts.length > 1 && (roleNameParts[1] == null || nullString.equals(roleNameParts[1]))) { roleNameParts = new String[]{roleNameParts[0]}; } int tenantId = -1; if (roleNameParts.length > 1) { tenantId = Integer.parseInt(roleNameParts[1]); searchCtx.setTenantId(tenantId); } else { tenantId = this.tenantId; searchCtx.setTenantId(tenantId); } if (tenantId != this.tenantId) { searchCtx.setShared(true); } searchCtx.setRoleName(roleNameParts[0]); return searchCtx; } @Override protected void persistDomain() throws UserStoreException { String domain = UserCoreUtil.getDomainName(this.realmConfig); if (domain != null) { UserCoreUtil.persistDomain(domain, this.tenantId, this.dataSource); } } /** * Get the existing user's Id */ private String getExistingUserId(String identifier, String credentialTypeName) { return Util.getExistingUserId(credentialTypeName, identifier, keyspace); } @Override public RealmConfiguration getRealmConfiguration() { return this.realmConfig; } @Override protected String getMyDomainName() { return UserCoreUtil.getDomainName(realmConfig); } @Override public String[] getUserListFromProperties(String property, String value, String profileName) throws UserStoreException { return new String[0]; } @Override protected String[] doGetSharedRoleNames(String tenantDomain, String filter, int maxItemLimit) throws UserStoreException { return new String[0]; } @Override public boolean doCheckIsUserInRole(String userName, String roleName) throws UserStoreException { String[] roles = doGetExternalRoleListOfUser(userName, "*"); if (roles != null) { for (String role : roles) { if (role.equalsIgnoreCase(roleName)) { return true; } } } return false; } @Override protected String[] doGetSharedRoleListOfUser(String userName, String tenantDomain, String filter) throws UserStoreException { return new String[0]; } @Override public String[] getAllProfileNames() throws UserStoreException { return new String[0]; } @Override public boolean isReadOnly() throws UserStoreException { if (TRUE.equalsIgnoreCase(realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_READ_ONLY))) { return true; } return false; } @Override public Date getPasswordExpirationTime(String username) throws UserStoreException { return null; } @Override public int getUserId(String username) throws UserStoreException { return 0; } @Override public int getTenantId(String username) throws UserStoreException { return tenantId; } @Override public int getTenantId() throws UserStoreException { return this.tenantId; } @Override public Map<String, String> getProperties(org.wso2.carbon.user.api.Tenant tenant) throws org.wso2.carbon.user.api.UserStoreException { return null; } @Override public boolean isMultipleProfilesAllowed() { return false; } @Override public void addRememberMe(String userName, String token) throws org.wso2.carbon.user.api.UserStoreException { } @Override public boolean isValidRememberMeToken(String userName, String token) throws org.wso2.carbon.user.api.UserStoreException { return false; } @Override public boolean isBulkImportSupported() throws UserStoreException { return false; } @Override public void doSetUserClaimValue(String userName, String claimURI, String claimValue, String profileName) throws UserStoreException { } @Override public void doSetUserClaimValues(String userName, Map<String, String> claims, String profileName) throws UserStoreException { } @Override public void doDeleteUserClaimValue(String userName, String claimURI, String profileName) throws UserStoreException { } @Override public void doDeleteUserClaimValues(String userName, String[] claims, String profileName) throws UserStoreException { } @Override protected String[] doGetDisplayNamesForInternalRole(String[] strings) throws UserStoreException { throw new UserStoreException( "doGetDisplayNamesForInternalRole(String[]) not implemented for CassandraUserStoreManager"); } @Override public String[] getProfileNames(String userName) throws UserStoreException { return new String[0]; } /** * Returns the propertis of the userstore */ @Override public Map<String, String> getProperties(Tenant tenant) throws UserStoreException { return null; } @Override public org.wso2.carbon.user.api.Properties getDefaultUserStoreProperties() { return new Properties(); } @Override public Map<String, String> getUserPropertyValues(String userName, String[] propertyNames, String profileName) throws UserStoreException { return null; } public class RoleBreakdown { private String[] roles; private Integer[] tenantIds; private String[] sharedRoles; private Integer[] sharedTenantids; public String[] getRoles() { if (roles != null) { return roles.clone(); } return new String[0]; } public void setRoles(String[] roles) { if (roles != null) { this.roles = roles.clone(); } } public Integer[] getTenantIds() { if (tenantIds != null) { return tenantIds.clone(); } return new Integer[0]; } public void setTenantIds(Integer[] tenantIds) { if (tenantIds != null) { this.tenantIds = tenantIds.clone(); } } public String[] getSharedRoles() { if (sharedRoles != null) { return sharedRoles.clone(); } return new String[0]; } public void setSharedRoles(String[] sharedRoles) { if (sharedRoles != null) { this.sharedRoles = sharedRoles.clone(); } } public Integer[] getSharedTenantids() { if (sharedTenantids != null) { return sharedTenantids.clone(); } else return new Integer[0]; } public void setSharedTenantids(Integer[] sharedTenantids) { if (sharedTenantids != null) { this.sharedTenantids = sharedTenantids.clone(); } } } }