/* * 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.identity.user.registration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xerces.impl.Constants; import org.apache.xerces.util.SecurityManager; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.RegistryType; import org.wso2.carbon.identity.base.IdentityConstants; import org.wso2.carbon.identity.base.IdentityException; import org.wso2.carbon.identity.core.IdentityClaimManager; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.user.registration.dto.PasswordRegExDTO; import org.wso2.carbon.identity.user.registration.dto.TenantRegistrationConfig; import org.wso2.carbon.identity.user.registration.dto.UserDTO; import org.wso2.carbon.identity.user.registration.dto.UserFieldDTO; import org.wso2.carbon.identity.user.registration.util.CarbonEntityResolver; import org.wso2.carbon.registry.core.Registry; import org.wso2.carbon.registry.core.Resource; import org.wso2.carbon.registry.core.exceptions.RegistryException; import org.wso2.carbon.user.core.Permission; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.UserRealm; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.core.UserStoreManager; import org.wso2.carbon.user.core.claim.Claim; import org.wso2.carbon.user.mgt.UserMgtConstants; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class UserRegistrationService { private static final Log log = LogFactory.getLog(UserRegistrationService.class); private static final String SECURITY_MANAGER_PROPERTY = Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY; private static final int ENTITY_EXPANSION_LIMIT = 0; public static final String EXTERNAL_GENERAL_ENTITIES_URI = "http://xml.org/sax/features/external-general-entities"; /** * This service method will return back all available password validation regular expressions * against the corresponding domain names. * * @return * @throws IdentityException */ public PasswordRegExDTO[] getPasswordRegularExpressions() throws IdentityException { UserRealm realm = null; realm = IdentityTenantUtil.getRealm(null, null); List<PasswordRegExDTO> passwordRegExList = new ArrayList<PasswordRegExDTO>(); PasswordRegExDTO passwordRegEx; try { UserStoreManager manager = realm.getUserStoreManager(); String domainName; String regEx; while (manager != null) { domainName = manager.getRealmConfiguration().getUserStoreProperty( UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME); regEx = manager.getRealmConfiguration().getUserStoreProperty( UserCoreConstants.RealmConfig.PROPERTY_JS_REG_EX); if (regEx != null && regEx.length() > 0) { passwordRegEx = new PasswordRegExDTO(); passwordRegEx.setDomainName(domainName); passwordRegEx.setRegEx(regEx); passwordRegExList.add(passwordRegEx); } manager = manager.getSecondaryUserStoreManager(); } } catch (UserStoreException e) { log.error(e); throw IdentityException.error( "Error occured while loading password validation regular expressions."); } return passwordRegExList.toArray(new PasswordRegExDTO[passwordRegExList.size()]); } public UserFieldDTO[] readUserFieldsForUserRegistration(String dialect) throws IdentityException { IdentityClaimManager claimManager = null; Claim[] claims = null; List<UserFieldDTO> claimList = null; UserRealm realm = null; claimManager = IdentityClaimManager.getInstance(); realm = IdentityTenantUtil.getRealm(null, null); claims = claimManager.getAllSupportedClaims(dialect, realm); if (claims == null || claims.length == 0) { return new UserFieldDTO[0]; } claimList = new ArrayList<UserFieldDTO>(); for (Claim claim : claims) { if (claim.getDisplayTag() != null && !IdentityConstants.PPID_DISPLAY_VALUE.equals(claim.getDisplayTag())) { if (UserCoreConstants.ClaimTypeURIs.ACCOUNT_STATUS.equals(claim.getClaimUri())) { continue; } if (!claim.isReadOnly()) { claimList.add(getUserFieldDTO(claim.getClaimUri(), claim.getDisplayTag(), claim.isRequired(), claim.getDisplayOrder(), claim.getRegEx(), claim.isSupportedByDefault())); } } } return claimList.toArray(new UserFieldDTO[claimList.size()]); } public void addUser(UserDTO user) throws Exception { UserFieldDTO[] userFieldDTOs = null; Map<String, String> userClaims = null; userFieldDTOs = user.getUserFields(); userClaims = new HashMap<String, String>(); if (userFieldDTOs != null) { for (UserFieldDTO userFieldDTO : userFieldDTOs) { userClaims.put(userFieldDTO.getClaimUri(), userFieldDTO.getFieldValue()); } } UserRealm realm = null; String tenantAwareUserName = MultitenantUtils.getTenantAwareUsername(user.getUserName()); String tenantName = MultitenantUtils.getTenantDomain(user.getUserName()); realm = IdentityTenantUtil.getRealm(tenantName, null); Registry registry = IdentityTenantUtil.getRegistry(null, null); addUser(tenantAwareUserName, user.getPassword(), userClaims, null, realm); } public boolean isAddUserEnabled() throws Exception { UserRealm userRealm = IdentityTenantUtil.getRealm(null, null); if (userRealm != null) { UserStoreManager userStoreManager = userRealm.getUserStoreManager(); if (userStoreManager != null) { return !userStoreManager.isReadOnly(); } } return false; } public boolean isAddUserWithOpenIDEnabled() throws Exception { return false; } public boolean isAddUserWithInfoCardEnabled() throws Exception { return false; } /** * Check whether the user exist. * @param username Username of the user. * @return True if exist. * @throws Exception */ public boolean isUserExist(String username) throws UserRegistrationException { try { return CarbonContext.getThreadLocalCarbonContext().getUserRealm(). getUserStoreManager().isExistingUser(username); } catch (UserStoreException e) { log.error("Unable to connect to the user store.", e); throw new UserRegistrationException("Internal error occurred while connecting to the user store.", e); } } private UserFieldDTO getUserFieldDTO(String claimUri, String displayName, boolean isRequired, int displayOrder, String regex, boolean isSupportedByDefault) { UserFieldDTO fieldDTO = null; fieldDTO = new UserFieldDTO(); fieldDTO.setClaimUri(claimUri); fieldDTO.setFieldName(displayName); fieldDTO.setRequired(isRequired); fieldDTO.setDisplayOrder(displayOrder); fieldDTO.setSupportedByDefault(isSupportedByDefault); fieldDTO.setRegEx(regex); return fieldDTO; } private void addUser(String userName, String password, Map<String, String> claimList, String profileName, UserRealm realm) throws IdentityException { UserStoreManager admin = null; Permission permission = null; try { // get config from tenant registry TenantRegistrationConfig tenantConfig = getTenantSignUpConfig(realm.getUserStoreManager().getTenantId()); // set tenant config specific sign up domain if (tenantConfig != null && !"".equals(tenantConfig.getSignUpDomain())) { int index = userName.indexOf(UserCoreConstants.DOMAIN_SEPARATOR); if (index > 0) { userName = tenantConfig.getSignUpDomain().toUpperCase() + UserCoreConstants.DOMAIN_SEPARATOR + userName.substring(index + 1); } else { userName = tenantConfig.getSignUpDomain().toUpperCase() + UserCoreConstants.DOMAIN_SEPARATOR + userName; } } // add user to the relevant user store admin = realm.getUserStoreManager(); if (!isUserNameWithAllowedDomainName(userName, realm)) { throw IdentityException.error("Domain does not permit self registration"); } // add user admin.addUser(userName, password, null, claimList, profileName); // after adding the user, assign specif roles List<String> roleNamesArr = getRoleName(userName, tenantConfig); if (claimList.get(SelfRegistrationConstants.SIGN_UP_ROLE_CLAIM_URI) != null) { // check is a user role is specified as a claim by the client, if so add it to the roles list if (tenantConfig != null) { roleNamesArr.add(tenantConfig.getSignUpDomain().toUpperCase() + UserCoreConstants.DOMAIN_SEPARATOR + claimList.get(SelfRegistrationConstants.SIGN_UP_ROLE_CLAIM_URI)); } else { roleNamesArr.add(UserCoreConstants.INTERNAL_DOMAIN + UserCoreConstants.DOMAIN_SEPARATOR + claimList.get(SelfRegistrationConstants.SIGN_UP_ROLE_CLAIM_URI)); } } String[] identityRoleNames = roleNamesArr.toArray(new String[roleNamesArr.size()]); for (int i = 0; i < identityRoleNames.length; i++) { // if this is the first time a user signs up, needs to create role doAddUser(i,admin, identityRoleNames,userName,permission); } } catch (UserStoreException e) { throw IdentityException.error("Error occurred while adding user : " + userName + ". " + e.getMessage(), e); } } private void doAddUser(int i, UserStoreManager admin, String[] identityRoleNames, String userName,Permission permission) throws IdentityException, UserStoreException { try { if (!admin.isExistingRole(identityRoleNames[i], false)) { permission = new Permission("/permission/admin/login", UserMgtConstants.EXECUTE_ACTION); admin.addRole(identityRoleNames[i], new String[]{userName}, new Permission[]{permission}, false); } else { // if role already exists, just add user to role admin.updateUserListOfRole(identityRoleNames[i], new String[]{}, new String[]{userName}); } } catch (org.wso2.carbon.user.api.UserStoreException e) { // If something goes wrong here - then remove the already added user. admin.deleteUser(userName); throw IdentityException.error("Error occurred while adding user : " + userName + ". " + e.getMessage(), e); } } private boolean isUserNameWithAllowedDomainName(String userName, UserRealm realm) throws IdentityException { int index; index = userName.indexOf("/"); // Check whether we have a secondary UserStoreManager setup. if (index > 0) { // Using the short-circuit. User name comes with the domain name. try { return !realm.getRealmConfiguration().isRestrictedDomainForSlefSignUp( userName.substring(0, index)); } catch (UserStoreException e) { throw IdentityException.error(e.getMessage(), e); } } return true; } private List<String> getRoleName(String userName, TenantRegistrationConfig tenantConfig) { // check for tenant config, if available return roles specified in tenant config if (tenantConfig != null) { List<String> roleNamesArr = new ArrayList<String>(); Map<String, Boolean> roles = tenantConfig.getRoles(); for (Map.Entry<String, Boolean> entry : roles.entrySet()) { String roleName; if (entry.getValue()) { // external role roleName = tenantConfig.getSignUpDomain().toUpperCase() + UserCoreConstants.DOMAIN_SEPARATOR + entry.getKey(); } else { // internal role roleName = UserCoreConstants.INTERNAL_DOMAIN + UserCoreConstants.DOMAIN_SEPARATOR + entry.getKey(); } roleNamesArr.add(roleName); } // return, don't need to worry about roles specified in identity.xml return roleNamesArr; } String roleName = IdentityUtil.getProperty(SelfRegistrationConstants.ROLE_NAME_PROPERTY); boolean externalRole = Boolean.parseBoolean(IdentityUtil.getProperty( SelfRegistrationConstants.ROLE_EXTERNAL_PROPERTY)); String domainName = UserCoreConstants.INTERNAL_DOMAIN; if (externalRole) { domainName = IdentityUtil.extractDomainFromName(userName); } if (roleName == null || roleName.trim().length() == 0) { roleName = IdentityConstants.IDENTITY_DEFAULT_ROLE; } if (domainName != null && domainName.trim().length() > 0) { roleName = domainName.toUpperCase() + CarbonConstants.DOMAIN_SEPARATOR + roleName; } return new ArrayList<String>(Arrays.asList(roleName)); } private TenantRegistrationConfig getTenantSignUpConfig(int tenantId) throws IdentityException { TenantRegistrationConfig config; NodeList nodes; try { // start tenant flow to load tenant registry PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId, true); PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); Registry registry = (Registry) PrivilegedCarbonContext.getThreadLocalCarbonContext() .getRegistry(RegistryType.SYSTEM_GOVERNANCE); if (registry.resourceExists(SelfRegistrationConstants.SIGN_UP_CONFIG_REG_PATH)) { Resource resource = registry.get(SelfRegistrationConstants.SIGN_UP_CONFIG_REG_PATH); // build config from tenant registry resource DocumentBuilder builder = getSecuredDocumentBuilder(); String configXml = new String((byte[]) resource.getContent()); InputSource configInputSource = new InputSource(); configInputSource.setCharacterStream(new StringReader(configXml.trim())); Document doc = builder.parse(configInputSource); nodes = doc.getElementsByTagName(SelfRegistrationConstants.SELF_SIGN_UP_ELEMENT); if (nodes.getLength() > 0) { config = new TenantRegistrationConfig(); config.setSignUpDomain(((Element) nodes.item(0)).getElementsByTagName(SelfRegistrationConstants .SIGN_UP_DOMAIN_ELEMENT) .item(0).getTextContent()); // there can be more than one <SignUpRole> elements, iterate through all elements NodeList rolesEl = ((Element) nodes.item(0)) .getElementsByTagName(SelfRegistrationConstants.SIGN_UP_ROLE_ELEMENT); for (int i = 0; i < rolesEl.getLength(); i++) { Element tmpEl = (Element) rolesEl.item(i); String tmpRole = tmpEl.getElementsByTagName(SelfRegistrationConstants.ROLE_NAME_ELEMENT) .item(0).getTextContent(); boolean tmpIsExternal = Boolean.parseBoolean(tmpEl.getElementsByTagName( SelfRegistrationConstants.IS_EXTERNAL_ELEMENT).item(0).getTextContent()); config.getRoles().put(tmpRole, tmpIsExternal); } return config; } else { return null; } } } catch (RegistryException e) { throw IdentityException.error("Error retrieving sign up config from registry " + e.getMessage(), e); } catch (ParserConfigurationException e) { throw IdentityException.error("Error parsing tenant sign up configuration " + e.getMessage(), e); } catch (SAXException e) { throw IdentityException.error("Error parsing tenant sign up configuration " + e.getMessage(), e); } catch (IOException e) { throw IdentityException.error("Error parsing tenant sign up configuration " + e.getMessage(), e); } finally { PrivilegedCarbonContext.endTenantFlow(); } return null; } /** * * This method provides a secured document builder which will secure XXE attacks. * * @return DocumentBuilder * @throws ParserConfigurationException */ private DocumentBuilder getSecuredDocumentBuilder() throws ParserConfigurationException { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); documentBuilderFactory.setExpandEntityReferences(false); documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); documentBuilderFactory.setFeature(EXTERNAL_GENERAL_ENTITIES_URI, false); SecurityManager securityManager = new SecurityManager(); securityManager.setEntityExpansionLimit(ENTITY_EXPANSION_LIMIT); documentBuilderFactory.setAttribute(SECURITY_MANAGER_PROPERTY, securityManager); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); documentBuilder.setEntityResolver(new CarbonEntityResolver()); return documentBuilder; } }