/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2007 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: IdentityServicesImpl.java,v 1.20 2010/01/06 19:11:17 veiming Exp $ * * Portions Copyrighted 2010-2016 ForgeRock AS. */ package com.sun.identity.idsvcs.opensso; import java.net.MalformedURLException; import java.security.AccessController; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.forgerock.guice.core.InjectorHolder; import org.forgerock.json.resource.BadRequestException; import org.forgerock.json.resource.ConflictException; import org.forgerock.json.resource.ForbiddenException; import org.forgerock.json.resource.InternalServerErrorException; import org.forgerock.json.resource.NotFoundException; import org.forgerock.json.resource.ResourceException; import org.forgerock.openam.errors.ExceptionMappingHandler; import org.forgerock.openam.errors.IdentityResourceExceptionMappingHandler; import org.forgerock.openam.errors.IdentityServicesExceptionMappingHandler; import org.forgerock.openam.ldap.LDAPConstants; import org.forgerock.openam.ldap.LDAPUtils; import org.forgerock.openam.utils.CrestQuery; import org.forgerock.openam.utils.StringUtils; import org.forgerock.util.Reject; import com.iplanet.am.util.SystemProperties; import com.iplanet.sso.SSOException; import com.iplanet.sso.SSOToken; import com.iplanet.sso.SSOTokenManager; import com.sun.identity.common.configuration.AgentConfiguration; import com.sun.identity.common.configuration.ConfigurationException; import com.sun.identity.idm.AMIdentity; import com.sun.identity.idm.AMIdentityRepository; import com.sun.identity.idm.IdConstants; import com.sun.identity.idm.IdOperation; import com.sun.identity.idm.IdRepoDuplicateObjectException; import com.sun.identity.idm.IdRepoErrorCode; import com.sun.identity.idm.IdRepoException; import com.sun.identity.idm.IdSearchControl; import com.sun.identity.idm.IdSearchOpModifier; import com.sun.identity.idm.IdSearchResults; import com.sun.identity.idm.IdType; import com.sun.identity.idm.IdUtils; import com.sun.identity.idsvcs.AccessDenied; import com.sun.identity.idsvcs.Attribute; import com.sun.identity.idsvcs.GeneralFailure; import com.sun.identity.idsvcs.IdServicesException; import com.sun.identity.idsvcs.IdentityDetails; import com.sun.identity.idsvcs.ListWrapper; import com.sun.identity.idsvcs.LogResponse; import com.sun.identity.idsvcs.NeedMoreCredentials; import com.sun.identity.idsvcs.ObjectNotFound; import com.sun.identity.idsvcs.Token; import com.sun.identity.idsvcs.TokenExpired; import com.sun.identity.idsvcs.UserDetails; import com.sun.identity.log.AMLogException; import com.sun.identity.log.LogRecord; import com.sun.identity.log.Logger; import com.sun.identity.security.AdminTokenAction; import com.sun.identity.shared.Constants; import com.sun.identity.shared.debug.Debug; import com.sun.identity.sm.SMSException; /** * Web Service to provide security based on authentication and authorization support. */ public class IdentityServicesImpl implements com.sun.identity.idsvcs.IdentityServicesImpl { private static final String AGENT_TYPE_LOWER_CASE = IdConstants.AGENT_TYPE.toLowerCase(); private final ExceptionMappingHandler idServicesErrorHandler = InjectorHolder.getInstance(IdentityServicesExceptionMappingHandler.class); private static final IdentityResourceExceptionMappingHandler RESOURCE_MAPPING_HANDLER = InjectorHolder.getInstance(IdentityResourceExceptionMappingHandler.class); private static Debug debug = Debug.getInstance("amIdentityServices"); /** * Creates a new {@code AMIdentity} in the identity repository with the * details specified in {@code identity}. * * @param identity The identity details. * @param admin The admin token. * @throws ResourceException If a problem occurs. */ public void create(IdentityDetails identity, SSOToken admin) throws ResourceException { Reject.ifNull(identity, admin); // Obtain identity details & verify String idName = identity.getName(); String idType = identity.getType(); String realm = identity.getRealm(); if (StringUtils.isEmpty(idName)) { // TODO: add a message to the exception throw new BadRequestException("Identity name not provided"); } if (StringUtils.isEmpty(idType)) { idType = "user"; } if (realm == null) { realm = "/"; } try { // Obtain IdRepo to create validate IdType & operations IdType objectIdType = getIdType(idType); AMIdentityRepository repo = getRepo(admin, realm); if (!isOperationSupported(repo, objectIdType, IdOperation.CREATE)) { // TODO: add message to exception throw new UnsupportedOperationException("Unsupported: Type: " + idType + " Operation: CREATE"); } // Obtain creation attributes Map> idAttrs = asMap(identity.getAttributes()); // Create the identity, special case of Agents to merge // and validate the attributes AMIdentity amIdentity; if (isTypeAgent(objectIdType)) { createAgent(idAttrs, objectIdType, idType, idName, realm, admin); } else { // Create other identites like User, Group, Role, etc. amIdentity = repo.createIdentity(objectIdType, idName, idAttrs); // Process roles, groups & memberships if (IdType.USER.equals(objectIdType)) { Set roles = asSet(identity.getRoleList()); if (roles != null && !roles.isEmpty()) { if (!isOperationSupported(repo, IdType.ROLE, IdOperation.EDIT)) { // TODO: localize message throw new UnsupportedOperationException("Unsupported: Type: " + IdType.ROLE + " Operation: EDIT"); } for (String roleName : roles) { AMIdentity role = fetchAMIdentity(repo, IdType.ROLE, roleName, false); if (role != null) { role.addMember(amIdentity); role.store(); } } } Set groups = asSet(identity.getGroupList()); if (groups != null && !groups.isEmpty()) { if (!isOperationSupported(repo, IdType.GROUP, IdOperation.EDIT)) { // TODO: localize message throw new UnsupportedOperationException("Unsupported: Type: " + IdType.GROUP + " Operation: EDIT"); } for (String groupName : groups) { AMIdentity group = fetchAMIdentity(repo, IdType.GROUP, groupName, false); if (group != null) { group.addMember(amIdentity); group.store(); } } } } if (IdType.GROUP.equals(objectIdType) || IdType.ROLE.equals(objectIdType)) { Set members = asSet(identity.getMemberList()); if (members != null) { if (IdType.GROUP.equals(objectIdType) && !isOperationSupported(repo, IdType.GROUP, IdOperation.EDIT)) { throw new ForbiddenException("Token is not authorized"); } if (IdType.ROLE.equals(objectIdType) && !isOperationSupported(repo, IdType.ROLE, IdOperation.EDIT)) { throw new ForbiddenException("Token is not authorized"); } for (String memberName : members) { AMIdentity user = fetchAMIdentity(repo, IdType.USER, memberName, false); if (user != null) { amIdentity.addMember(user); } } amIdentity.store(); } } } } catch (IdRepoDuplicateObjectException ex) { throw new ConflictException("Resource already exists", ex); } catch (IdRepoException e) { debug.error("IdentityServicesImpl:create", e); if (IdRepoErrorCode.ACCESS_DENIED.equals(e.getErrorCode())) { throw new ForbiddenException(e.getMessage()); } else if (IdRepoErrorCode.IDENTITY_ATTRIBUTE_INVALID.equals(e.getErrorCode())) { debug.error(e.getMessage(), e); throw new BadRequestException(e.getMessage()); } else if (e.getLdapErrorIntCode() == LDAPConstants.LDAP_CONSTRAINT_VIOLATION) { debug.error(e.getMessage(), e); throw new BadRequestException(); } else { throw new NotFoundException(e.getMessage()); } } catch (SSOException | SMSException | ConfigurationException | MalformedURLException | UnsupportedOperationException e) { debug.error("IdentityServicesImpl:create", e); throw new NotFoundException(e.getMessage()); } catch (ObjectNotFound e) { debug.error("IdentityServicesImpl:create", e); throw new NotFoundException(e.getMessage()); } } /** * Updates an {@code AMIdentity} in the identity repository with the * details specified in {@code identity}. * * @param identity The updated identity details. * @param admin The admin token. * @throws ResourceException If a problem occurs. */ public void update(IdentityDetails identity, SSOToken admin) throws ResourceException { String idName = identity.getName(); String idType = identity.getType(); String realm = identity.getRealm(); if (StringUtils.isEmpty(idName)) { // TODO: add a message to the exception throw new BadRequestException(""); } if (StringUtils.isEmpty(idType)) { idType="user"; } if (realm == null) { realm = ""; } try { IdType objectIdType = getIdType(idType); AMIdentityRepository repo = getRepo(admin, realm); if (!isOperationSupported(repo, objectIdType, IdOperation.EDIT)) { // TODO: add message to exception throw new ForbiddenException(""); } AMIdentity amIdentity = getAMIdentity(admin, repo, idType, idName); if (amIdentity == null) { String msg = "Object \'" + idName + "\' of type \'" + idType + "\' not found.'"; throw new NotFoundException(msg); } if (isSpecialUser(amIdentity)) { throw new ForbiddenException("Cannot update attributes for this user."); } Map> attrs = asMap(identity.getAttributes()); if (attrs != null && !attrs.isEmpty()) { Map> idAttrs = new HashMap<>(); Set removeAttrs = new HashSet<>(); for (Map.Entry> entry : attrs.entrySet()) { String attrName = entry.getKey(); Set attrValues = entry.getValue(); if (attrValues != null && !attrValues.isEmpty()) { // attribute to add or modify idAttrs.put(attrName, attrValues); } else { // attribute to remove removeAttrs.add(attrName); } } boolean storeNeeded = false; if (!idAttrs.isEmpty()) { amIdentity.setAttributes(idAttrs); storeNeeded = true; } if (!removeAttrs.isEmpty()) { amIdentity.removeAttributes(removeAttrs); storeNeeded = true; } if (storeNeeded) { amIdentity.store(); } } if (IdType.USER.equals(objectIdType)) { Set roles = asSet(identity.getRoleList()); if (!roles.isEmpty()) { setMemberships(repo, amIdentity, roles, IdType.ROLE); } Set groups = asSet(identity.getGroupList()); if (!groups.isEmpty()) { setMemberships(repo, amIdentity, groups, IdType.GROUP); } } if (IdType.GROUP.equals(objectIdType) || IdType.ROLE.equals(objectIdType)) { Set members = asSet(identity.getMemberList()); if (!members.isEmpty()) { setMembers(repo, amIdentity, members, IdType.USER); } } } catch (IdRepoException ex) { debug.error("IdentityServicesImpl:update", ex); if (IdRepoErrorCode.LDAP_EXCEPTION.equals(ex.getErrorCode())) { throw new InternalServerErrorException(ex.getConstraintViolationDetails()); } else if (LDAPConstants.LDAP_INVALID_SYNTAX.equals(ex.getLDAPErrorCode())) { throw new BadRequestException("Unrecognized or invalid syntax for an attribute."); } else if (IdRepoErrorCode.ILLEGAL_ARGUMENTS.equals(ex.getErrorCode())) { throw new BadRequestException(ex); } throw RESOURCE_MAPPING_HANDLER.handleError(ex); } catch (SSOException ex) { debug.error("IdentityServicesImpl:update", ex); throw new BadRequestException(ex.getMessage()); } catch (ObjectNotFound e) { debug.error("IdentityServicesImpl:update", e); throw new NotFoundException(e.getMessage()); } } /** * Deletes an {@code AMIdentity} from the identity repository that match * the details specified in {@code identity}. * * @param identity The identity to delete. * @param admin The admin token. * @throws ResourceException If a problem occurs. */ public void delete(IdentityDetails identity, SSOToken admin) throws ResourceException { if (identity == null) { throw new BadRequestException("delete failed: identity object not specified."); } String name = identity.getName(); String identityType = identity.getType(); String realm = identity.getRealm(); if (name == null) { throw new NotFoundException("delete failed: null object name."); } if (realm == null) { realm = "/"; } try { AMIdentity amIdentity = getAMIdentity(admin, identityType, name, realm); if (amIdentity != null) { if (isSpecialUser(amIdentity)) { throw new ForbiddenException("Cannot delete user."); } AMIdentityRepository repo = getRepo(admin, realm); IdType idType = amIdentity.getType(); if (IdType.GROUP.equals(idType) || IdType.ROLE.equals(idType)) { // First remove users from memberships Set members = getMembers(amIdentity, IdType.USER); for (AMIdentity member : members) { try { removeMember(repo, amIdentity, member); } catch (IdRepoException ex) { //ignore this, member maybe already removed. } } } deleteAMIdentity(repo, amIdentity); } else { String msg = "Object \'" + name + "\' of type \'" + identityType + "\' was not found."; throw new NotFoundException(msg); } } catch (IdRepoException ex) { debug.error("IdentityServicesImpl:delete", ex); throw RESOURCE_MAPPING_HANDLER.handleError(ex); } catch (SSOException ex) { debug.error("IdentityServicesImpl:delete", ex); throw new BadRequestException(ex.getMessage()); } catch (ObjectNotFound e) { debug.error("IdentityServicesImpl:delete", e); throw new NotFoundException(e.getMessage()); } } /** * Searches the identity repository to find all identities that match the search criteria. * * @param crestQuery A CREST Query object which will contain either a _queryId or a _queryFilter. * @param searchModifiers The search modifiers * @param admin Your SSO token. * @return a list of matching identifiers. * @throws ResourceException */ public List search(CrestQuery crestQuery, Map> searchModifiers, SSOToken admin) throws ResourceException { List rv = new ArrayList<>(); try { String realm = "/"; String objectType = "User"; if (searchModifiers != null) { realm = attractValues("realm", searchModifiers, "/"); objectType = attractValues("objecttype", searchModifiers, "User"); } AMIdentityRepository repo = getRepo(admin, realm); IdType idType = getIdType(objectType); if (idType != null) { List objList = fetchAMIdentities(idType, crestQuery, false, repo, searchModifiers); if (objList != null && !objList.isEmpty()) { List names = getNames(realm, idType, objList); if (!names.isEmpty()) { for (String name : names) { rv.add(name); } } } } else { debug.error("IdentityServicesImpl:search unsupported IdType" + objectType); throw new BadRequestException("search unsupported IdType: " + objectType); } } catch (IdRepoException e) { debug.error("IdentityServicesImpl:search", e); throw new InternalServerErrorException(e.getMessage()); } catch (SSOException e) { debug.error("IdentityServicesImpl:search", e); throw new InternalServerErrorException(e.getMessage()); } catch (ObjectNotFound e) { debug.error("IdentityServicesImpl:search", e); throw new NotFoundException(e.getMessage()); } return rv; } /** * Searches the identity repository to find all identities that match the search criteria and returns them as a * list of identities. * * @param crestQuery A CREST Query object which will contain either a _queryId or a _queryFilter. * @param searchModifiers The search modifiers * @param admin Your SSO token. * @return a list of matching identities. * @throws ResourceException */ public List searchIdentityDetails(CrestQuery crestQuery, Map> searchModifiers, SSOToken admin) throws ResourceException { try { String realm = "/"; String objectType = "User"; if (searchModifiers != null) { realm = attractValues("realm", searchModifiers, "/"); objectType = attractValues("objecttype", searchModifiers, "User"); } AMIdentityRepository repo = getRepo(admin, realm); IdType idType = getIdType(objectType); if (idType != null) { List identities = fetchAMIdentities(idType, crestQuery, true, repo, searchModifiers); List result = new ArrayList<>(); for (AMIdentity identity : identities) { result.add(convertToIdentityDetails(identity, null)); } return result; } debug.error("IdentityServicesImpl.searchIdentities unsupported IdType " + objectType); throw new BadRequestException("searchIdentities: unsupported IdType " + objectType); } catch (IdRepoException e) { debug.error("IdentityServicesImpl.searchIdentities", e); throw new InternalServerErrorException(e.getMessage()); } catch (SSOException e) { debug.error("IdentityServicesImpl.searchIdentities", e); throw new InternalServerErrorException(e.getMessage()); } catch (ObjectNotFound e) { debug.error("IdentityServicesImpl.searchIdentities", e); throw new NotFoundException(e.getMessage()); } } @Override public LogResponse log(Token app, Token subject, String logName, String message) throws AccessDenied, TokenExpired, GeneralFailure { if (app == null) { throw new AccessDenied("No logging application token specified"); } SSOToken appToken; SSOToken subjectToken; appToken = getSSOToken(app); subjectToken = subject == null ? appToken : getSSOToken(subject); try { LogRecord logRecord = new LogRecord(java.util.logging.Level.INFO, message, subjectToken); //TODO Support internationalization via a resource bundle specification Logger logger = (Logger) Logger.getLogger(logName); logger.log(logRecord, appToken); logger.flush(); } catch (AMLogException e) { debug.error("IdentityServicesImpl:log", e); throw new GeneralFailure(e.getMessage()); } return new LogResponse(); } @Override public UserDetails attributes(String[] attributeNames, Token subject, Boolean refresh) throws TokenExpired, GeneralFailure, AccessDenied { List attrNames = null; if (attributeNames != null && attributeNames.length > 0) { attrNames = new ArrayList<>(); attrNames.addAll(Arrays.asList(attributeNames)); } return attributes(attrNames, subject, refresh); } private UserDetails attributes(List attributeNames, Token subject, Boolean refresh) throws TokenExpired, GeneralFailure, AccessDenied { UserDetails details = new UserDetails(); try { SSOToken ssoToken = getSSOToken(subject); if (refresh != null && refresh) { SSOTokenManager.getInstance().refreshSession(ssoToken); } Map> sessionAttributes = new HashMap<>(); Set s; if (attributeNames != null) { String propertyNext; for (String attrNext : attributeNames) { s = new HashSet<>(); if (attrNext.equalsIgnoreCase("idletime")) { s.add(Long.toString(ssoToken.getIdleTime())); } else if (attrNext.equalsIgnoreCase("timeleft")) { s.add(Long.toString(ssoToken.getTimeLeft())); } else if (attrNext.equalsIgnoreCase("maxsessiontime")) { s.add(Long.toString(ssoToken.getMaxSessionTime())); } else if (attrNext.equalsIgnoreCase("maxidletime")) { s.add(Long.toString(ssoToken.getMaxIdleTime())); } else { propertyNext = ssoToken.getProperty(attrNext); if (propertyNext != null && !propertyNext.isEmpty()) { s.add(propertyNext); } } if (!s.isEmpty()) { sessionAttributes.put(attrNext, s); } } } // Obtain user memberships (roles and groups) AMIdentity userIdentity = IdUtils.getIdentity(ssoToken); if (isSpecialUser(userIdentity)) { throw new AccessDenied( "Cannot retrieve attributes for this user."); } // Determine the types that can have members SSOToken adminToken = AccessController.doPrivileged(AdminTokenAction.getInstance()); AMIdentityRepository idrepo = new AMIdentityRepository(userIdentity.getRealm(), adminToken); Set supportedTypes = idrepo.getSupportedIdTypes(); Set membersTypes = new HashSet<>(); for (IdType type : supportedTypes) { if (type.canHaveMembers().contains(userIdentity.getType())) { membersTypes.add(type); } } // Determine the roles and groups List roles = new ArrayList<>(); for (IdType type : membersTypes) { try { Set memberships = userIdentity.getMemberships(type); for (AMIdentity membership : memberships) { roles.add(membership.getUniversalId()); } } catch (IdRepoException ire) { debug.message("IdentityServicesImpl:attributes", ire); // Ignore and continue } } String[] r = new String[roles.size()]; details.setRoles(roles.toArray(r)); Map> userAttributes; if (attributeNames != null) { Set attrNames = new HashSet<>(attributeNames); userAttributes = userIdentity.getAttributes(attrNames); } else { userAttributes = userIdentity.getAttributes(); } if (userAttributes != null) { for (Map.Entry> entry : sessionAttributes.entrySet()) { if (userAttributes.keySet().contains(entry.getKey())) { userAttributes.get(entry.getKey()).addAll(entry.getValue()); } else { userAttributes.put(entry.getKey(), entry.getValue()); } } } else { userAttributes = sessionAttributes; } List attributes = new ArrayList<>(userAttributes.size()); for (String name : userAttributes.keySet()) { Attribute attribute = new Attribute(); attribute.setName(name); Set value = userAttributes.get(name); if (value != null && !value.isEmpty()) { List valueList = new ArrayList<>(value.size()); // Convert the set to a List of String for (String next : value) { if (next != null) { valueList.add(next); } } String[] v = new String[valueList.size()]; attribute.setValues(valueList.toArray(v)); attributes.add(attribute); } } Attribute[] a = new Attribute[attributes.size()]; details.setAttributes(attributes.toArray(a)); } catch (IdRepoException e) { debug.error("IdentityServicesImpl:attributes", e); throw new GeneralFailure(e.getMessage()); } catch (SSOException e) { debug.error("IdentityServicesImpl:attributes", e); throw new GeneralFailure(e.getMessage()); } catch (TokenExpired e) { debug.warning("IdentityServicesImpl:attributes original error", e); throw new TokenExpired("Cannot retrieve Token."); } //TODO handle token translation details.setToken(subject); return details; } @Override public IdentityDetails read(String name, Attribute[] attributes, Token admin) throws IdServicesException { List attrList = null; if (name == null || name.isEmpty() || name.equals("null")) { debug.error("IdentityServicesImpl:read identity not found"); throw new ObjectNotFound(name); } if (attributes != null && attributes.length > 0) { attrList = new ArrayList<>(); attrList.addAll(Arrays.asList(attributes)); } return read(name, attrList, getSSOToken(admin)); } private IdentityDetails read(String name, List attributes, SSOToken admin) throws IdServicesException { return read(name, asMap(attributes), admin); } public IdentityDetails read(String name, Map> attributes, SSOToken admin) throws IdServicesException { IdentityDetails rv = null; String realm = null; String repoRealm; String identityType = null; List attrsToGet = null; if (attributes != null) { for (Attribute attr : asAttributeArray(attributes)) { String attrName = attr.getName(); if ("realm".equalsIgnoreCase(attrName)) { String[] values = attr.getValues(); if (values != null && values.length > 0) { realm = values[0]; } } else if ("objecttype".equalsIgnoreCase(attrName)) { String[] values = attr.getValues(); if (values != null && values.length > 0) { identityType = values[0]; } } else { if (attrsToGet == null) { attrsToGet = new ArrayList<>(); } attrsToGet.add(attrName); } } } if (StringUtils.isEmpty(realm)) { repoRealm = "/"; } else { repoRealm = realm; } if (StringUtils.isEmpty(identityType)) { identityType = "User"; } try { AMIdentity amIdentity = getAMIdentity(admin, identityType, name, repoRealm); if (amIdentity == null) { debug.error("IdentityServicesImpl:read identity not found"); throw new ObjectNotFound(name); } if (isSpecialUser(amIdentity)) { throw new AccessDenied("Cannot retrieve attributes for this user."); } rv = convertToIdentityDetails(amIdentity, attrsToGet); if (!StringUtils.isEmpty(realm)) { // use the realm specified by the request rv.setRealm(realm); } } catch (IdRepoException e) { debug.error("IdentityServicesImpl:read", e); mapIdRepoException(e); } catch (SSOException e) { debug.error("IdentityServicesImpl:read", e); throw new GeneralFailure(e.getMessage()); } return rv; } @Override public String getCookieNameForToken() throws GeneralFailure { return SystemProperties.get(Constants.AM_COOKIE_NAME, "iPlanetDirectoryPro"); } @Override public String[] getCookieNamesToForward() throws GeneralFailure { String[] cookies; String ssoTokenCookie = getCookieNameForToken(); String lbCookieName = SystemProperties.get( Constants.AM_LB_COOKIE_NAME); if (lbCookieName == null) { cookies = new String[1]; } else { cookies = new String[2]; cookies[1] = lbCookieName; } cookies[0] = ssoTokenCookie; return cookies; } private boolean isTypeAgent(IdType objectIdType) { return objectIdType.equals(IdType.AGENT) || objectIdType.equals(IdType.AGENTONLY) || objectIdType.equals(IdType.AGENTGROUP); } /** * To be backward compatible, look for 'AgentType' attribute * in the attribute map which is passed as a parameter and if * not present/sent, check if the IdType.AGENTONLY or AGENT * and then assume that it is '2.2_Agent' type to create * that agent under the 2.2_Agent node. **/ private void createAgent(Map> idAttrs, IdType objectIdType, String idType, String idName, String realm, SSOToken adminToken) throws SMSException, SSOException, ConfigurationException, IdRepoException, MalformedURLException { String agentType; String serverUrl = null; String agentUrl = null; final String SERVER_URL = "serverurl"; final String AGENT_URL = "agenturl"; final String DEFAULT_AGENT_TYPE = "2.2_Agent"; Set set = idAttrs.remove(IdConstants.AGENT_TYPE); if (set == null) { set = idAttrs.remove(AGENT_TYPE_LOWER_CASE); } if (set != null && !set.isEmpty()) { agentType = set.iterator().next(); } else if (objectIdType.equals(IdType.AGENTONLY) || objectIdType.equals(IdType.AGENT)) { agentType = DEFAULT_AGENT_TYPE; } else { throw new UnsupportedOperationException("Unsupported: Agent Type required for " + idType); } set = idAttrs.remove(SERVER_URL); if (set != null && !set.isEmpty()) { serverUrl = set.iterator().next(); } set = idAttrs.remove(AGENT_URL); if (set != null && !set.isEmpty()) { agentUrl = set.iterator().next(); } if (agentType.equals(AgentConfiguration.AGENT_TYPE_WEB) || agentType.equals(AgentConfiguration.AGENT_TYPE_J2EE)) { if (StringUtils.isBlank(agentUrl)) { throw new MalformedURLException("Agent type requires agenturl to be configured."); } else if (StringUtils.isBlank(serverUrl)) { throw new MalformedURLException("Agent type requires serverurl to be configured."); } } if (objectIdType.equals(IdType.AGENT) || objectIdType.equals(IdType.AGENTONLY)) { if (StringUtils.isBlank(serverUrl) || StringUtils.isBlank(agentUrl)) { AgentConfiguration.createAgent(adminToken, realm, idName, agentType, idAttrs); } else { AgentConfiguration.createAgent(adminToken, realm, idName, agentType, idAttrs, serverUrl, agentUrl); } } else { if (StringUtils.isBlank(serverUrl) || StringUtils.isBlank(agentUrl)) { AgentConfiguration.createAgentGroup(adminToken, realm, idName, agentType, idAttrs); } else { AgentConfiguration.createAgentGroup(adminToken, realm, idName, agentType, idAttrs, serverUrl, agentUrl); } } } private boolean isSpecialUser(AMIdentity identity) { Set specialUsers = getSpecialUsers(identity.getRealm()); return specialUsers != null && specialUsers.contains(identity); } private Set getSpecialUsers(String realmName) { SSOToken adminToken = AccessController.doPrivileged(AdminTokenAction.getInstance()); try { AMIdentityRepository repo = new AMIdentityRepository(realmName, adminToken); IdSearchResults results = repo.getSpecialIdentities(IdType.USER); return results.getSearchResults(); } catch (IdRepoException | SSOException e) { debug.warning("AMModelBase.getSpecialUsers", e); } return Collections.emptySet(); } private Set getSpecialUserNames(String realmName) { Set identities = new HashSet(); Set set = getSpecialUsers(realmName); if (set != null) { for (AMIdentity amid : set) { identities.add(amid.getName()); } } return identities; } /** * Maps a IdRepoException to appropriate exception. * * @param exception IdRepoException that needs to be mapped * @return boolean true if the identity object was deleted. * @throws NeedMoreCredentials when more credentials are required for * authorization. * @throws ObjectNotFound if no subject is found that matches the input criteria. * @throws TokenExpired when subject's token has expired. * @throws AccessDenied when permission to preform action is denied * @throws GeneralFailure on other errors. */ private void mapIdRepoException(IdRepoException exception) throws IdServicesException { throw idServicesErrorHandler.handleError(exception); } private AMIdentityRepository getRepo(SSOToken token, String realm) { if (StringUtils.isEmpty(realm)) { realm = "/"; } return new AMIdentityRepository(realm, token); } private IdType getIdType(String objectType) { try { return IdUtils.getType(objectType); } catch (IdRepoException ioe) { // Ignore exception } return null; } private List getNames(String realm, IdType idType, List objList) throws SSOException, IdRepoException { List names = new ArrayList(); if (objList != null && !objList.isEmpty()) { for (AMIdentity identity : objList) { if (identity != null) { names.add(identity.getName()); } } } if (idType.equals(IdType.USER)) { Set specialUserNames = getSpecialUserNames(realm); names.removeAll(specialUserNames); } return names; } private boolean isOperationSupported(AMIdentityRepository repo, IdType idType, IdOperation operation) { try { Set ops = repo.getAllowedIdOperations(idType); if (ops != null) { return ops.contains(operation); } } catch (IdRepoException | SSOException ex) { // Ignore } return false; } private Set getMembers(AMIdentity amIdentity, IdType type) throws IdRepoException, SSOException { return amIdentity.getMembers(type); } private Set getMemberNames(AMIdentity amIdentity, IdType type) throws IdRepoException, SSOException { Set rv = null; Set members = getMembers(amIdentity, type); if (members != null) { rv = new HashSet<>(); for (AMIdentity member : members) { rv.add(member.getName()); } } return rv; } private AMIdentity fetchAMIdentity(AMIdentityRepository repo, IdType type, String identity, boolean fetchAllAttrs) throws IdRepoException, ObjectNotFound, SSOException { AMIdentity rv = null; List identities = fetchAMIdentities(type, new CrestQuery(identity), fetchAllAttrs, repo, null); if (identities != null && !identities.isEmpty()) { rv = identities.get(0); } return rv; } private List fetchAMIdentities(IdType type, CrestQuery crestQuery, boolean fetchAllAttrs, AMIdentityRepository repo, Map searchModifiers) throws IdRepoException, ObjectNotFound, SSOException { IdSearchControl searchControl = new IdSearchControl(); IdSearchResults searchResults; List identities; if (isOperationSupported(repo, type, IdOperation.READ)) { Set resultSet; if (fetchAllAttrs) { searchControl.setAllReturnAttributes(true); } else { searchControl.setAllReturnAttributes(false); } if (searchModifiers != null) { searchControl.setSearchModifiers(IdSearchOpModifier.AND, searchModifiers); } searchResults = repo.searchIdentities(type, crestQuery, searchControl); resultSet = searchResults.getSearchResults(); identities = new ArrayList<>(resultSet); } else { // A list is expected back /* * TODO: throw an exception instead of returning an empty list */ identities = new ArrayList<>(); } return identities; } private AMIdentity getAMIdentity(SSOToken ssoToken, AMIdentityRepository repo, String guid, IdType idType) throws IdRepoException, SSOException { try { if (LDAPUtils.isDN(guid)) { return new AMIdentity(ssoToken, guid); } else { return new AMIdentity(ssoToken, guid, idType, repo.getRealmIdentity().getRealm(), null); } } catch (IdRepoException ex) { String errCode = ex.getErrorCode(); // If it is error code 215, ignore the error as this indicates // an invalid uid. if (!IdRepoErrorCode.ILLEGAL_UNIVERSAL_IDENTIFIER.equals(errCode)) { throw ex; } } return null; } private String attractValues(String name, Map> map, String defaultValue) { Set set = map.get(name); if (set != null && !set.isEmpty()) { String value = set.iterator().next().trim(); map.remove(name); return value.isEmpty() ? defaultValue : value; } else { return defaultValue; } } private AMIdentity getAMIdentity(SSOToken ssoToken, String objectType, String id, String realm) throws IdRepoException, SSOException, ObjectNotFound { AMIdentityRepository repo = getRepo(ssoToken, realm); return getAMIdentity(ssoToken, repo, objectType, id); } private AMIdentity getAMIdentity(SSOToken ssoToken, AMIdentityRepository repo, String objectType, String id) throws IdRepoException, SSOException, ObjectNotFound { AMIdentity rv = null; IdType idType = null; if (objectType != null) { idType = getIdType(objectType); } if (idType != null) { // First assume id is a universal id rv = getAMIdentity(ssoToken, repo, id, idType); if (rv == null) { // Not found through id lookup, try name lookup rv = fetchAMIdentity(repo, idType, id, true); } } return rv; } private void addMember(AMIdentityRepository repo, AMIdentity amIdentity, AMIdentity member) throws IdRepoException, SSOException, ForbiddenException { if (!member.isMember(amIdentity)) { if (isOperationSupported(repo, amIdentity.getType(), IdOperation.EDIT)) { amIdentity.addMember(member); } else { // TODO: Add message to exception throw new ForbiddenException(""); } } } private void removeMember(AMIdentityRepository repo, AMIdentity amIdentity, AMIdentity member) throws IdRepoException, SSOException, ForbiddenException { if (member.isMember(amIdentity)) { IdType type = amIdentity.getType(); if (isOperationSupported(repo, type, IdOperation.EDIT)) { amIdentity.removeMember(member); } else { // TODO: Add message to exception throw new ForbiddenException(""); } } } private void deleteAMIdentity(AMIdentityRepository repo, AMIdentity amIdentity) throws IdRepoException, SSOException, ForbiddenException { Set identities = new HashSet<>(); identities.add(amIdentity); deleteAMIdentities(repo, amIdentity.getType(), identities); } private void deleteAMIdentities(AMIdentityRepository repo, IdType type, Set identities) throws IdRepoException, SSOException, ForbiddenException { if (isOperationSupported(repo, type, IdOperation.DELETE)) { repo.deleteIdentities(identities); } else { // TODO: add message to exception throw new ForbiddenException(""); } } private void setMembers(AMIdentityRepository repo, AMIdentity amIdentity, Set members, IdType idType) throws IdRepoException, SSOException, ObjectNotFound, ForbiddenException { Set membershipsToAdd = members; Set membershipsToRemove = null; Set currentMembers = getMemberNames(amIdentity, idType); if ((currentMembers != null) && (currentMembers.size() > 0)) { membershipsToRemove = removeAllIgnoreCase(currentMembers, members); membershipsToAdd = removeAllIgnoreCase(members, currentMembers); } if (membershipsToRemove != null) { for (String memberName : membershipsToRemove) { AMIdentity identity = fetchAMIdentity(repo, idType, memberName, false); if (identity != null) { removeMember(repo, amIdentity, identity); } } } if (membershipsToAdd != null) { for (String memberName : membershipsToAdd) { AMIdentity identity = fetchAMIdentity(repo, idType, memberName, false); if (identity != null) { addMember(repo, amIdentity, identity); } } } } private void setMemberships(AMIdentityRepository repo, AMIdentity amIdentity, Set memberships, IdType idType) throws IdRepoException, SSOException, ObjectNotFound, ForbiddenException { Set membershipsToAdd = memberships; Set membershipsToRemove = null; Set currentMemberships = getMembershipNames(amIdentity, idType); if (currentMemberships != null && !currentMemberships.isEmpty()) { membershipsToRemove = removeAllIgnoreCase(currentMemberships, memberships); membershipsToAdd = removeAllIgnoreCase(memberships, currentMemberships); } if (membershipsToRemove != null) { for (String idName : membershipsToRemove) { AMIdentity container = fetchAMIdentity(repo, idType, idName, false); removeMember(repo, container, amIdentity); } } if (membershipsToAdd != null) { for (String idName : membershipsToAdd) { AMIdentity container = fetchAMIdentity(repo, idType, idName, false); addMember(repo, container, amIdentity); } } } private Set removeAllIgnoreCase(Set src, Set removeSet) { if (src == null || src.isEmpty()) { return new HashSet<>(); } else { Set result = new HashSet<>(src); if (removeSet != null && !removeSet.isEmpty()) { Map upcaseSrc = new HashMap<>(src.size()); for (String s : src) { upcaseSrc.put(s.toUpperCase(), s); } for (String s : removeSet) { s = upcaseSrc.get(s.toUpperCase()); if (s != null) { result.remove(s); } } } return result; } } private Set getMemberships(AMIdentity amIdentity, IdType idType) throws SSOException { try { return amIdentity.getMemberships(idType); } catch (IdRepoException ex) { // This can be thrown if the identity is not a member // in any object of idType. return new HashSet<>(0); } } private Set getMembershipNames(AMIdentity amIdentity, IdType idType) throws SSOException { Set memberships = getMemberships(amIdentity, idType); Set rv = new HashSet<>(memberships.size()); for (AMIdentity membership : memberships) { rv.add(membership.getName()); } return rv; } private IdentityDetails convertToIdentityDetails(AMIdentity amIdentity, List attrList) throws IdRepoException, SSOException { IdentityDetails rv = null; if (amIdentity != null) { IdType idType = amIdentity.getType(); Map> attrs; boolean addUniversalId = false; rv = new IdentityDetails(); rv.setName(amIdentity.getName()); rv.setType(amIdentity.getType().getName()); rv.setRealm(amIdentity.getRealm()); if (IdType.USER.equals(idType)) { Set roles = amIdentity.getMemberships(IdType.ROLE); if (roles != null && !roles.isEmpty()) { AMIdentity[] rolesFound = new AMIdentity[roles.size()]; String[] roleNames = new String[rolesFound.length]; roles.toArray(rolesFound); for (int i = 0; i < rolesFound.length; i++) { roleNames[i] = rolesFound[i].getName(); } rv.setRoleList(new ListWrapper(roleNames)); } Set groups = amIdentity.getMemberships(IdType.GROUP); if ((groups != null) && (groups.size() > 0)) { AMIdentity[] groupsFound = new AMIdentity[groups.size()]; String[] groupNames = new String[groupsFound.length]; groups.toArray(groupsFound); for (int i = 0; i < groupsFound.length; i++) { groupNames[i] = groupsFound[i].getName(); } rv.setGroupList(new ListWrapper(groupNames)); } } if (IdType.GROUP.equals(idType) || IdType.ROLE.equals(idType)) { Set members = amIdentity.getMembers(IdType.USER); if ((members != null) && (members.size() > 0)) { AMIdentity[] membersFound = new AMIdentity[members.size()]; String[] memberNames = new String[membersFound.length]; members.toArray(membersFound); for (int i = 0; i < membersFound.length; i++) { memberNames[i] = membersFound[i].getName(); } rv.setMemberList(new ListWrapper(memberNames)); } } if (attrList != null) { Set attrNames = new HashSet<>(); for (String attrName : attrList) { if ("universalid".equalsIgnoreCase(attrName)) { addUniversalId = true; } else { if (!attrNames.contains(attrName)) { attrNames.add(attrName); } } } attrs = amIdentity.getAttributes(attrNames); } else { attrs = amIdentity.getAttributes(); addUniversalId = true; } if (addUniversalId) { if (attrs == null) { attrs = new HashMap<>(); } Set uidValue = new HashSet<>(); uidValue.add(amIdentity.getUniversalId()); attrs.put("universalid", uidValue); } if (attrs != null) { rv.setAttributes(asAttributeArray(attrs)); } } return rv; } private SSOToken getSSOToken(Token admin) throws TokenExpired { try { if (admin == null) { throw new TokenExpired("Token is NULL"); } SSOTokenManager mgr = SSOTokenManager.getInstance(); return mgr.createSSOToken(admin.getId()); } catch (SSOException ex) { // throw TokenExpired exception throw new TokenExpired(ex.getMessage()); } } private static Map> asMap(List attributes) { Map> map = new HashMap<>(); if (attributes != null) { for (Attribute attribute : attributes) { map.put(attribute.getName(), new HashSet<>(Arrays.asList(attribute.getValues()))); } } return map; } public static Map> asMap(Attribute... attributes) { if (attributes == null) { return new HashMap<>(); } return asMap(Arrays.asList(attributes)); } private static Set asSet(ListWrapper list) { if (list == null) { return new HashSet<>(); } return new HashSet<>(Arrays.asList(list.getElements())); } public static Attribute[] asAttributeArray(Map> attributes) { List attributesArray = new ArrayList<>(); if (attributes != null) { for (Map.Entry> attribute : attributes.entrySet()) { attributesArray.add(new Attribute(attribute.getKey(), attribute.getValue().toArray(new String[0]))); } } return attributesArray.toArray(new Attribute[0]); } /** * Take a map and capture the keys in a list. * @param map The map whose keys you wish to capture. * @return an ArrayList of the keys of the map. */ public static List asListOfKeys(Map> map) { List result = new ArrayList<>(); if (map != null) { for (Map.Entry> entry : map.entrySet()) { result.add(entry.getKey()); } } return result; } }