/* * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.model.impl.security; import com.evolveum.midpoint.common.ActivationComputer; import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.model.api.context.EvaluatedAssignment; import com.evolveum.midpoint.model.api.context.EvaluatedAssignmentTarget; import com.evolveum.midpoint.model.api.util.DeputyUtils; import com.evolveum.midpoint.model.common.SystemObjectCache; import com.evolveum.midpoint.model.common.expression.ItemDeltaItem; import com.evolveum.midpoint.model.common.expression.ObjectDeltaObject; import com.evolveum.midpoint.model.common.mapping.MappingFactory; import com.evolveum.midpoint.model.impl.UserComputer; import com.evolveum.midpoint.model.impl.lens.AssignmentEvaluator; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensContextPlaceholder; import com.evolveum.midpoint.model.impl.lens.LensUtil; import com.evolveum.midpoint.model.impl.lens.projector.MappingEvaluator; import com.evolveum.midpoint.prism.PrismContainerDefinition; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.prism.query.builder.QueryBuilder; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.AdminGuiConfigTypeUtil; import com.evolveum.midpoint.schema.util.ObjectQueryUtil; import com.evolveum.midpoint.schema.util.ObjectResolver; import com.evolveum.midpoint.security.api.Authorization; import com.evolveum.midpoint.security.api.DelegatorWithOtherPrivilegesLimitations; import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.security.api.UserProfileService; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.task.api.TaskManager; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.PolicyViolationException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.ldap.userdetails.UserDetailsContextMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DirContextOperations; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * @author lazyman * @author semancik */ @Service(value = "userDetailsService") public class UserProfileServiceImpl implements UserProfileService, UserDetailsService, UserDetailsContextMapper { private static final Trace LOGGER = TraceManager.getTrace(UserProfileServiceImpl.class); @Autowired(required = true) private transient RepositoryService repositoryService; @Autowired(required = true) private ObjectResolver objectResolver; @Autowired(required = true) private SystemObjectCache systemObjectCache; @Autowired(required = true) private MappingFactory mappingFactory; @Autowired(required = true) private MappingEvaluator mappingEvaluator; @Autowired(required = true) private SecurityHelper securityHelper; @Autowired(required = true) private UserComputer userComputer; @Autowired(required = true) private ActivationComputer activationComputer; @Autowired(required = true) private Clock clock; @Autowired(required = true) private PrismContext prismContext; @Autowired(required = true) private TaskManager taskManager; @Override public MidPointPrincipal getPrincipal(String username) throws ObjectNotFoundException, SchemaException { OperationResult result = new OperationResult(OPERATION_GET_PRINCIPAL); PrismObject<UserType> user = null; try { user = findByUsername(username, result); } catch (ObjectNotFoundException ex) { LOGGER.trace("Couldn't find user with name '{}', reason: {}.", new Object[]{username, ex.getMessage(), ex}); throw ex; } catch (Exception ex) { LOGGER.warn("Error getting user with name '{}', reason: {}.", new Object[]{username, ex.getMessage(), ex}); throw new SystemException(ex.getMessage(), ex); } return createPrincipal(user, result); } @Override public MidPointPrincipal getPrincipal(PrismObject<UserType> user) throws SchemaException { OperationResult result = new OperationResult(OPERATION_GET_PRINCIPAL); return createPrincipal(user, result); } private MidPointPrincipal createPrincipal(PrismObject<UserType> user, OperationResult result) throws SchemaException { if (user == null) { return null; } PrismObject<SystemConfigurationType> systemConfiguration = null; try { systemConfiguration = repositoryService.getObject(SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(), null, result); } catch (ObjectNotFoundException | SchemaException e) { LOGGER.warn("No system configuration: {}", e.getMessage(), e); } userComputer.recompute(user); MidPointPrincipal principal = new MidPointPrincipal(user.asObjectable()); initializePrincipalFromAssignments(principal, systemConfiguration); return principal; } @Override public void updateUser(MidPointPrincipal principal) { OperationResult result = new OperationResult(OPERATION_UPDATE_USER); try { save(principal, result); } catch (Exception ex) { LOGGER.warn("Couldn't save user '{}, ({})', reason: {}.", new Object[]{principal.getFullName(), principal.getOid(), ex.getMessage(), ex}); } } private PrismObject<UserType> findByUsername(String username, OperationResult result) throws SchemaException, ObjectNotFoundException { PolyString usernamePoly = new PolyString(username); ObjectQuery query = ObjectQueryUtil.createNormNameQuery(usernamePoly, prismContext); LOGGER.trace("Looking for user, query:\n" + query.debugDump()); List<PrismObject<UserType>> list = repositoryService.searchObjects(UserType.class, query, null, result); LOGGER.trace("Users found: {}.", (list != null ? list.size() : 0)); if (list == null || list.size() != 1) { return null; } return list.get(0); } private void initializePrincipalFromAssignments(MidPointPrincipal principal, PrismObject<SystemConfigurationType> systemConfiguration) throws SchemaException { UserType userType = principal.getUser(); Collection<Authorization> authorizations = principal.getAuthorities(); List<AdminGuiConfigurationType> adminGuiConfigurations = new ArrayList<>(); Task task = taskManager.createTaskInstance(UserProfileServiceImpl.class.getName() + ".addAuthorizations"); OperationResult result = task.getResult(); principal.setApplicableSecurityPolicy(securityHelper.locateSecurityPolicy(userType.asPrismObject(), systemConfiguration, task, result)); if (!userType.getAssignment().isEmpty()) { LensContext<UserType> lensContext = new LensContextPlaceholder<>(userType.asPrismObject(), prismContext); AssignmentEvaluator.Builder<UserType> builder = new AssignmentEvaluator.Builder<UserType>() .repository(repositoryService) .focusOdo(new ObjectDeltaObject<>(userType.asPrismObject(), null, userType.asPrismObject())) .channel(null) .objectResolver(objectResolver) .systemObjectCache(systemObjectCache) .prismContext(prismContext) .mappingFactory(mappingFactory) .mappingEvaluator(mappingEvaluator) .activationComputer(activationComputer) .now(clock.currentTimeXMLGregorianCalendar()) // We do need only authorizations + gui config. Therefore we not need to evaluate // constructions and the like, so switching it off makes the evaluation run faster. // It also avoids nasty problems with resources being down, // resource schema not available, etc. .loginMode(true) // We do not have real lens context here. But the push methods in ModelExpressionThreadLocalHolder // will need something to push on the stack. So give them context placeholder. .lensContext(lensContext); AssignmentEvaluator<UserType> assignmentEvaluator = builder.build(); for (AssignmentType assignmentType: userType.getAssignment()) { try { ItemDeltaItem<PrismContainerValue<AssignmentType>,PrismContainerDefinition<AssignmentType>> assignmentIdi = new ItemDeltaItem<>(); assignmentIdi.setItemOld(LensUtil.createAssignmentSingleValueContainerClone(assignmentType)); assignmentIdi.recompute(); EvaluatedAssignment<UserType> assignment = assignmentEvaluator.evaluate(assignmentIdi, false, userType, userType.toString(), task, result); if (assignment.isValid()) { authorizations.addAll(assignment.getAuthorizations()); adminGuiConfigurations.addAll(assignment.getAdminGuiConfigurations()); } for (EvaluatedAssignmentTarget target : assignment.getRoles().getNonNegativeValues()) { if (target.getTarget() != null && target.getTarget().asObjectable() instanceof UserType && DeputyUtils.isDelegationPath(target.getAssignmentPath())) { List<OtherPrivilegesLimitationType> limitations = DeputyUtils.extractLimitations(target.getAssignmentPath()); principal.addDelegatorWithOtherPrivilegesLimitations(new DelegatorWithOtherPrivilegesLimitations( (UserType) target.getTarget().asObjectable(), limitations)); } } } catch (SchemaException e) { LOGGER.error("Schema violation while processing assignment of {}: {}; assignment: {}", userType, e.getMessage(), assignmentType, e); } catch (ObjectNotFoundException e) { LOGGER.error("Object not found while processing assignment of {}: {}; assignment: {}", userType, e.getMessage(), assignmentType, e); } catch (ExpressionEvaluationException e) { LOGGER.error("Evaluation error while processing assignment of {}: {}; assignment: {}", userType, e.getMessage(), assignmentType, e); } catch (PolicyViolationException e) { LOGGER.error("Policy violation while processing assignment of {}: {}; assignment: {}", userType, e.getMessage(), assignmentType, e); } } } if (userType.getAdminGuiConfiguration() != null) { // config from the user object should go last (to be applied as the last one) adminGuiConfigurations.add(userType.getAdminGuiConfiguration()); } principal.setAdminGuiConfiguration(AdminGuiConfigTypeUtil.compileAdminGuiConfiguration(adminGuiConfigurations, systemConfiguration)); } private MidPointPrincipal save(MidPointPrincipal person, OperationResult result) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { UserType oldUserType = getUserByOid(person.getOid(), result); PrismObject<UserType> oldUser = oldUserType.asPrismObject(); PrismObject<UserType> newUser = person.getUser().asPrismObject(); ObjectDelta<UserType> delta = oldUser.diff(newUser); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Updating user {} with delta:\n{}", newUser, delta.debugDump()); } repositoryService.modifyObject(UserType.class, delta.getOid(), delta.getModifications(), new OperationResult(OPERATION_UPDATE_USER)); return person; } private UserType getUserByOid(String oid, OperationResult result) throws ObjectNotFoundException, SchemaException { ObjectType object = repositoryService.getObject(UserType.class, oid, null, result).asObjectable(); if (object != null && (object instanceof UserType)) { return (UserType) object; } return null; } @Override public <F extends FocusType, O extends ObjectType> PrismObject<F> resolveOwner(PrismObject<O> object) { if (object == null || object.getOid() == null) { return null; } PrismObject<F> owner = null; OperationResult result = new OperationResult(UserProfileServiceImpl.class+".resolveOwner"); if (object.canRepresent(ShadowType.class)) { owner = repositoryService.searchShadowOwner(object.getOid(), null, result); } else if (object.canRepresent(UserType.class)) { ObjectQuery query = QueryBuilder.queryFor(UserType.class, prismContext) .item(FocusType.F_PERSONA_REF).ref(object.getOid()).build(); SearchResultList<PrismObject<UserType>> owners = null; try { owners = repositoryService.searchObjects(UserType.class, query, null, result); if (owners.isEmpty()) { return null; } if (owners.size() > 1) { LOGGER.warn("More than one owner of {}: {}", object, owners); } owner = (PrismObject<F>) owners.get(0); } catch (SchemaException e) { LOGGER.warn("Cannot resolve owner of {}: {}", object, e.getMessage(), e); } } else if (object.canRepresent(AbstractRoleType.class)) { ObjectReferenceType ownerRef = ((AbstractRoleType)(object.asObjectable())).getOwnerRef(); if (ownerRef != null && ownerRef.getOid() != null && ownerRef.getType() != null) { try { owner = (PrismObject<F>) repositoryService.getObject(ObjectTypes.getObjectTypeFromTypeQName(ownerRef.getType()).getClassDefinition(), ownerRef.getOid(), null, result); } catch (ObjectNotFoundException | SchemaException e) { LOGGER.warn("Cannot resolve owner of {}: {}", object, e.getMessage(), e); } } } else if (object.canRepresent(TaskType.class)) { ObjectReferenceType ownerRef = ((TaskType)(object.asObjectable())).getOwnerRef(); if (ownerRef != null && ownerRef.getOid() != null && ownerRef.getType() != null) { try { owner = (PrismObject<F>) repositoryService.getObject(ObjectTypes.getObjectTypeFromTypeQName(ownerRef.getType()).getClassDefinition(), ownerRef.getOid(), null, result); } catch (ObjectNotFoundException | SchemaException e) { LOGGER.warn("Cannot resolve owner of {}: {}", object, e.getMessage(), e); } } } if (owner == null) { return null; } if (owner.canRepresent(UserType.class)) { userComputer.recompute((PrismObject<UserType>)owner); } return owner; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { try { return getPrincipal(username); } catch (ObjectNotFoundException e) { throw new UsernameNotFoundException(e.getMessage(), e); } catch (SchemaException e) { throw new SystemException(e.getMessage(), e); } } @Override public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) { try { return getPrincipal(username); } catch (ObjectNotFoundException e) { throw new UsernameNotFoundException(e.getMessage(), e); } catch (SchemaException e) { throw new SystemException(e.getMessage(), e); } } @Override public void mapUserToContext(UserDetails user, DirContextAdapter ctx) { // TODO Auto-generated method stub } }