diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java index 3f503d1e85f..99fcc90a2e4 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/util/WebComponentUtil.java @@ -581,9 +581,13 @@ public static DropDownChoicePanel createEnumPanel(Class clazz, } public static DropDownChoicePanel createEnumPanel(Class clazz, String id, final IModel model, final Component component, boolean allowNull) { - // final Class clazz = model.getObject().getClass(); - final Object o = model.getObject(); - return new DropDownChoicePanel(id, model, WebComponentUtil.createReadonlyModelFromEnum(clazz), + return createEnumPanel(clazz, id, WebComponentUtil.createReadonlyModelFromEnum(clazz), + model, component, allowNull ); + } + + public static DropDownChoicePanel createEnumPanel(Class clazz, String id, + IModel> choicesList, final IModel model, final Component component, boolean allowNull) { + return new DropDownChoicePanel(id, model, choicesList, new IChoiceRenderer() { @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentCatalogPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentCatalogPanel.java index 9365b4793a1..cf2505c0297 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentCatalogPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentCatalogPanel.java @@ -19,18 +19,11 @@ import com.evolveum.midpoint.gui.api.component.ObjectBrowserPanel; import com.evolveum.midpoint.gui.api.model.LoadableModel; import com.evolveum.midpoint.gui.api.page.PageBase; -import com.evolveum.midpoint.gui.api.util.WebComponentUtil; -import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils; import com.evolveum.midpoint.model.api.ModelInteractionService; import com.evolveum.midpoint.model.api.RoleSelectionSpecification; -import com.evolveum.midpoint.model.api.context.*; -import com.evolveum.midpoint.prism.PrismContainerDefinition; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.delta.DeltaSetTriple; -import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.query.*; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.LoggingUtils; @@ -46,18 +39,14 @@ import com.evolveum.midpoint.web.component.util.SelectableBean; import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; import com.evolveum.midpoint.web.page.admin.orgs.OrgTreePanel; -import com.evolveum.midpoint.web.page.admin.users.dto.TreeStateSet; import com.evolveum.midpoint.web.page.admin.users.dto.UserDtoStatus; -import com.evolveum.midpoint.web.page.self.PageAssignmentShoppingKart; import com.evolveum.midpoint.web.page.self.PageAssignmentsList; -import com.evolveum.midpoint.web.page.self.dto.AssignmentConflictDto; import com.evolveum.midpoint.web.page.self.dto.AssignmentViewType; import com.evolveum.midpoint.web.security.SecurityUtils; import com.evolveum.midpoint.web.session.OrgTreeStateStorage; import com.evolveum.midpoint.web.session.SessionStorage; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang3.EnumUtils; import org.apache.wicket.ajax.AjaxChannel; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; @@ -72,7 +61,6 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; -import org.apache.wicket.model.util.ListModel; import javax.xml.namespace.QName; import java.util.*; @@ -83,11 +71,11 @@ public class AssignmentCatalogPanel extends BasePanel { private static final long serialVersionUID = 1L; - private static String ID_TREE_PANEL_CONTAINER = "treePanelContainer"; - private static String ID_TREE_PANEL = "treePanel"; - private static String ID_CATALOG_ITEMS_PANEL_CONTAINER = "catalogItemsPanelContainer"; - private static String ID_ASSIGNMENTS_OWNER_NAME = "assignmentsOwnerName"; - private static String ID_CATALOG_ITEMS_PANEL = "catalogItemsPanel"; + private static final String ID_TREE_PANEL_CONTAINER = "treePanelContainer"; + private static final String ID_TREE_PANEL = "treePanel"; + private static final String ID_CATALOG_ITEMS_PANEL_CONTAINER = "catalogItemsPanelContainer"; + private static final String ID_ASSIGNMENTS_OWNER_NAME = "assignmentsOwnerName"; + private static final String ID_CATALOG_ITEMS_PANEL = "catalogItemsPanel"; private static final String ID_CART_BUTTON = "cartButton"; private static final String ID_CART_ITEMS_COUNT = "itemsCount"; private static final String ID_HEADER_PANEL = "headerPanel"; @@ -102,6 +90,7 @@ public class AssignmentCatalogPanel extends BasePane private static final String DOT_CLASS = AssignmentCatalogPanel.class.getName(); private static final Trace LOGGER = TraceManager.getTrace(AssignmentCatalogPanel.class); + private static final String OPERATION_LOAD_ASSIGNMENT_CONSTRAINTS = DOT_CLASS + "loadAssignmentConstraints"; private static final String OPERATION_LOAD_ASSIGNABLE_ROLES = DOT_CLASS + "loadAssignableRoles"; private PageBase pageBase; @@ -111,11 +100,9 @@ public class AssignmentCatalogPanel extends BasePane private IModel viewModel; private IModel> targetUserModel; private ObjectDataProvider objectProvider; - private ListDataProvider listProvider; private int itemsPerRow = 4; private boolean showUserSelectionPopup = true; private List listProviderData; - private AssignmentViewType defaultAssignmentViewType = AssignmentViewType.ROLE_CATALOG_VIEW; List viewTypeList = new ArrayList<>(); public AssignmentCatalogPanel(String id) { @@ -153,7 +140,8 @@ protected void initProvider() { @Override public AssignmentEditorDto createDataObjectWrapper(PrismObject obj) { AssignmentEditorDto dto = AssignmentEditorDto.createDtoFromObject(obj.asObjectable(), UserDtoStatus.ADD, pageBase); - dto.setAlreadyAssigned(isAlreadyAssigned(obj)); + dto.setAlreadyAssigned(isAlreadyAssigned(obj, dto)); + dto.setDefualtAssignmentConstraints(getAssignmentConstraints()); return dto; } @@ -534,9 +522,8 @@ private ObjectQuery createMemberQuery(String oid) { ObjectFilter filter = OrgFilter.createOrg(oid, OrgFilter.Scope.ONE_LEVEL); TypeFilter roleTypeFilter = TypeFilter.createType(RoleType.COMPLEX_TYPE, filter); - TypeFilter orgTypeFilter = TypeFilter.createType(OrgType.COMPLEX_TYPE, filter); TypeFilter serviceTypeFilter = TypeFilter.createType(ServiceType.COMPLEX_TYPE, filter); - ObjectQuery query = ObjectQuery.createObjectQuery(OrFilter.createOr(roleTypeFilter, orgTypeFilter, serviceTypeFilter)); + ObjectQuery query = ObjectQuery.createObjectQuery(OrFilter.createOr(roleTypeFilter, serviceTypeFilter)); return query; } @@ -642,7 +629,9 @@ protected void onSelectPerformed(AjaxRequestTarget target, UserType targetUser) super.onSelectPerformed(target, targetUser); if (targetUserSelection) { pageBase.getSessionStorage().getRoleCatalog().setTargetUser(targetUser.asPrismContainer()); - target.add(getTargetUserContainer()); + AssignmentCatalogPanel.this.addOrReplaceLayout(target, getCatalogItemsPanelContainer()); + target.add(getHeaderPanel()); + target.add(getCatalogItemsPanelContainer()); } else { pageBase.getSessionStorage().getRoleCatalog().setAssignmentsUserOwner(targetUser.asPrismContainer()); AssignmentCatalogPanel.this.addOrReplaceLayout(target, getCatalogItemsPanelContainer()); @@ -689,17 +678,21 @@ public void detach() { }; } - private boolean isAlreadyAssigned(PrismObject obj){ + private boolean isAlreadyAssigned(PrismObject obj, AssignmentEditorDto assignmentDto){ PrismObject user = getTargetUser(); if (user == null || user.asObjectable().getAssignment() == null){ return false; } + boolean isAssigned = false; + List assignedRelationsList = new ArrayList<>(); for (AssignmentType assignment : user.asObjectable().getAssignment()){ if (assignment.getTargetRef() != null && assignment.getTargetRef().getOid().equals(obj.getOid())){ - return true; + isAssigned = true; + assignedRelationsList.add(RelationTypes.getRelationType(assignment.getTargetRef().getRelation())); } } - return false; + assignmentDto.setAssignedRelationsList(assignedRelationsList); + return isAssigned; } private PrismObject getTargetUser(){ @@ -710,5 +703,20 @@ private PrismObject getTargetUser(){ return pageBase.loadUserSelf(pageBase); } + private AssignmentConstraintsType getAssignmentConstraints() { + OperationResult result = new OperationResult(OPERATION_LOAD_ASSIGNMENT_CONSTRAINTS); + SystemConfigurationType systemConfig = null; + try { + systemConfig = pageBase.getModelInteractionService().getSystemConfiguration(result); + } catch (ObjectNotFoundException | SchemaException e) { + LOGGER.error("Error getting system configuration: {}", e.getMessage(), e); + return null; + } + if (systemConfig != null && systemConfig.getRoleManagement() != null) { + return systemConfig.getRoleManagement().getDefaultAssignmentConstraints(); + } + return null; + } + } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentEditorDto.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentEditorDto.java index fb74fe13274..c37887498f1 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentEditorDto.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentEditorDto.java @@ -17,6 +17,7 @@ package com.evolveum.midpoint.web.component.assignment; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -95,7 +96,9 @@ public class AssignmentEditorDto extends SelectableBean implements Comparable assignedRelationsList = new ArrayList<>(); //used only for role request functionalityp private Boolean isOrgUnitManager = Boolean.FALSE; private AssignmentType newAssignment; @@ -582,7 +585,7 @@ public QName getFocusType() { public String getRelation() { ObjectReferenceType ref = newAssignment.getTargetRef(); if (ref == null || ref.getRelation() == null) { - return null; // TODO member vs. null ? + return null; // TODO default vs. null ? } return ref.getRelation().getLocalPart(); @@ -674,6 +677,22 @@ public void setAlreadyAssigned(boolean alreadyAssigned) { isAlreadyAssigned = alreadyAssigned; } + public AssignmentConstraintsType getDefualtAssignmentConstraints() { + return defualtAssignmentConstraints; + } + + public void setDefualtAssignmentConstraints(AssignmentConstraintsType defualtAssignmentConstraints) { + this.defualtAssignmentConstraints = defualtAssignmentConstraints; + } + + public List getAssignedRelationsList() { + return assignedRelationsList; + } + + public void setAssignedRelationsList(List assignedRelationsList) { + this.assignedRelationsList = assignedRelationsList; + } + public List getPrivilegeLimitationList() { return privilegeLimitationList; } @@ -705,6 +724,76 @@ public void setDelegationOwner(UserType delegationOwner) { this.delegationOwner = delegationOwner; } + public List getNotAssignedRelationsList(){ + List relations = new ArrayList<>(Arrays.asList(RelationTypes.values())); + if (getAssignedRelationsList() == null || getAssignedRelationsList().size() == 0){ + return relations; + } + for (RelationTypes relation : getAssignedRelationsList()){ + if (relations.contains(relation)){ + relations.remove(relation); + } + } + return relations; + } + + public boolean isAssignable() { + if (!isAlreadyAssigned){ + return true; + } + if (defualtAssignmentConstraints == null) { + return true; + } + if (defualtAssignmentConstraints.isAllowSameTarget() && defualtAssignmentConstraints.isAllowSameRelation()){ + return true; + } + if (defualtAssignmentConstraints.isAllowSameTarget() && !defualtAssignmentConstraints.isAllowSameRelation() + && getAssignedRelationsList().size() < RelationTypes.values().length){ + return true; + } + if (!defualtAssignmentConstraints.isAllowSameTarget() && defualtAssignmentConstraints.isAllowSameRelation() + && getAssignedRelationsList().size() < RelationTypes.values().length){ + return true; + } + if (!defualtAssignmentConstraints.isAllowSameTarget() && !defualtAssignmentConstraints.isAllowSameRelation()){ + return false; + } + return false; + } + + public boolean isMultyAssignable(){ + if (defualtAssignmentConstraints == null) { + return true; + } + if (defualtAssignmentConstraints.isAllowSameTarget() && defualtAssignmentConstraints.isAllowSameRelation()){ + return true; + } + return false; + } + + public boolean isSingleAssignable(){ + if (defualtAssignmentConstraints == null) { + return false; + } + if (!defualtAssignmentConstraints.isAllowSameTarget() && !defualtAssignmentConstraints.isAllowSameRelation()){ + return true; + } + return false; + } + + public void setDefaultRelation(){ + if (getTargetRef() == null){ + return; + } + if (!getAssignedRelationsList().contains(RelationTypes.MEMBER)){ + getTargetRef().setRelation(SchemaConstants.ORG_DEFAULT); + } + List availableRelations = getNotAssignedRelationsList(); + if (availableRelations.size() > 0){ + getTargetRef().setRelation(availableRelations.get(0).getRelation()); + } + } + @Override public boolean equals(Object o) { if (this == o) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentEditorPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentEditorPanel.java index 9883267c367..d3f908fe8cb 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentEditorPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentEditorPanel.java @@ -107,7 +107,7 @@ public class AssignmentEditorPanel extends BasePanel { private static final String ID_DESCRIPTION = "description"; private static final String ID_RELATION_CONTAINER = "relationContainer"; private static final String ID_FOCUS_TYPE = "focusType"; - private static final String ID_RELATION = "relation"; + protected static final String ID_RELATION = "relation"; private static final String ID_RELATION_LABEL = "relationLabel"; private static final String ID_ADMINISTRATIVE_STATUS = "administrativeStatus"; private static final String ID_VALID_FROM = "validFrom"; @@ -417,42 +417,7 @@ public boolean isVisible() { ObjectTypeSelectPanel focusType = new ObjectTypeSelectPanel<>(ID_FOCUS_TYPE, new PropertyModel(getModel(), AssignmentEditorDto.F_FOCUS_TYPE), FocusType.class); body.add(focusType); - - IModel relationModel = new IModel() { - @Override - public RelationTypes getObject() { - if (getModelObject().getTargetRef() == null) { - return RelationTypes.MEMBER; - } - return RelationTypes.getRelationType(getModelObject().getTargetRef().getRelation()); - } - - @Override - public void setObject(RelationTypes newValue) { - ObjectReferenceType ref = getModelObject().getTargetRef(); - if (ref != null){ - ref.setRelation(newValue.getRelation()); - } - } - - @Override - public void detach() { - - } - }; - DropDownChoicePanel relation = WebComponentUtil.createEnumPanel(RelationTypes.class, ID_RELATION, - relationModel, this, false); - relation.setEnabled(getModel().getObject().isEditable()); - relation.setOutputMarkupId(true); - relation.setOutputMarkupPlaceholderTag(true); - relation.add(new VisibleEnableBehaviour() { - - @Override - public boolean isVisible() { - return isCreatingNewAssignment(); - } - }); - relationContainer.add(relation); + addRelationDropDown(relationContainer); Label relationLabel = new Label(ID_RELATION_LABEL, new AbstractReadOnlyModel() { @@ -940,6 +905,83 @@ protected String load() { }; } + private void addRelationDropDown(WebMarkupContainer relationContainer){ + List availableRelations = getModelObject().getNotAssignedRelationsList(); + DropDownChoicePanel relation = WebComponentUtil.createEnumPanel(RelationTypes.class, ID_RELATION, + getModelObject().isMultyAssignable() ? + WebComponentUtil.createReadonlyModelFromEnum(RelationTypes.class) : Model.ofList(availableRelations), + getRelationModel(availableRelations), this, false); + relation.setEnabled(getModel().getObject().isEditable()); + relation.setOutputMarkupId(true); + relation.setOutputMarkupPlaceholderTag(true); + relation.add(new VisibleEnableBehaviour() { + + @Override + public boolean isVisible() { + return isCreatingNewAssignment(); + } + }); + relationContainer.add(relation); + + } + + private IModel getRelationModel(List availableRelations){ + return new IModel() { + @Override + public RelationTypes getObject() { + RelationTypes defaultRelation = RelationTypes.MEMBER; + if (!getModelObject().isMultyAssignable() && + getModelObject().getAssignedRelationsList().contains(defaultRelation)){ + defaultRelation = availableRelations.get(0); + } + if (getModelObject().getTargetRef() == null){ + return defaultRelation; + } + return RelationTypes.getRelationType(getModelObject().getTargetRef().getRelation()); + } + + @Override + public void setObject(RelationTypes relationTypes) { + if (getModelObject().getTargetRef() != null){ + getModelObject().getTargetRef().setRelation(relationTypes.getRelation()); + } + } + + @Override + public void detach() { + + } + }; + } + + + protected IModel getRelationModel(){ + return new IModel() { + private static final long serialVersionUID = 1L; + + @Override + public RelationTypes getObject() { + if (getModelObject().getTargetRef() == null) { + return RelationTypes.MEMBER; + } + return RelationTypes.getRelationType(getModelObject().getTargetRef().getRelation()); + } + + @Override + public void setObject(RelationTypes newValue) { + ObjectReferenceType ref = getModelObject().getTargetRef(); + if (ref != null){ + ref.setRelation(newValue.getRelation()); + } + } + + @Override + public void detach() { + + } + }; + } + private PrismObject getTargetObject(AssignmentEditorDto dto) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentTablePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentTablePanel.java index d7bafa41ab9..1abcc8bd934 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentTablePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentTablePanel.java @@ -72,6 +72,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import org.w3c.dom.Attr; /** * @author shood @@ -128,7 +129,7 @@ private void initLayout(IModel labelText) { @Override protected void populateItem(ListItem item) { - AssignmentTablePanel.this.populateItem(item); + AssignmentTablePanel.this.populateAssignmentDetailsPanel(item); } }; list.setOutputMarkupId(true); @@ -167,7 +168,7 @@ public boolean isVisible(){ } - protected void populateItem(ListItem item){ + protected void populateAssignmentDetailsPanel(ListItem item){ AssignmentEditorPanel editor = new AssignmentEditorPanel(ID_ROW, item.getModel()){ @Override protected boolean ignoreMandatoryAttributes(){ @@ -176,7 +177,11 @@ protected boolean ignoreMandatoryAttributes(){ }; item.add(editor); - editor.add(AttributeModifier.append("class", new AbstractReadOnlyModel() { + editor.add(getClassModifier(item)); + } + + protected AttributeModifier getClassModifier(ListItem item){ + return AttributeModifier.append("class", new AbstractReadOnlyModel() { private static final long serialVersionUID = 1L; @Override @@ -189,7 +194,7 @@ public String getObject() { return GuiStyleConstants.CLASS_OBJECT_RESOURCE_BOX_THIN_CSS_CLASSES; } } - })); + }); } protected List createAssignmentMenu() { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/ShoppingCartEditorPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/ShoppingCartEditorPanel.java index fc5f0aa4405..cb57a877ec2 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/ShoppingCartEditorPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/ShoppingCartEditorPanel.java @@ -16,13 +16,21 @@ package com.evolveum.midpoint.web.component.assignment; +import com.evolveum.midpoint.gui.api.util.WebComponentUtil; +import com.evolveum.midpoint.web.component.input.DropDownChoicePanel; +import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import org.apache.wicket.AttributeModifier; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; +import java.util.ArrayList; +import java.util.List; + /** * Created by honchar. */ diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/MultiButtonTable.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/MultiButtonTable.java index e8756e8d3e9..4b7e46d7ce4 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/MultiButtonTable.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/data/MultiButtonTable.java @@ -18,17 +18,12 @@ import com.evolveum.midpoint.gui.api.GuiStyleConstants; import com.evolveum.midpoint.gui.api.component.BasePanel; import com.evolveum.midpoint.gui.api.page.PageBase; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.component.assignment.*; import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; import com.evolveum.midpoint.web.page.self.PageAssignmentDetails; import com.evolveum.midpoint.web.session.RoleCatalogStorage; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentConstraintsType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.behavior.AttributeAppender; @@ -36,6 +31,7 @@ import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.repeater.RepeatingView; import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; import java.util.ArrayList; import java.util.List; @@ -61,7 +57,6 @@ public class MultiButtonTable extends BasePanel> { private static final String DOT_CLASS = AssignmentCatalogPanel.class.getName(); private static final Trace LOGGER = TraceManager.getTrace(AssignmentCatalogPanel.class); - private static final String OPERATION_LOAD_ASSIGNMENT_CONSTRAINTS = DOT_CLASS + "loadAssignmentConstraints"; private String addToCartLinkIcon = "fa fa-times-circle fa-lg text-danger"; private String detailsLinkIcon = "fa fa-arrow-circle-right"; @@ -219,37 +214,14 @@ public boolean isEnabled(){ } private boolean canAssign(final AssignmentEditorDto assignment) { - AssignmentConstraintsType assignmentConstraints = getAssignmentConstraints(); - if (assignmentConstraints == null) { - return true; - } - // TODO -// return !(AssignmentMultiplicityType.SINGLE.equals(getAssignmentMultiplicity()) -// && assignment.isAlreadyAssigned()); - return true; + return assignment.isAssignable(); } private void assignmentDetailsPerformed(final AssignmentEditorDto assignment, AjaxRequestTarget target){ if (!plusIconClicked) { - IModel assignmentModel = new IModel() { - @Override - public AssignmentEditorDto getObject() { - assignment.setMinimized(false); - assignment.setSimpleView(true); - return assignment; - } - - @Override - public void setObject(AssignmentEditorDto assignmentEditorDto) { - - } - - @Override - public void detach() { - - } - }; - setResponsePage(new PageAssignmentDetails(assignmentModel)); + assignment.setMinimized(false); + assignment.setSimpleView(true); + pageBase.navigateToNext(new PageAssignmentDetails(Model.of(assignment))); } else { plusIconClicked = false; } @@ -284,30 +256,15 @@ private String getBackgroundClass(AssignmentEditorDto dto){ private void addAssignmentPerformed(AssignmentEditorDto assignment, AjaxRequestTarget target){ plusIconClicked = true; - RoleCatalogStorage storage = getPageBase().getSessionStorage().getRoleCatalog(); + RoleCatalogStorage storage = pageBase.getSessionStorage().getRoleCatalog(); if (storage.getAssignmentShoppingCart() == null){ storage.setAssignmentShoppingCart(new ArrayList()); } - List assignmentsToAdd = storage.getAssignmentShoppingCart(); - assignmentsToAdd.add(assignment); - storage.setAssignmentShoppingCart(assignmentsToAdd); + assignment.setDefaultRelation(); + storage.getAssignmentShoppingCart().add(assignment); AssignmentCatalogPanel parent = MultiButtonTable.this.findParent(AssignmentCatalogPanel.class); parent.reloadCartButton(target); } - private AssignmentConstraintsType getAssignmentConstraints() { - OperationResult result = new OperationResult(OPERATION_LOAD_ASSIGNMENT_CONSTRAINTS); - SystemConfigurationType systemConfig = null; - try { - systemConfig = pageBase.getModelInteractionService().getSystemConfiguration(result); - } catch (ObjectNotFoundException | SchemaException e) { - LOGGER.error("Error getting system configuration: {}", e.getMessage(), e); - return null; - } - if (systemConfig != null && systemConfig.getRoleManagement() != null) { - return systemConfig.getRoleManagement().getDefaultAssignmentConstraints(); - } - return null; - } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertDefinitionDto.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertDefinitionDto.java index bdf4c50e338..611268d9c63 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertDefinitionDto.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/CertDefinitionDto.java @@ -220,7 +220,7 @@ public void setRemediationStyle(AccessCertificationRemediationStyleType remediat private DefinitionScopeDto createDefinitionScopeDto(AccessCertificationScopeType scopeTypeObj, PrismContext prismContext) { DefinitionScopeDto dto = new DefinitionScopeDto(); - // default values, optionally overriden below + // default values, optionally overridden below dto.setIncludeAssignments(true); dto.setIncludeInducements(true); dto.setIncludeResources(true); @@ -232,11 +232,9 @@ private DefinitionScopeDto createDefinitionScopeDto(AccessCertificationScopeType if (scopeTypeObj != null) { dto.setName(scopeTypeObj.getName()); dto.setDescription(scopeTypeObj.getDescription()); - if (scopeTypeObj instanceof AccessCertificationObjectBasedScopeType) { - dto.setItemSelectionExpression(((AccessCertificationObjectBasedScopeType) scopeTypeObj).getItemSelectionExpression()); - } if (scopeTypeObj instanceof AccessCertificationObjectBasedScopeType) { AccessCertificationObjectBasedScopeType objScopeType = (AccessCertificationObjectBasedScopeType) scopeTypeObj; + dto.setItemSelectionExpression(objScopeType.getItemSelectionExpression()); if (objScopeType.getObjectType() != null) { dto.setObjectType(DefinitionScopeObjectType.valueOf(objScopeType.getObjectType().getLocalPart())); } @@ -251,6 +249,7 @@ private DefinitionScopeDto createDefinitionScopeDto(AccessCertificationScopeType dto.setIncludeOrgs(!Boolean.FALSE.equals(assignmentScope.isIncludeOrgs())); dto.setIncludeServices(!Boolean.FALSE.equals(assignmentScope.isIncludeServices())); dto.setEnabledItemsOnly(!Boolean.FALSE.equals(assignmentScope.isEnabledItemsOnly())); + dto.setRelationList(new ArrayList<>(assignmentScope.getRelation())); } } } @@ -304,6 +303,7 @@ public void updateScopeDefinition(PrismContext prismContext) { scopeTypeObj.setIncludeServices(definitionScopeDto.isIncludeServices()); scopeTypeObj.setEnabledItemsOnly(definitionScopeDto.isEnabledItemsOnly()); scopeTypeObj.setItemSelectionExpression(definitionScopeDto.getItemSelectionExpression()); + scopeTypeObj.getRelation().addAll(definitionScopeDto.getRelationList()); } definition.setScopeDefinition(scopeTypeObj); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/DefinitionScopeDto.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/DefinitionScopeDto.java index 344f8ab27ea..f00bc3b177a 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/DefinitionScopeDto.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/certification/dto/DefinitionScopeDto.java @@ -23,7 +23,9 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType; import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; +import javax.xml.namespace.QName; import java.io.Serializable; +import java.util.List; /** * @author Kate @@ -54,6 +56,7 @@ public class DefinitionScopeDto implements Serializable { private boolean includeServices; private boolean enabledItemsOnly; private ExpressionType itemSelectionExpression; + private List relationList; public void loadSearchFilter(SearchFilterType searchFilterType, PrismContext prismContext) { if (searchFilterType == null) { @@ -179,4 +182,12 @@ public ExpressionType getItemSelectionExpression() { public void setItemSelectionExpression(ExpressionType itemSelectionExpression) { this.itemSelectionExpression = itemSelectionExpression; } + + public List getRelationList() { + return relationList; + } + + public void setRelationList(List relationList) { + this.relationList = relationList; + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUser.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUser.java index 9894552ed4a..6f133790365 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUser.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUser.java @@ -197,7 +197,7 @@ public WebMarkupContainer createPanel(String panelId) { private static final long serialVersionUID = 1L; @Override - public void populateItem(ListItem item) { + public void populateAssignmentDetailsPanel(ListItem item) { DelegationEditorPanel editor = new DelegationEditorPanel(ID_ROW, item.getModel(), false, privilegesList, PageUser.this); item.add(editor); @@ -330,7 +330,7 @@ public WebMarkupContainer createPanel(String panelId) { private static final long serialVersionUID = 1L; @Override - public void populateItem(ListItem item) { + public void populateAssignmentDetailsPanel(ListItem item) { DelegationEditorPanel editor = new DelegationEditorPanel(ID_ROW, item.getModel(), true, privilegesList, PageUser.this); item.add(editor); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUserHistory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUserHistory.java index f77803504bf..100c1825550 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUserHistory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/users/PageUserHistory.java @@ -215,7 +215,7 @@ public WebMarkupContainer createPanel(String panelId) { private static final long serialVersionUID = 1L; @Override - public void populateItem(ListItem item) { + public void populateAssignmentDetailsPanel(ListItem item) { DelegationEditorPanel editor = new DelegationEditorPanel(ID_ROW, item.getModel(), true, new ArrayList(), PageUserHistory.this); item.add(editor); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentDetails.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentDetails.java index 17eb8d4b64e..6b774388cb4 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentDetails.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentDetails.java @@ -70,9 +70,8 @@ public void onClick(AjaxRequestTarget target) { if (storage.getAssignmentShoppingCart() == null){ storage.setAssignmentShoppingCart(new ArrayList()); } - List assignmentsToAdd = storage.getAssignmentShoppingCart(); - assignmentsToAdd.add(assignmentModel.getObject()); - storage.setAssignmentShoppingCart(assignmentsToAdd); + AssignmentEditorDto dto = assignmentModel.getObject(); + storage.getAssignmentShoppingCart().add(dto); redirectBack(); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentsList.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentsList.html index 17d2362c174..bf5e7f45812 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentsList.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentsList.html @@ -22,6 +22,7 @@
+

diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentsList.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentsList.java index aed411a2172..de1f622f039 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentsList.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/self/PageAssignmentsList.java @@ -19,7 +19,9 @@ import com.evolveum.midpoint.web.component.AjaxButton; import com.evolveum.midpoint.web.component.AjaxSubmitButton; import com.evolveum.midpoint.web.component.assignment.AssignmentEditorDto; +import com.evolveum.midpoint.web.component.assignment.AssignmentEditorPanel; import com.evolveum.midpoint.web.component.assignment.AssignmentTablePanel; +import com.evolveum.midpoint.web.component.assignment.ShoppingCartEditorPanel; import com.evolveum.midpoint.web.component.form.Form; import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItem; import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItemAction; @@ -33,6 +35,7 @@ import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.markup.html.form.TextArea; +import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; @@ -85,7 +88,7 @@ public void initLayout() { mainForm.setOutputMarkupId(true); add(mainForm); - AssignmentTablePanel panel = new AssignmentTablePanel(ID_ASSIGNMENT_TABLE_PANEL, + AssignmentTablePanel panel = new AssignmentTablePanel(ID_ASSIGNMENT_TABLE_PANEL, createStringResource("FocusType.assignment"), assignmentsModel){ @Override protected List createAssignmentMenu() { @@ -101,7 +104,6 @@ public void onClick(AjaxRequestTarget target) { }); items.add(item); return items; - } }; mainForm.add(panel); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java index df7cf9af7b1..dc9f3c75910 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java @@ -21,6 +21,7 @@ import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.AuthorizationConstants; +import com.evolveum.midpoint.security.api.ItemSecurityDecisions; import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.security.api.ObjectSecurityConstraints; import com.evolveum.midpoint.security.api.OwnerResolver; @@ -33,6 +34,7 @@ import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.application.DescriptorLoader; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationPhaseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; @@ -211,4 +213,11 @@ public T runAs(Producer producer, PrismObject user) throws Sche public T runPrivileged(Producer producer) { return securityEnforcer.runPrivileged(producer); } + + @Override + public ItemSecurityDecisions getAllowedRequestAssignmentItems( + MidPointPrincipal midPointPrincipal, PrismObject object, PrismObject target, + OwnerResolver ownerResolver) throws SchemaException { + return securityEnforcer.getAllowedRequestAssignmentItems(midPointPrincipal, object, target, ownerResolver); + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/session/RoleCatalogStorage.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/session/RoleCatalogStorage.java index 9c514d929c0..3d77751423b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/session/RoleCatalogStorage.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/session/RoleCatalogStorage.java @@ -146,7 +146,10 @@ public void setConflictsList(List conflictsList) { } public List getAssignmentShoppingCart() { - return assignmentShoppingCart == null ? new ArrayList() : assignmentShoppingCart; + if (assignmentShoppingCart == null){ + assignmentShoppingCart = new ArrayList<>(); + } + return assignmentShoppingCart; } public void setAssignmentShoppingCart(List assignmentShoppingCart) { diff --git a/gui/admin-gui/src/main/resources/initial-objects/120-security-policy.xml b/gui/admin-gui/src/main/resources/initial-objects/015-security-policy.xml similarity index 100% rename from gui/admin-gui/src/main/resources/initial-objects/120-security-policy.xml rename to gui/admin-gui/src/main/resources/initial-objects/015-security-policy.xml diff --git a/gui/admin-gui/src/main/resources/localization/Midpoint.properties b/gui/admin-gui/src/main/resources/localization/Midpoint.properties index 09999e076c1..a99be7490a5 100644 --- a/gui/admin-gui/src/main/resources/localization/Midpoint.properties +++ b/gui/admin-gui/src/main/resources/localization/Midpoint.properties @@ -3405,8 +3405,8 @@ PageAssignmentConflicts.back=Back PageAssignmentConflicts.submit=Submit AssignmentCatalogPanel.selectTargetUser=Select target user AssignmentCatalogPanel.selectAssignmentsUserOwner=Select assignments user owner -AssignmentCatalogPanel.requestForMe=Request for me -AssignmentCatalogPanel.requestFor=Request for +AssignmentCatalogPanel.requestForMe=Target user: me +AssignmentCatalogPanel.requestFor=Target user: AssignmentCatalogPanel.assignmentsOwner={0}'s assignments MultiButtonPanel.plusIconTitle=Add item to shopping cart MultiButtonPanel.assignmentDetailsPopupTitle=Assignment details @@ -3418,6 +3418,7 @@ PageAssignmentDetails.addToCartButton=Add to cart PageAssignmentsList.submitButton=Submit PageAssignmentsList.resolveConflicts=Resolve conflicts PageAssignmentsList.commentHere=Comment here... +PageAssignmentsList.requestComment=Request comment (optional) AssignmentShoppingCartPanel.treeTitle=Role catalog AssignmentViewType.ROLE_CATALOG_VIEW=Role catalog view AssignmentViewType.ROLE_TYPE=All roles view diff --git a/gui/admin-gui/src/main/resources/localization/Midpoint_en.properties b/gui/admin-gui/src/main/resources/localization/Midpoint_en.properties index 4dada181b92..e2ef3bf6375 100644 --- a/gui/admin-gui/src/main/resources/localization/Midpoint_en.properties +++ b/gui/admin-gui/src/main/resources/localization/Midpoint_en.properties @@ -3361,8 +3361,8 @@ PageAssignmentConflicts.back=Back PageAssignmentConflicts.submit=Submit AssignmentCatalogPanel.selectTargetUser=Select target user AssignmentCatalogPanel.selectAssignmentsUserOwner=Select assignments user owner -AssignmentCatalogPanel.requestForMe=Request for me -AssignmentCatalogPanel.requestFor=Request for +AssignmentCatalogPanel.requestForMe=Target user: me +AssignmentCatalogPanel.requestFor=Target user: AssignmentCatalogPanel.assignmentsOwner={0}'s assignments MultiButtonPanel.plusIconTitle=Add item to shopping cart MultiButtonPanel.assignmentDetailsPopupTitle=Assignment details diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainer.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainer.java index 7333d70a2cb..5920b8241c3 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainer.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainer.java @@ -456,25 +456,26 @@ public Item findItem(It } } - > I findCreateItem(ItemPath itemPath, Class type, ID itemDefinition, boolean create) throws SchemaException { + public boolean containsItem(ItemPath itemPath, boolean acceptEmptyItem) throws SchemaException { if (itemPath == null || itemPath.isEmpty()) { throw new IllegalArgumentException("Empty path specified"); } - - if (itemPath.isEmpty()) { - // This is the end ... - if (type.isAssignableFrom(getClass())) { - return (I) this; - } else { - if (create) { - throw new IllegalStateException("The " + type.getSimpleName() + " " + getElementName() + " cannot be created because " - + this.getClass().getSimpleName() + " with the same name exists"); - } else { - return null; - } + + ItemPath rest = ItemPath.pathRestStartingWithName(itemPath); + for (PrismContainerValue value: getValues()) { + if (value.containsItem(rest, acceptEmptyItem)) { + return true; } } + return false; + } + + > I findCreateItem(ItemPath itemPath, Class type, ID itemDefinition, boolean create) throws SchemaException { + if (itemPath == null || itemPath.isEmpty()) { + throw new IllegalArgumentException("Empty path specified"); + } + IdItemPathSegment idSegment = ItemPath.getFirstIdSegment(itemPath); PrismContainerValue cval = findValue(idSegment); if (cval == null) { diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainerValue.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainerValue.java index 1bd9f3da525..60dc1949306 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainerValue.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismContainerValue.java @@ -631,6 +631,29 @@ public > return findItem(itemDefinition.getName(), type); } + public boolean containsItem(ItemPath propPath, boolean acceptEmptyItem) throws SchemaException { + ItemPathSegment first = propPath.first(); + if (!(first instanceof NameItemPathSegment)) { + throw new IllegalArgumentException("Attempt to lookup item using a non-name path "+propPath+" in "+this); + } + QName subName = ((NameItemPathSegment)first).getName(); + ItemPath rest = propPath.rest(); + Item item = findItemByQName(subName); + if (item != null) { + if (rest.isEmpty()) { + return (acceptEmptyItem || !item.isEmpty()); + } else { + // Go deeper + if (item instanceof PrismContainer) { + return ((PrismContainer)item).containsItem(rest, acceptEmptyItem); + } else { + return (acceptEmptyItem || !item.isEmpty()); + } + } + } + + return false; + } // Expects that "self" path is NOT present in propPath @SuppressWarnings("unchecked") diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java index a0c498c8535..437110a12ea 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java @@ -766,6 +766,10 @@ public ReferenceDelta createReferenceModification(ItemPath path, PrismReferenceD return addModification(referenceDelta); } + public ContainerDelta createContainerModification(QName qname) { + return createContainerModification(new ItemPath(qname)); + } + public ContainerDelta createContainerModification(ItemPath path) { PrismObjectDefinition objDef = getPrismContext().getSchemaRegistry().findObjectDefinitionByCompileTimeClass(getObjectTypeClass()); PrismContainerDefinition propDef = objDef.findContainerDefinition(path); diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java index a49c329f33c..6cb32efc1a6 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/util/PrismAsserts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * 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. @@ -480,6 +480,14 @@ public static void assertNoItemDelta(ObjectDelta objectDelta, ItemPath itemPa assert !objectDelta.hasItemDelta(itemPath) : "Delta for item "+itemPath+" present while not expecting it"; } + public static void assertPropertyDelta(PropertyDelta delta, T[] expectedOldValues, T[] expectedAddValues, T[] expectedDeleteValues, T[] expectedReplaceValues) { + assertNotNull("No delta",delta); + assertSet("delta "+delta, "old", delta.getEstimatedOldValues(), expectedOldValues); + assertSet("delta "+delta, "add", delta.getValuesToAdd(), expectedAddValues); + assertSet("delta "+delta, "delete", delta.getValuesToDelete(), expectedDeleteValues); + assertSet("delta "+delta, "replace", delta.getValuesToReplace(), expectedReplaceValues); + } + public static ContainerDelta assertContainerAddGetContainerDelta(ObjectDelta objectDelta, QName name) { return assertContainerAddGetContainerDelta(objectDelta, new ItemPath(name)); } @@ -846,21 +854,25 @@ public static void assertValues(String message, Collection void assertValues(String message, MatchingRule matchingRule, Collection> actualPValues, T... expectedValues) throws SchemaException { - assertNotNull("Null set in " + message, actualPValues); - if (expectedValues.length != actualPValues.size()) { - fail("Wrong number of values in " + message+ "; expected "+expectedValues.length+" (real values) " - +PrettyPrinter.prettyPrint(expectedValues)+"; has "+actualPValues.size()+" (pvalues) "+actualPValues); - } - for (PrismPropertyValue actualPValue: actualPValues) { - boolean found = false; - for (T value: expectedValues) { - if (PrismUtil.equals(value, actualPValue.getValue(), matchingRule)) { - found = true; - } + if (expectedValues == null) { + assertNull("Unexpected set in" +message+": "+actualPValues, actualPValues); + } else { + assertNotNull("Null set in " + message, actualPValues); + if (expectedValues.length != actualPValues.size()) { + fail("Wrong number of values in " + message+ "; expected "+expectedValues.length+" (real values) " + +PrettyPrinter.prettyPrint(expectedValues)+"; has "+actualPValues.size()+" (pvalues) "+actualPValues); } - if (!found) { - fail("Unexpected value "+actualPValue+" in " + message + "; expected (real values) " - +PrettyPrinter.prettyPrint(expectedValues)+"; has (pvalues) "+actualPValues); + for (PrismPropertyValue actualPValue: actualPValues) { + boolean found = false; + for (T value: expectedValues) { + if (PrismUtil.equals(value, actualPValue.getValue(), matchingRule)) { + found = true; + } + } + if (!found) { + fail("Unexpected value "+actualPValue+" in " + message + "; expected (real values) " + +PrettyPrinter.prettyPrint(expectedValues)+"; has (pvalues) "+actualPValues); + } } } } @@ -1050,6 +1062,10 @@ public static void assertRefFilter(ObjectFilter objectFilter, QName expectedFilt static void assertNotNull(String string, Object object) { assert object != null : string; } + + static void assertNull(String string, Object object) { + assert object == null : string; + } private static void assertTrue(String message, boolean test) { assert test : message; diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/ExpressionConstants.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/ExpressionConstants.java index c877c5ff844..43d25e790f8 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/ExpressionConstants.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/ExpressionConstants.java @@ -32,6 +32,7 @@ public class ExpressionConstants { public static final QName VAR_PROJECTION = new QName(SchemaConstants.NS_C, "projection"); public static final QName VAR_SOURCE = new QName(SchemaConstants.NS_C, "source"); public static final QName VAR_ASSIGNMENT = new QName(SchemaConstants.NS_C, "assignment"); + public static final QName VAR_ASSIGNMENT_PATH = new QName(SchemaConstants.NS_C, "assignmentPath"); public static final QName VAR_IMMEDIATE_ASSIGNMENT = new QName(SchemaConstants.NS_C, "immediateAssignment"); public static final QName VAR_THIS_ASSIGNMENT = new QName(SchemaConstants.NS_C, "thisAssignment"); public static final QName VAR_FOCUS_ASSIGNMENT = new QName(SchemaConstants.NS_C, "focusAssignment"); diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectTypeUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectTypeUtil.java index 81aaf5cd947..a31e59cc211 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectTypeUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectTypeUtil.java @@ -627,7 +627,7 @@ public static boolean relationsEquivalent(QName relation1, QName relation2) { if (ObjectTypeUtil.isDefaultRelation(relation1)) { return ObjectTypeUtil.isDefaultRelation(relation2); } else { - return QNameUtil.match(relation2, relation1); + return QNameUtil.match(relation1, relation2); } } } \ No newline at end of file diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-certification-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-certification-3.xsd index df1762092cc..0dd7932d0c4 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-certification-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-certification-3.xsd @@ -691,6 +691,14 @@ + + + + Relation(s) which are to be considered. Value of q:any means "any relation". + If no relation is present, org:default (i.e. null) is assumed. + + + diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd index 683d6c49a43..afd10d41652 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd @@ -11202,13 +11202,13 @@ - + Constraint all assignments that have the same target. I.e. multiple assignments of the same (abstract) role. - If prohibitSameTarget=false then multiple assignments of the same role are allowed. - If prohibitSameTarget=true then multiple assignments of the same role are prohibited. + If allowSameTarget=true then multiple assignments of the same role are allowed. + If allowSameTarget=false then multiple assignments of the same role are prohibited. (but see also other constraints in this type) @@ -11216,13 +11216,12 @@ - + Constraint all assignments that have the same relation. - E.g. if prohibitSameTarget=true and prohibitSameRelation=false then multiple assignments + E.g. if allowSameTarget=true and allowSameRelation=false then multiple assignments of the same role are allowed as long as they have different relation. - If prohibitSameTarget=true and prohibitSameRelation=false 3.6 diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd index a41c796539f..8bbbd411a5a 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-policy-3.xsd @@ -192,7 +192,7 @@ - Relation(s) to which this constraint applies. All of these relation must match + Relation(s) to which this constraint applies. All of these relations must match the defined multiplicity. If no relation is present, org:default (i.e. null) is assumed. diff --git a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/handlers/DirectAssignmentCertificationHandler.java b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/handlers/DirectAssignmentCertificationHandler.java index fb1be209330..acc93d97474 100644 --- a/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/handlers/DirectAssignmentCertificationHandler.java +++ b/model/certification-impl/src/main/java/com/evolveum/midpoint/certification/impl/handlers/DirectAssignmentCertificationHandler.java @@ -20,6 +20,7 @@ import com.evolveum.midpoint.model.common.expression.ExpressionVariables; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.schema.constants.ExpressionConstants; +import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ActivationUtil; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; @@ -41,6 +42,7 @@ import javax.xml.namespace.QName; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; /** @@ -110,23 +112,25 @@ private void processAssignment(AssignmentType assignment, boolean isInducement, } else { throw new IllegalStateException("Unexpected targetRef type: " + assignment.getTargetRef().getType() + " in " + ObjectTypeUtil.toShortString(assignment)); } + valid = valid && relationMatches(assignment.getTargetRef().getRelation(), scope.getRelation()); } else if (assignment.getConstruction() != null) { assignmentCase.setTargetRef(assignment.getConstruction().getResourceRef()); valid = isIncludeResources(scope); } else { valid = false; // neither role/org/service nor resource assignment; ignored for now } - if (valid && isEnabledItemsOnly(scope) && !ActivationUtil.isAdministrativeEnabledOrNull(assignment.getActivation())) { - valid = false; - } - if (valid && !itemSelectionExpressionAccepts(assignment, isInducement, object, campaign, task, result)) { - valid = false; - } + valid = valid && (!isEnabledItemsOnly(scope) || ActivationUtil.isAdministrativeEnabledOrNull(assignment.getActivation())); + valid = valid && itemSelectionExpressionAccepts(assignment, isInducement, object, campaign, task, result); if (valid) { caseList.add(assignmentCase); } } + private boolean relationMatches(QName assignmentRelation, List scopeRelations) { + return (!scopeRelations.isEmpty() ? scopeRelations : Collections.singletonList(SchemaConstants.ORG_DEFAULT)) + .stream().anyMatch(r -> ObjectTypeUtil.relationMatches(r, assignmentRelation)); + } + private boolean itemSelectionExpressionAccepts(AssignmentType assignment, boolean isInducement, ObjectType object, AccessCertificationCampaignType campaign, Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException { AccessCertificationObjectBasedScopeType scope = null; if (campaign.getScopeDefinition() instanceof AccessCertificationObjectBasedScopeType) { diff --git a/model/certification-impl/src/test/resources/common/certification-of-critical-roles.xml b/model/certification-impl/src/test/resources/common/certification-of-critical-roles.xml index 542424cf4d6..220a471766c 100644 --- a/model/certification-impl/src/test/resources/common/certification-of-critical-roles.xml +++ b/model/certification-impl/src/test/resources/common/certification-of-critical-roles.xml @@ -55,15 +55,15 @@ jack->CTO none (A) -> A none (A) -> A | A true false false + default + approver diff --git a/model/certification-impl/src/test/resources/common/certification-of-eroot-user-assignments.xml b/model/certification-impl/src/test/resources/common/certification-of-eroot-user-assignments.xml index a239692cfbd..8f53a883276 100644 --- a/model/certification-impl/src/test/resources/common/certification-of-eroot-user-assignments.xml +++ b/model/certification-impl/src/test/resources/common/certification-of-eroot-user-assignments.xml @@ -33,6 +33,7 @@ + default diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelInteractionService.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelInteractionService.java index 8a6a37d4a1c..05d9680b53b 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelInteractionService.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelInteractionService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * 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. @@ -25,6 +25,7 @@ import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.statistics.ConnectorOperationalStatus; +import com.evolveum.midpoint.security.api.ItemSecurityDecisions; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.DisplayableValue; import com.evolveum.midpoint.util.exception.*; @@ -53,6 +54,7 @@ public interface ModelInteractionService { static final String PREVIEW_CHANGES = CLASS_NAME_WITH_DOT + "previewChanges"; static final String GET_EDIT_OBJECT_DEFINITION = CLASS_NAME_WITH_DOT + "getEditObjectDefinition"; static final String GET_EDIT_SHADOW_DEFINITION = CLASS_NAME_WITH_DOT + "getEditShadowDefinition"; + static final String GET_ALLOWED_REQUEST_ASSIGNMENT_ITEMS = CLASS_NAME_WITH_DOT + "getAllowedRequestAssignmentItems"; static final String GET_ASSIGNABLE_ROLE_SPECIFICATION = CLASS_NAME_WITH_DOT + "getAssignableRoleSpecification"; static final String GET_CREDENTIALS_POLICY = CLASS_NAME_WITH_DOT + "getCredentialsPolicy"; static final String GET_AUTHENTICATIONS_POLICY = CLASS_NAME_WITH_DOT + "getAuthenticationsPolicy"; @@ -63,6 +65,8 @@ public interface ModelInteractionService { static final String MERGE_OBJECTS_PREVIEW_DELTA = CLASS_NAME_WITH_DOT + "mergeObjectsPreviewDelta"; static final String MERGE_OBJECTS_PREVIEW_OBJECT = CLASS_NAME_WITH_DOT + "mergeObjectsPreviewObject"; + + /** * Computes the most likely changes triggered by the provided delta. The delta may be any change of any object, e.g. * add of a user or change of a shadow. The resulting context will sort that out to "focus" and "projection" as needed. @@ -137,6 +141,15 @@ ModelContext previewChanges( * @param focus Object of the operation. The object (usually user) to whom the roles should be assigned. */ RoleSelectionSpecification getAssignableRoleSpecification(PrismObject focus, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ConfigurationException; + + /** + * Returns decisions for individual items for "assign" authorization. This is usually applicable to assignment parameters. + * The decisions are evaluated using the security context of a currently logged-in user. + * + * @param object object of the operation (user) + * @param target target of the operation (role, org, service that is being assigned) + */ + ItemSecurityDecisions getAllowedRequestAssignmentItems(PrismObject object, PrismObject target) throws SchemaException, SecurityViolationException; SecurityPolicyType getSecurityPolicy(PrismObject user, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException; @@ -206,7 +219,7 @@ AccessCertificationConfigurationType getCertificationConfiguration(OperationResu * @return true if the password matches, false otherwise */ boolean checkPassword(String userOid, ProtectedStringType password, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException; - + // TEMPORARY List visualizeDeltas(List> deltas, Task task, OperationResult result) throws SchemaException; diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPathSegment.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPathSegment.java index 47703f5d152..ecc8b7466a6 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPathSegment.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/context/AssignmentPathSegment.java @@ -46,7 +46,19 @@ public interface AssignmentPathSegment extends DebugDumpable { ObjectType getTarget(); QName getRelation(); - + + /** + * Whether this assignment/inducement matches the focus level, i.e. if we should collect constructions, + * focus mappings, focus policy rules and similar items from it. + */ + boolean isMatchingOrder(); + + /** + * Whether this assignment/inducement matches the target level, i.e. if we should collect target + * policy rules from it. + */ + boolean isMatchingOrderForTarget(); + /** * True if the relation is a delegation one. */ diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java index cbf81dafb22..cf71e387305 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelInteractionServiceImpl.java @@ -62,6 +62,9 @@ import com.evolveum.midpoint.schema.result.OperationResultStatus; import com.evolveum.midpoint.schema.statistics.ConnectorOperationalStatus; import com.evolveum.midpoint.schema.util.ShadowUtil; +import com.evolveum.midpoint.security.api.Authorization; +import com.evolveum.midpoint.security.api.AuthorizationConstants; +import com.evolveum.midpoint.security.api.ItemSecurityDecisions; import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.security.api.ObjectSecurityConstraints; import com.evolveum.midpoint.security.api.SecurityEnforcer; @@ -360,6 +363,9 @@ public RefinedObjectClassDefinition getEditObjectClassDefinition(PrismObject ItemSecurityDecisions getAllowedRequestAssignmentItems(PrismObject object, PrismObject target) throws SchemaException, SecurityViolationException { + return securityEnforcer.getAllowedRequestAssignmentItems(securityEnforcer.getPrincipal(), object, target, null); + } @Override public Collection> getActionUrls() { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java index af46af47cc1..e4d65ddae80 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathSegmentImpl.java @@ -345,17 +345,15 @@ public void setProcessMembership(boolean processMembership) { this.processMembership = processMembership; } - /** - * Whether this assignment/inducement matches the focus level, i.e. if we should collect constructions, - * focus mappings, and focus policy rules from it. - */ + @Override public boolean isMatchingOrder() { if (isMatchingOrder == null) { isMatchingOrder = computeMatchingOrder(evaluationOrder, getAssignment()); } return isMatchingOrder; } - + + @Override public boolean isMatchingOrderForTarget() { if (isMatchingOrderForTarget == null) { isMatchingOrderForTarget = computeMatchingOrder(evaluationOrderForTarget, getAssignment()); @@ -468,6 +466,9 @@ public String toString() { } sb.append(": "); sb.append(source).append(" "); + if (!isAssignment) { + sb.append("inducement "); + } PrismContainer assignment = (PrismContainer) assignmentIdi.getAnyItem(); AssignmentType assignmentType = assignment != null ? assignment.getValue().asContainerable() : null; if (assignmentType != null) { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathVariables.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathVariables.java index e5d2316e9e0..8d6e7dba9e8 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathVariables.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentPathVariables.java @@ -27,13 +27,22 @@ * */ public class AssignmentPathVariables { - + + private AssignmentPathImpl assignmentPath; private ItemDeltaItem,PrismContainerDefinition> magicAssignment; private ItemDeltaItem,PrismContainerDefinition> immediateAssignment; private ItemDeltaItem,PrismContainerDefinition> thisAssignment; private ItemDeltaItem,PrismContainerDefinition> focusAssignment; private PrismObject immediateRole; + public AssignmentPathImpl getAssignmentPath() { + return assignmentPath; + } + + public void setAssignmentPath(AssignmentPathImpl assignmentPath) { + this.assignmentPath = assignmentPath; + } + public ItemDeltaItem,PrismContainerDefinition> getMagicAssignment() { return magicAssignment; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Clockwork.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Clockwork.java index 1fb967f7cd4..d11292bc90e 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Clockwork.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Clockwork.java @@ -1115,7 +1115,7 @@ private AuthorizationDecisionType evaluateCredentialDecision(ObjectSecurityConst ModelAuthorizationAction.CHANGE_CREDENTIALS.getUrl(), AuthorizationPhaseType.REQUEST); } - private void authorizeAssignmentRequest(String assignActionUrl, PrismObject object, + private void authorizeAssignmentRequest(String assignActionUrl, PrismObject object, OwnerResolver ownerResolver, Collection evaluatedAssignments, boolean prohibitPolicies, OperationResult result) throws SecurityViolationException, SchemaException { if (evaluatedAssignments == null) { return; @@ -1128,13 +1128,17 @@ private void authorizeAssignmentRequest(String assignActio securityEnforcer.failAuthorization("with assignment because of policies in the assignment", AuthorizationPhaseType.REQUEST, object, null, target, result); } } - if (securityEnforcer.isAuthorized(assignActionUrl, AuthorizationPhaseType.REQUEST, object, null, target, ownerResolver)) { + ObjectDelta assignmentObjectDelta = object.createModifyDelta(); + ContainerDelta assignmentDelta = assignmentObjectDelta.createContainerModification(FocusType.F_ASSIGNMENT); + // We do not care if this is add or delete. All that matters for authorization is that it is in a delta. + assignmentDelta.addValuesToAdd(evaluatedAssignment.getAssignmentType().asPrismContainerValue().clone()); + if (securityEnforcer.isAuthorized(assignActionUrl, AuthorizationPhaseType.REQUEST, object, assignmentObjectDelta, target, ownerResolver)) { LOGGER.trace("Operation authorized with {} authorization", assignActionUrl); continue; } QName relation = evaluatedAssignment.getRelation(); if (ObjectTypeUtil.isDelegationRelation(relation)) { - if (securityEnforcer.isAuthorized(ModelAuthorizationAction.DELEGATE.getUrl(), AuthorizationPhaseType.REQUEST, object, null, target, ownerResolver)) { + if (securityEnforcer.isAuthorized(ModelAuthorizationAction.DELEGATE.getUrl(), AuthorizationPhaseType.REQUEST, object, assignmentObjectDelta, target, ownerResolver)) { LOGGER.trace("Operation authorized with {} authorization", ModelAuthorizationAction.DELEGATE.getUrl()); continue; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Construction.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Construction.java index a14182a976a..9fdb271b608 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Construction.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/Construction.java @@ -328,7 +328,7 @@ private ResourceType resolveTarget(String sourceDescription, Task task, Operatio // SearchFilterType filter = targetRef.getFilter(); ExpressionVariables variables = Utils .getDefaultExpressionVariables(focusOdo.getNewObject().asObjectable(), null, null, null); - if (assignmentPathVariables == null){ + if (assignmentPathVariables == null) { assignmentPathVariables = LensUtil.computeAssignmentPathVariables(assignmentPath); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java index 833cf9b441c..3b7ce49d567 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java @@ -419,6 +419,13 @@ ItemDelta consolidateTripleToDelta( LOGGER.trace("Existing values for item {} in {}, weak mapping processing skipped", new Object[]{itemPath, contextDescription}); } + + if (itemExisting != null) { + List existingValues = itemExisting.getValues(); + if (existingValues != null) { + itemDelta.setEstimatedOldValues(PrismValue.cloneCollection(existingValues)); + } + } return itemDelta; @@ -868,6 +875,7 @@ public static AssignmentPathVariables computeAssignmentPathVariables(AssignmentP return null; } AssignmentPathVariables vars = new AssignmentPathVariables(); + vars.setAssignmentPath(assignmentPath.clone()); Iterator iterator = assignmentPath.getSegments().iterator(); while (iterator.hasNext()) { @@ -969,13 +977,16 @@ private static void mergeExtension(PrismContainer magicExtension, public static Mapping.Builder addAssignmentPathVariables(Mapping.Builder builder, AssignmentPathVariables assignmentPathVariables) { if (assignmentPathVariables != null ) { return builder + .addVariableDefinition(ExpressionConstants.VAR_ASSIGNMENT_PATH, assignmentPathVariables.getAssignmentPath()) .addVariableDefinition(ExpressionConstants.VAR_ASSIGNMENT, assignmentPathVariables.getMagicAssignment()) .addVariableDefinition(ExpressionConstants.VAR_IMMEDIATE_ASSIGNMENT, assignmentPathVariables.getImmediateAssignment()) .addVariableDefinition(ExpressionConstants.VAR_THIS_ASSIGNMENT, assignmentPathVariables.getThisAssignment()) .addVariableDefinition(ExpressionConstants.VAR_FOCUS_ASSIGNMENT, assignmentPathVariables.getFocusAssignment()) .addVariableDefinition(ExpressionConstants.VAR_IMMEDIATE_ROLE, assignmentPathVariables.getImmediateRole()); } else { - return builder; + // to avoid "no such variable" exceptions in boundary cases + // for null/empty paths we might consider creating empty AssignmentPathVariables objects to keep null/empty path distinction + return builder.addVariableDefinition(ExpressionConstants.VAR_ASSIGNMENT_PATH, (Object) null); } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PolicyRuleProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PolicyRuleProcessor.java index f4e4344f629..a94e12e8793 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PolicyRuleProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PolicyRuleProcessor.java @@ -308,59 +308,101 @@ private void checkAssigneeConstraints(LensContext conte private void checkAssigneeConstraints(LensContext context, EvaluatedAssignment assignment, PlusMinusZero plusMinus, OperationResult result) throws PolicyViolationException, SchemaException { PrismObject target = assignment.getTarget(); - if (target != null) { - Objectable targetType = target.asObjectable(); - if (targetType instanceof AbstractRoleType) { - Collection policyRules = assignment.getThisTargetPolicyRules(); - for (EvaluatedPolicyRule policyRule: policyRules) { - PolicyConstraintsType policyConstraints = policyRule.getPolicyConstraints(); - if (policyConstraints != null && (!policyConstraints.getMinAssignees().isEmpty() || !policyConstraints.getMaxAssignees().isEmpty())) { - String focusOid = null; - if (context.getFocusContext() != null) { - focusOid = context.getFocusContext().getOid(); - } - int numberOfAssigneesExceptMyself = countAssignees((PrismObject)target, focusOid, result); - if (plusMinus == PlusMinusZero.PLUS) { - numberOfAssigneesExceptMyself++; - } - for (MultiplicityPolicyConstraintType constraint: policyConstraints.getMinAssignees()) { - Integer multiplicity = XsdTypeMapper.multiplicityToInteger(constraint.getMultiplicity()); - // Complain only if the situation is getting worse - if (multiplicity >= 0 && numberOfAssigneesExceptMyself < multiplicity && plusMinus == PlusMinusZero.MINUS) { - EvaluatedPolicyRuleTrigger trigger = new EvaluatedPolicyRuleTrigger(PolicyConstraintKindType.MIN_ASSIGNEES, - constraint, ""+target+" requires at least "+multiplicity+ - " assignees. The operation would result in "+numberOfAssigneesExceptMyself+" assignees."); - assignment.triggerConstraint(policyRule, trigger); - - } - } - for (MultiplicityPolicyConstraintType constraint: policyConstraints.getMaxAssignees()) { - Integer multiplicity = XsdTypeMapper.multiplicityToInteger(constraint.getMultiplicity()); - // Complain only if the situation is getting worse - if (multiplicity >= 0 && numberOfAssigneesExceptMyself > multiplicity && plusMinus == PlusMinusZero.PLUS) { - EvaluatedPolicyRuleTrigger trigger = new EvaluatedPolicyRuleTrigger(PolicyConstraintKindType.MAX_ASSIGNEES, - constraint, ""+target+" requires at most "+multiplicity+ - " assignees. The operation would result in "+numberOfAssigneesExceptMyself+" assignees."); - assignment.triggerConstraint(policyRule, trigger); - - } - } - } + if (target == null || !(target.asObjectable() instanceof AbstractRoleType)) { + return; + } + AbstractRoleType targetRole = (AbstractRoleType) target.asObjectable(); + QName relation = ObjectTypeUtil.normalizeRelation(assignment.getRelation()); + Collection policyRules = assignment.getThisTargetPolicyRules(); + for (EvaluatedPolicyRule policyRule: policyRules) { + PolicyConstraintsType policyConstraints = policyRule.getPolicyConstraints(); + if (policyConstraints == null) { + continue; + } + List relevantMinAssignees = getForRelation(policyConstraints.getMinAssignees(), relation); + List relevantMaxAssignees = getForRelation(policyConstraints.getMaxAssignees(), relation); + if (relevantMinAssignees.isEmpty() && relevantMaxAssignees.isEmpty()) { + continue; + } + String focusOid = null; + if (context.getFocusContext() != null) { + focusOid = context.getFocusContext().getOid(); + } + int currentAssigneesExceptMyself = getNumberOfAssigneesExceptMyself(targetRole, focusOid, relation, result); + for (MultiplicityPolicyConstraintType constraint: relevantMinAssignees) { + Integer requiredMultiplicity = XsdTypeMapper.multiplicityToInteger(constraint.getMultiplicity()); + if (requiredMultiplicity <= 0) { + continue; // unbounded or 0 + } + // Complain only if the situation is getting worse + if (currentAssigneesExceptMyself < requiredMultiplicity && plusMinus == PlusMinusZero.MINUS) { + EvaluatedPolicyRuleTrigger trigger = + new EvaluatedPolicyRuleTrigger<>(PolicyConstraintKindType.MIN_ASSIGNEES, + constraint, target+" requires at least " + requiredMultiplicity + + " assignees with the relation of '" + relation.getLocalPart() + + "'. The operation would result in "+currentAssigneesExceptMyself+" assignees."); + assignment.triggerConstraint(policyRule, trigger); + } + } + for (MultiplicityPolicyConstraintType constraint: relevantMaxAssignees) { + Integer requiredMultiplicity = XsdTypeMapper.multiplicityToInteger(constraint.getMultiplicity()); + if (requiredMultiplicity < 0) { + continue; // unbounded + } + // Complain only if the situation is getting worse + if (currentAssigneesExceptMyself >= requiredMultiplicity && plusMinus == PlusMinusZero.PLUS) { + EvaluatedPolicyRuleTrigger trigger = + new EvaluatedPolicyRuleTrigger<>(PolicyConstraintKindType.MAX_ASSIGNEES, + constraint, target + " requires at most " + requiredMultiplicity + + " assignees with the relation of '" + relation.getLocalPart() + + "'. The operation would result in " + (currentAssigneesExceptMyself+1) + " assignees."); + assignment.triggerConstraint(policyRule, trigger); } } } } - private int countAssignees(PrismObject target, String selfOid, OperationResult result) throws SchemaException { + private List getForRelation(List all, QName relation) { + return all.stream() + .filter(c -> containsRelation(c, relation)) + .collect(Collectors.toList()); + } + + private boolean containsRelation(MultiplicityPolicyConstraintType constraint, QName relation) { + return getConstraintRelations(constraint).stream() + .anyMatch(constraintRelation -> ObjectTypeUtil.relationMatches(constraintRelation, relation)); + } + + private List getConstraintRelations(MultiplicityPolicyConstraintType constraint) { + return !constraint.getRelation().isEmpty() ? + constraint.getRelation() : + Collections.singletonList(SchemaConstants.ORG_DEFAULT); + } + + /** + * Returns numbers of assignees with the given relation name. + */ + private int getNumberOfAssigneesExceptMyself(AbstractRoleType target, String selfOid, QName relation, OperationResult result) + throws SchemaException { S_AtomicFilterExit q = QueryBuilder.queryFor(FocusType.class, prismContext) .item(FocusType.F_ASSIGNMENT, AssignmentType.F_TARGET_REF).ref(target.getOid()); if (selfOid != null) { q = q.and().not().id(selfOid); } ObjectQuery query = q.build(); - return repositoryService.countObjects(FocusType.class, query, result); + List> assignees = repositoryService.searchObjects(FocusType.class, query, null, result); + int count = 0; + assignee: for (PrismObject assignee : assignees) { + for (AssignmentType assignment : assignee.asObjectable().getAssignment()) { + if (assignment.getTargetRef() != null + && ObjectTypeUtil.relationsEquivalent(relation, assignment.getTargetRef().getRelation())) { + count++; + continue assignee; + } + } + } + return count; } - public boolean processPruning(LensContext context, DeltaSetTriple> evaluatedAssignmentTriple, diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java index ba3bdbb4691..372a82b3e1f 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java @@ -16,6 +16,7 @@ package com.evolveum.midpoint.model.impl.lens; import com.evolveum.midpoint.common.Clock; +import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext; import com.evolveum.midpoint.model.impl.lens.projector.AssignmentProcessor; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.delta.ObjectDelta; @@ -23,6 +24,7 @@ import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; +import com.evolveum.midpoint.schema.constants.ExpressionConstants; import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; @@ -34,11 +36,14 @@ import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; import org.apache.commons.collections4.Bag; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.collections4.bag.TreeBag; +import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.testng.annotations.Test; +import javax.xml.namespace.QName; import java.io.File; import java.util.*; import java.util.function.Function; @@ -49,6 +54,7 @@ import static com.evolveum.midpoint.test.IntegrationTestTools.displayObjectTypeCollection; import static com.evolveum.midpoint.test.util.TestUtil.assertSuccess; import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNull; /** * Comprehensive test of assignment evaluator and processor. @@ -92,6 +98,7 @@ public class TestAssignmentProcessor2 extends AbstractLensTest { private static final boolean FIRST_PART = true; private static final boolean SECOND_PART = true; + private static final boolean THIRD_PART = true; private static final File RESOURCE_DUMMY_EMPTY_FILE = new File(TEST_DIR, "resource-dummy-empty.xml"); private static final String RESOURCE_DUMMY_EMPTY_OID = "10000000-0000-0000-0000-00000000EEE4"; @@ -111,11 +118,17 @@ public class TestAssignmentProcessor2 extends AbstractLensTest { private RoleType metarole7, metarole8, metarole9; private RoleType metametarole7; + // third part + private RoleType rolePirate, roleSailor, roleMan, roleWoman, roleHuman; + private RoleType metaroleCrewMember, metarolePerson; + private List objects; private static final String ROLE_R1_OID = getRoleOid("R1"); private static final String ROLE_R7_OID = getRoleOid("R7"); private static final String ROLE_MR1_OID = getRoleOid("MR1"); + private static final String ROLE_PIRATE_OID = getRoleOid("Pirate"); + private static final String ROLE_MAN_OID = getRoleOid("Man"); @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { @@ -458,6 +471,85 @@ public void test060JackDeputyOfGuybrushDeputyOfBarbossa() throws Exception { assertGuiConfig(evaluatedAssignment, "R1 R2 O3 R4 R5 R6"); } + /** + * MMR1 -----------I------------------------------* + * ^ | + * | I + * | V + * MR1 -----------I-------------*-----> MR3 MR4 + * ^ MR2 --I---* | | | + * | ^ I I I I + * | | V V V V + * R1 --I--> R2 O3 R4 R5 R6 + * ^ + * A + * | + * jack --D--> barbossa + * + * (D = deputy assignment) (A = approver) + * + */ + @Test(enabled = FIRST_PART) + public void test070JackDeputyOfBarbossaApproverOfR1() throws Exception { + final String TEST_NAME = "test070JackDeputyOfBarbossaApproverOfR1"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + unassignAllRoles(USER_JACK_OID); + unassignAllRoles(USER_GUYBRUSH_OID); + unassignAllRoles(USER_BARBOSSA_OID); + + // barbossa has a policy rule barbossa-0 from test050 + assignRole(USER_BARBOSSA_OID, ROLE_R1_OID, SchemaConstants.ORG_APPROVER, task, result); + + display("barbossa", getUser(USER_BARBOSSA_OID)); + + LensContext context = createContextForAssignment(UserType.class, USER_JACK_OID, UserType.class, USER_BARBOSSA_OID, + SchemaConstants.ORG_DEPUTY, null, result); + + // WHEN + assignmentProcessor.processAssignmentsProjections(context, clock.currentTimeXMLGregorianCalendar(), task, result); + + // THEN + display("Output context", context); + display("Evaluated assignment triple", context.getEvaluatedAssignmentTriple()); + + result.computeStatus(); + assertSuccess("Assignment processor failed (result)", result); + + Collection> evaluatedAssignments = assertAssignmentTripleSetSize(context, 0, 1, 0); + EvaluatedAssignmentImpl evaluatedAssignment = evaluatedAssignments.iterator().next(); + assertEquals("Wrong evaluatedAssignment.isValid", true, evaluatedAssignment.isValid()); + + assertTargets(evaluatedAssignment, true, "", null, null, null, null, null); + assertTargets(evaluatedAssignment, false, "barbossa R1 R2 O3 R4 R5 R6 MR1 MR2 MR3 MR4 MMR1", null, null, null, null, null); + assertMembershipRef(evaluatedAssignment, ""); + assertOrgRef(evaluatedAssignment, ""); + assertDelegation(evaluatedAssignment, "barbossa R1"); + PrismReferenceValue barbossaRef = evaluatedAssignment.getDelegationRefVals().stream() + .filter(v -> USER_BARBOSSA_OID.equals(v.getOid())).findFirst().orElseThrow( + () -> new AssertionError("No barbossa ref in delegation ref vals")); + assertEquals("Wrong relation for barbossa delegation", SchemaConstants.ORG_DEPUTY, barbossaRef.getRelation()); + PrismReferenceValue r1Ref = evaluatedAssignment.getDelegationRefVals().stream() + .filter(v -> ROLE_R1_OID.equals(v.getOid())).findFirst().orElseThrow( + () -> new AssertionError("No R1 ref in delegation ref vals")); + assertEquals("Wrong relation for R1 delegation", SchemaConstants.ORG_APPROVER, r1Ref.getRelation()); + + // Constructions are named "role-level". We expect e.g. that from R1 we get a construction induced with order=1 (R1-1). + assertConstructions(evaluatedAssignment, "Brethren_account_construction Undead_monkey_account_construction", null, null, null, null, null); + assertFocusMappings(evaluatedAssignment, ""); + assertFocusPolicyRules(evaluatedAssignment, "barbossa-0"); + + // Rules for other targets are empty, which is very probably OK. All rules are bound to target "barbossa". + // There is no alternative target, as barbossa does not induce anything. + assertTargetPolicyRules(evaluatedAssignment, "barbossa-0", ""); + assertAuthorizations(evaluatedAssignment, ""); + assertGuiConfig(evaluatedAssignment, ""); + } + /** * Now disable some roles. Their administrative status is simply set to DISABLED. @@ -839,6 +931,474 @@ public void test300AssignR7ToJack() throws Exception { assertAuthorizations(evaluatedAssignment, "R7 R8 R9"); assertGuiConfig(evaluatedAssignment, "R7 R8 R9"); } + + /** + * Testing assignment path variables + * + * MetaroleCrewMember (C3) -----I-----> MetarolePerson (C4) --I--+ + * ^ ^ ^ ^ | + * | | | | | + * | | | | V + * (C1) Pirate --I--> Sailor (C2) Man Woman Human (C5) + * ^ | + * | +----------------------------------+ + * | | (added later) + * jack + * + * Assume these constructions: + * - C1 giving each Pirate some account (attached via inducement) + * - C2 giving each Sailor some account (attached via inducement) + * - C3 giving each crew member some account (attached via inducement of order 2) + * - C4 giving each person some account (attached via inducement of order 2) + * - C5 giving each Human some account (attached via inducement) + */ + + @Test(enabled = THIRD_PART) + public void test400AssignJackPirate() throws Exception { + final String TEST_NAME = "test400AssignJackPirate"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestAssignmentProcessor.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + createObjectsInThirdPart(false, task, result, () -> { + + createCustomConstruction(rolePirate, "C1", 1); + createCustomConstruction(roleSailor, "C2", 1); + createCustomConstruction(metaroleCrewMember, "C3", 2); + createCustomConstruction(metarolePerson, "C4", 2); + createCustomConstruction(roleHuman, "C5", 1); + + addConditionToRoles("Pirate! Sailor! MetaroleCrewMember! MetarolePerson! Man! Human!"); + addConditionToAssignments("Pirate-Sailor! Pirate-MetaroleCrewMember! Sailor-MetaroleCrewMember! MetaroleCrewMember-MetarolePerson! Man-MetarolePerson! MetarolePerson-Human!"); + }); + + LensContext context = createContextForRoleAssignment(USER_JACK_OID, ROLE_PIRATE_OID, null, null, result); + + // WHEN + recording = true; + assignmentProcessor.processAssignmentsProjections(context, clock.currentTimeXMLGregorianCalendar(), task, result); + recording = false; + + // THEN + display("Output context", context); + display("Evaluated assignment triple", context.getEvaluatedAssignmentTriple()); + + result.computeStatus(); + assertSuccess("Assignment processor failed (result)", result); + + Collection> evaluatedAssignments = assertAssignmentTripleSetSize(context, 0, 1, 0); + EvaluatedAssignmentImpl evaluatedAssignment = evaluatedAssignments.iterator().next(); + assertEquals("Wrong evaluatedAssignment.isValid", true, evaluatedAssignment.isValid()); + + assertTargets(evaluatedAssignment, true, "Pirate Sailor Human Human", null, null, null, null, null); + assertTargets(evaluatedAssignment, false, "MetaroleCrewMember MetaroleCrewMember MetarolePerson MetarolePerson", null, null, null, null, null); + assertMembershipRef(evaluatedAssignment, "Pirate Sailor Human"); + assertOrgRef(evaluatedAssignment, ""); + assertDelegation(evaluatedAssignment, ""); + + String expectedItems = "Pirate-1 Sailor-1 MetaroleCrewMember-2 MetaroleCrewMember-2 MetarolePerson-2 MetarolePerson-2 Human-1 Human-1"; + assertConstructions(evaluatedAssignment, expectedItems + " C1 C2 C3 C3 C4 C4 C5 C5", null, null, null, null, null); + assertFocusMappings(evaluatedAssignment, expectedItems); + assertFocusPolicyRules(evaluatedAssignment, expectedItems); + + assertTargetPolicyRules(evaluatedAssignment, + "Pirate-0 MetaroleCrewMember-1 MetarolePerson-1", + "Sailor-0 MetaroleCrewMember-1 MetarolePerson-1 Human-0 Human-0"); + assertAuthorizations(evaluatedAssignment, "Pirate Sailor Human"); + assertGuiConfig(evaluatedAssignment, "Pirate Sailor Human"); + + dump("Pirate!", 0); + dump("Sailor!", 0); + dump("MetaroleCrewMember!", 1); + dump("MetaroleCrewMember!", 0); + dump("MetarolePerson!", 1); + dump("MetarolePerson!", 0); + dump("Human!", 1); + dump("Human!", 0); + + dump("Pirate-Sailor!", 0); + dump("Pirate-MetaroleCrewMember!", 0); + dump("Sailor-MetaroleCrewMember!", 0); + dump("MetaroleCrewMember-MetarolePerson!", 1); + dump("MetaroleCrewMember-MetarolePerson!", 0); + dump("MetarolePerson-Human!", 1); + dump("MetarolePerson-Human!", 0); + + dump("C1", 0); + dump("C2", 0); + dump("C3", 1); + dump("C3", 0); + dump("C4", 1); + dump("C4", 0); + dump("C5", 1); + dump("C5", 0); + + runs.values().forEach(r -> r.assertFocus("jack").assertFocusAssignment("->Pirate")); + + getRunInfo("Pirate!", 0) + .assertThisAssignment("->Pirate") + .assertImmediateAssignment(null) + .assertSource("jack") + .assertImmediateRole(null) + .assertAssignmentPath(1); + + getRunInfo("Sailor!", 0) + .assertThisAssignment("->Sailor") + .assertImmediateAssignment("->Pirate") + .assertSource("Pirate") + .assertImmediateRole(null) + .assertAssignmentPath(2); + + // swap indices if internals of evaluator change and "Pirate" branch is executed first + getRunInfo("MetaroleCrewMember!", 1) + .assertThisAssignment("->MetaroleCrewMember") + .assertImmediateAssignment("->Pirate") + .assertSource("Pirate") + .assertImmediateRole(null) + .assertAssignmentPath(2); + + getRunInfo("MetaroleCrewMember!", 0) + .assertThisAssignment("->MetaroleCrewMember") + .assertImmediateAssignment("->Sailor") + .assertSource("Sailor") + .assertImmediateRole("Pirate") + .assertAssignmentPath(3); + + getRunInfo("MetarolePerson!", 1) + .assertThisAssignment("->MetarolePerson") + .assertImmediateAssignment("->MetaroleCrewMember") + .assertSource("MetaroleCrewMember") + .assertImmediateRole("Pirate") + .assertAssignmentPath(3); + + getRunInfo("MetarolePerson!", 0) + .assertThisAssignment("->MetarolePerson") + .assertImmediateAssignment("->MetaroleCrewMember") + .assertSource("MetaroleCrewMember") + .assertImmediateRole("Sailor") + .assertAssignmentPath(4); + + getRunInfo("Human!", 1) + .assertThisAssignment("->Human") + .assertImmediateAssignment("->MetarolePerson") + .assertSource("MetarolePerson") + .assertImmediateRole("MetaroleCrewMember") + .assertAssignmentPath(4); + + getRunInfo("Human!", 0) + .assertThisAssignment("->Human") + .assertImmediateAssignment("->MetarolePerson") + .assertSource("MetarolePerson") + .assertImmediateRole("MetaroleCrewMember") + .assertAssignmentPath(5); + + // assignments + + getRunInfo("Pirate-Sailor!", 0) + .assertThisAssignment("->Sailor") + .assertImmediateAssignment("->Pirate") + .assertSource("Pirate") + .assertImmediateRole(null) + .assertAssignmentPath(2); + + getRunInfo("Pirate-MetaroleCrewMember!", 0) + .assertThisAssignment("->MetaroleCrewMember") + .assertImmediateAssignment("->Pirate") + .assertSource("Pirate") + .assertImmediateRole(null) + .assertAssignmentPath(2); + + getRunInfo("Sailor-MetaroleCrewMember!", 0) + .assertThisAssignment("->MetaroleCrewMember") + .assertImmediateAssignment("->Sailor") + .assertSource("Sailor") + .assertImmediateRole("Pirate") + .assertAssignmentPath(3); + + getRunInfo("MetaroleCrewMember-MetarolePerson!", 1) + .assertThisAssignment("->MetarolePerson") + .assertImmediateAssignment("->MetaroleCrewMember") + .assertSource("MetaroleCrewMember") + .assertImmediateRole("Pirate") + .assertAssignmentPath(3); + + getRunInfo("MetaroleCrewMember-MetarolePerson!", 0) + .assertThisAssignment("->MetarolePerson") + .assertImmediateAssignment("->MetaroleCrewMember") + .assertSource("MetaroleCrewMember") + .assertImmediateRole("Sailor") + .assertAssignmentPath(4); + + getRunInfo("MetarolePerson-Human!", 1) + .assertThisAssignment("->Human") + .assertImmediateAssignment("->MetarolePerson") + .assertSource("MetarolePerson") + .assertImmediateRole("MetaroleCrewMember") + .assertAssignmentPath(4); + + getRunInfo("MetarolePerson-Human!", 0) + .assertThisAssignment("->Human") + .assertImmediateAssignment("->MetarolePerson") + .assertSource("MetarolePerson") + .assertImmediateRole("MetaroleCrewMember") + .assertAssignmentPath(5); + + getRunInfo("C1", 0) + .assertImmediateAssignment("->Pirate") + .assertSource("Pirate") + .assertThisObject("Pirate") + .assertImmediateRole(null) + .assertAssignmentPath(2); + + getRunInfo("C2", 0) + .assertImmediateAssignment("->Sailor") + .assertSource("Sailor") + .assertThisObject("Sailor") + .assertImmediateRole("Pirate") + .assertAssignmentPath(3); + + getRunInfo("C3", 1) + .assertImmediateAssignment("->MetaroleCrewMember") + .assertSource("MetaroleCrewMember") + .assertThisObject("Pirate") + .assertImmediateRole("Pirate") + .assertAssignmentPath(3); + + getRunInfo("C3", 0) + .assertImmediateAssignment("->MetaroleCrewMember") + .assertSource("MetaroleCrewMember") + .assertThisObject("Sailor") + .assertImmediateRole("Sailor") + .assertAssignmentPath(4); + + getRunInfo("C4", 1) + .assertImmediateAssignment("->MetarolePerson") + .assertSource("MetarolePerson") + .assertThisObject("MetaroleCrewMember") // TODO + .assertImmediateRole("MetaroleCrewMember") + .assertAssignmentPath(4); + + getRunInfo("C4", 0) + .assertImmediateAssignment("->MetarolePerson") + .assertSource("MetarolePerson") + .assertThisObject("MetaroleCrewMember") // TODO + .assertImmediateRole("MetaroleCrewMember") + .assertAssignmentPath(5); + + getRunInfo("C5", 1) + .assertImmediateAssignment("->Human") + .assertSource("Human") + .assertThisObject("Human") + .assertImmediateRole("MetarolePerson") + .assertAssignmentPath(5); + + getRunInfo("C5", 0) + .assertImmediateAssignment("->Human") + .assertSource("Human") + .assertThisObject("Human") + .assertImmediateRole("MetarolePerson") + .assertAssignmentPath(6); + } + + private void dump(String runName, int index) { + System.out.println(getRunInfo(runName, index).dump()); + } + + private RunInfo getRunInfo(String runName, int index) { + return getRunInfos(runName).stream().filter(r -> r.index == index) + .findFirst() + .orElseThrow(() -> new AssertionError("No run " + runName + " with index " + index)); + } + + private Collection getRunInfos(String name) { + return CollectionUtils.emptyIfNull(runs.get(name)); + } + + private RunInfo getRunInfo(String name) { + Collection runs = getRunInfos(name); + assertEquals("Wrong # of run infos for " + name, 1, runs.size()); + return runs.iterator().next(); + } + + private void showEvaluations(EvaluatedAssignmentImpl evaluatedAssignment, String name, int expectedConstructions, Task task, OperationResult result) + throws Exception { + List> constructions = getConstructions(evaluatedAssignment, name); + assertEquals("Wrong # of constructions: " + name, expectedConstructions, constructions.size()); + for (int i = 0; i < constructions.size(); i++) { + Construction construction = constructions.get(i); + System.out.println("Evaluating " + name + " #" + (i+1)); + construction.evaluate(task, result); + System.out.println("Done"); + } + } + + + private static boolean recording() { + return recording && ScriptExpressionEvaluationContext.getThreadLocal().isEvaluateNew(); + } + + private static class RunInfo { + private String name; + private int index; + private AssignmentType assignment; + private AssignmentType thisAssignment; + private AssignmentType immediateAssignment; + private AssignmentType focusAssignment; + private FocusType source; + private FocusType immediateRole; + private FocusType thisObject; + private FocusType focus; + private AssignmentPathImpl assignmentPath; + private String dump() { + String p = name + "#" + (index+1); + StringBuilder sb = new StringBuilder(); + sb.append("------------------------------------------------------------------------------------\n"); + sb.append(p).append(" ").append("assignment: ").append(assignment).append("\n"); + sb.append(p).append(" ").append("thisAssignment: ").append(thisAssignment).append("\n"); + sb.append(p).append(" ").append("immediateAssignment: ").append(immediateAssignment).append("\n"); + sb.append(p).append(" ").append("focusAssignment: ").append(focusAssignment).append("\n"); + sb.append(p).append(" ").append("source: ").append(source).append("\n"); + sb.append(p).append(" ").append("immediateRole: ").append(immediateRole).append("\n"); + sb.append(p).append(" ").append("thisObject: ").append(thisObject).append("\n"); + sb.append(p).append(" ").append("focus: ").append(focus).append("\n"); + sb.append(p).append(" ").append("assignmentPath: ").append(shortString(assignmentPath)).append("\n"); + return sb.toString(); + } + + private String shortString(AssignmentPathImpl assignmentPath) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (AssignmentPathSegmentImpl segment : assignmentPath.getSegments()) { + if (first) { + first = false; + } else { + sb.append("; "); + } + sb.append(segment.getSource().getName()); + if (segment.getTarget() != null) { + sb.append(" -> ").append(segment.getTarget().getName()); + } + } + return sb.toString(); + } + + public RunInfo assertThisAssignment(String name) { + return assertAssignment(thisAssignment, name); + } + + private RunInfo assertAssignment(AssignmentType assignment, String name) { + if (name == null) { + assertNull("Assignment is not null", assignment); + } else { + name = name.substring(2); + assertEquals("Wrong target OID in assignment", getRoleOid(name), assignment.getTargetRef().getOid()); + } + return this; + } + + public RunInfo assertImmediateAssignment(String name) { + return assertAssignment(immediateAssignment, name); + } + + public RunInfo assertFocusAssignment(String name) { + return assertAssignment(focusAssignment, name); + } + + public RunInfo assertSource(String name) { + return assertObject(source, name); + } + + public RunInfo assertThisObject(String name) { + return assertObject(thisObject, name); + } + + public RunInfo assertFocus(String name) { + return assertObject(focus, name); + } + + public RunInfo assertImmediateRole(String name) { + return assertObject(immediateRole, name); + } + + public RunInfo assertObject(ObjectType object, String name) { + if (name == null) { + assertNull("Object is not null", object); + } else { + assertEquals("Wrong object", name, object.getName().getOrig()); + } + return this; + } + + public RunInfo assertAssignmentPath(int expected) { + assertEquals("Wrong length of assignmentPath", expected, assignmentPath.size()); + return this; + } + } + + private static boolean recording = false; + + private static MultiValuedMap runs = new ArrayListValuedHashMap<>(); + + private static RunInfo currentRun; + + // called from the script + public static void startCallback(String desc) { + if (recording()) { + System.out.println("Starting execution: " + desc); + currentRun = new RunInfo(); + currentRun.name = desc; + currentRun.index = CollectionUtils.emptyIfNull(runs.get(desc)).size(); + } + } + + private static final List RECORDED_VARIABLES = Arrays.asList( + ExpressionConstants.VAR_ASSIGNMENT.getLocalPart(), + ExpressionConstants.VAR_FOCUS_ASSIGNMENT.getLocalPart(), + ExpressionConstants.VAR_IMMEDIATE_ASSIGNMENT.getLocalPart(), + ExpressionConstants.VAR_THIS_ASSIGNMENT.getLocalPart(), + ExpressionConstants.VAR_FOCUS.getLocalPart(), + ExpressionConstants.VAR_ORDER_ONE_OBJECT.getLocalPart(), + ExpressionConstants.VAR_IMMEDIATE_ROLE.getLocalPart(), + ExpressionConstants.VAR_SOURCE.getLocalPart(), + ExpressionConstants.VAR_ASSIGNMENT_PATH.getLocalPart()); + + @SuppressWarnings("unchecked") + public static void variableCallback(String name, Object value, String desc) { + if (recording()) { + if (RECORDED_VARIABLES.contains(name)) { + System.out.println(desc + ": name = " + name + ", value = " + value); + if (ExpressionConstants.VAR_ASSIGNMENT.getLocalPart().equals(name)) { + currentRun.assignment = (AssignmentType) value; + } else if (ExpressionConstants.VAR_FOCUS_ASSIGNMENT.getLocalPart().equals(name)) { + currentRun.focusAssignment = (AssignmentType) value; + } else if (ExpressionConstants.VAR_IMMEDIATE_ASSIGNMENT.getLocalPart().equals(name)) + currentRun.immediateAssignment = (AssignmentType) value; + else if (ExpressionConstants.VAR_THIS_ASSIGNMENT.getLocalPart().equals(name)) { + currentRun.thisAssignment = (AssignmentType) value; + } else if (ExpressionConstants.VAR_FOCUS.getLocalPart().equals(name)) { + currentRun.focus = (FocusType) value; + } else if (ExpressionConstants.VAR_ORDER_ONE_OBJECT.getLocalPart().equals(name)) { + currentRun.thisObject = (FocusType) value; + } else if (ExpressionConstants.VAR_IMMEDIATE_ROLE.getLocalPart().equals(name)) { + currentRun.immediateRole = (FocusType) value; + } else if (ExpressionConstants.VAR_SOURCE.getLocalPart().equals(name)) { + currentRun.source = (FocusType) value; + } else if (ExpressionConstants.VAR_ASSIGNMENT_PATH.getLocalPart().equals(name)) { + currentRun.assignmentPath = (AssignmentPathImpl) value; + } + } + } + } + + public static void finishCallback(String desc) { + if (recording()) { + System.out.println("Finishing execution: " + desc); + runs.put(desc, currentRun); + } + } + //region ============================================================= helper methods (preparing scenarios) private void createObjectsInFirstPart(boolean deleteFirst, Task task, OperationResult result, Runnable adjustment) throws Exception { @@ -894,6 +1454,56 @@ private void createObjectsInSecondPart(boolean deleteFirst, Task task, Operation createObjects(deleteFirst, task, result, adjustment); } + private void createObjectsInThirdPart(boolean deleteFirst, Task task, OperationResult result, Runnable adjustment) throws Exception { + rolePirate = createRole("Pirate"); + rolePirate.setDelegable(true); + roleSailor = createRole("Sailor"); + metaroleCrewMember = createRole("MetaroleCrewMember"); + metarolePerson = createRole("MetarolePerson"); + roleMan = createRole("Man"); + roleWoman = createRole("Woman"); + roleHuman = createRole("Human"); + + assign(rolePirate, metaroleCrewMember); + assign(roleSailor, metaroleCrewMember); + induce(rolePirate, roleSailor, 1); + assign(roleMan, metarolePerson); + assign(roleWoman, metarolePerson); + induce(metaroleCrewMember, metarolePerson, 1); + induce(metarolePerson, roleHuman, 2); + + objects = new ArrayList<>( + Arrays.asList(rolePirate, roleSailor, metaroleCrewMember, metarolePerson, roleMan, roleWoman, roleHuman)); + + createObjects(deleteFirst, task, result, adjustment); + } + + private void createCustomConstruction(RoleType role, String name, int order) { + ConstructionType c = new ConstructionType(prismContext); + c.setDescription(name); + c.setResourceRef(ObjectTypeUtil.createObjectRef(RESOURCE_DUMMY_EMPTY_OID, ObjectTypes.RESOURCE)); + ResourceAttributeDefinitionType nameDef = new ResourceAttributeDefinitionType(); + nameDef.setRef(new ItemPath(new QName(SchemaConstants.NS_ICF_SCHEMA, "name")).asItemPathType()); + MappingType outbound = new MappingType(); + outbound.setName(name); + ExpressionType expression = new ExpressionType(); + ScriptExpressionEvaluatorType script = new ScriptExpressionEvaluatorType(); + script.setCode( + "import com.evolveum.midpoint.model.impl.lens.TestAssignmentProcessor2\n\n" + + "TestAssignmentProcessor2.startCallback('" + name + "')\n" + + "this.binding.variables.each {k,v -> TestAssignmentProcessor2.variableCallback(k, v, '" + name + "')}\n" + + "TestAssignmentProcessor2.finishCallback('" + name + "')\n" + + "return null"); + expression.getExpressionEvaluator().add(new ObjectFactory().createScript(script)); + outbound.setExpression(expression); + nameDef.setOutbound(outbound); + c.getAttribute().add(nameDef); + AssignmentType a = new AssignmentType(prismContext); + a.setDescription("Assignment for " + c.getDescription()); + a.setConstruction(c); + addAssignmentOrInducement(role, order, a); + } + private void createObjects(boolean deleteFirst, Task task, OperationResult result, Runnable adjustment) throws Exception { if (adjustment != null) { adjustment.run(); @@ -938,7 +1548,7 @@ private void addConditionToRoles(String text) { String name = StringUtils.substring(item, 0, -1); char conditionType = item.charAt(item.length() - 1); AbstractRoleType role = findRole(name); - role.setCondition(createCondition(conditionType)); + role.setCondition(createCondition(item, conditionType)); } } @@ -947,16 +1557,17 @@ private void addConditionToAssignments(String text) { String assignmentText = StringUtils.substring(item, 0,-1); char conditionType = item.charAt(item.length() - 1); AssignmentType assignment = findAssignmentOrInducement(assignmentText); - assignment.setCondition(createCondition(conditionType)); + assignment.setCondition(createCondition(item, conditionType)); } } - private MappingType createCondition(char conditionType) { + private MappingType createCondition(String conditionName, char conditionType) { ScriptExpressionEvaluatorType script = new ScriptExpressionEvaluatorType(); switch (conditionType) { case '+': script.setCode("basic.stringify(name) == 'jack1'"); break; case '-': script.setCode("basic.stringify(name) == 'jack'"); break; case '0': script.setCode("basic.stringify(name) == 'never there'"); break; + case '!': script.setCode(createDumpConditionCode(conditionName)); break; default: throw new AssertionError(conditionType); } ExpressionType expression = new ExpressionType(); @@ -964,11 +1575,20 @@ private MappingType createCondition(char conditionType) { VariableBindingDefinitionType source = new VariableBindingDefinitionType(); source.setPath(new ItemPath(UserType.F_NAME).asItemPathType()); MappingType rv = new MappingType(); + rv.setName(conditionName); rv.setExpression(expression); rv.getSource().add(source); return rv; } + private String createDumpConditionCode(String text) { + return "import com.evolveum.midpoint.model.impl.lens.TestAssignmentProcessor2\n\n" + + "TestAssignmentProcessor2.startCallback('" + text + "')\n" + + "this.binding.variables.each {k,v -> TestAssignmentProcessor2.variableCallback(k, v, '" + text + "')}\n" + + "TestAssignmentProcessor2.finishCallback('" + text + "')\n" + + "return true"; + } + private void induce(AbstractRoleType source, AbstractRoleType target, int inducementOrder) { AssignmentType inducement = ObjectTypeUtil.createAssignmentTo(target.asPrismObject()); if (inducementOrder > 1) { @@ -983,15 +1603,26 @@ private void assign(RoleType source, RoleType target) { } private RoleType createRole(int level, int number) { - return prepareAbstractRole(new RoleType(prismContext), level, number, "R"); + return prepareAbstractRole(new RoleType(prismContext), createName("R", level, number)); + } + + private RoleType createRole(String name) { + return prepareAbstractRole(new RoleType(prismContext), name); + } + + private String createName(String prefix, int level, int number) { + return StringUtils.repeat('M', level-1) + prefix + number; } private OrgType createOrg(int number) { - return prepareAbstractRole(new OrgType(prismContext), 1, number, "O"); + return prepareAbstractRole(new OrgType(prismContext), createName("O", 1, number)); } - private R prepareAbstractRole(R abstractRole, int level, int number, String nameSymbol) { - String name = StringUtils.repeat('M', level-1) + nameSymbol + number; + private OrgType createOrg(String name) { + return prepareAbstractRole(new OrgType(prismContext), name); + } + + private R prepareAbstractRole(R abstractRole, String name) { String oid = getRoleOid(name); abstractRole.setName(PolyStringType.fromOrig(name)); @@ -1056,6 +1687,7 @@ private void addAssignmentOrInducement(R abstractRo } private static String getRoleOid(String name) { + name = name.substring(0, Math.min(12, name.length())); return "99999999-0000-0000-0000-" + StringUtils.repeat('0', 12-name.length()) + name; } @@ -1199,6 +1831,12 @@ private Collection> assertAssig //endregion //region ============================================================= helper methods (misc) + private List> getConstructions(EvaluatedAssignmentImpl evaluatedAssignment, String name) { + return evaluatedAssignment.getConstructions().getAllValues().stream() + .filter(c -> name.equals(c.getDescription())) + .collect(Collectors.toList()); + } + private AssignmentType findAssignmentOrInducement(String assignmentText) { String[] split = StringUtils.split(assignmentText, "-"); AbstractRoleType source = findRole(split[0]); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java index 56254d3e8b5..2c9207b83b7 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java @@ -163,6 +163,12 @@ public class AbstractConfiguredModelIntegrationTest extends AbstractModelIntegra protected static final String RESOURCE_DUMMY_BLACK_OID = "10000000-0000-0000-0000-000000000305"; protected static final String RESOURCE_DUMMY_BLACK_NAME = "black"; protected static final String RESOURCE_DUMMY_BLACK_NAMESPACE = MidPointConstants.NS_RI; + + // Black dummy resource for testing tolerant attributes + protected static final File RESOURCE_DUMMY_RELATIVE_FILE = new File(COMMON_DIR, "resource-dummy-relative.xml"); + protected static final String RESOURCE_DUMMY_RELATIVE_OID = "adcd4654-0f15-11e7-8337-0bdf60ad7bcd"; + protected static final String RESOURCE_DUMMY_RELATIVE_NAME = "relative"; + protected static final String RESOURCE_DUMMY_RELATIVE_NAMESPACE = MidPointConstants.NS_RI; // Orange dummy resource for testing associations with resource-provided referential integrity // It also have very little outbound expressions and it has some strange inbound expressions. @@ -195,7 +201,7 @@ public class AbstractConfiguredModelIntegrationTest extends AbstractModelIntegra protected static final String ROLE_PIRATE_DESCRIPTION = "Scurvy Pirates"; protected static final String ROLE_PIRATE_TITLE = "Bloody Pirate"; protected static final String ROLE_PIRATE_WEAPON = "cutlass"; - + protected static final File ROLE_CARIBBEAN_PIRATE_FILE = new File(COMMON_DIR, "role-caribbean-pirate.xml"); protected static final String ROLE_CARIBBEAN_PIRATE_OID = "0719ec66-edd9-11e6-bd70-03a74157ff9e"; @@ -204,6 +210,10 @@ public class AbstractConfiguredModelIntegrationTest extends AbstractModelIntegra protected static final String ROLE_PIRATE_GREEN_NAME = "Pirate Green"; protected static final String ROLE_PIRATE_GREEN_DESCRIPTION = "Scurvy Pirates"; + protected static final File ROLE_PIRATE_RELATIVE_FILE = new File(COMMON_DIR, "role-pirate-relative.xml"); + protected static final String ROLE_PIRATE_RELATIVE_OID = "4a579cd0-0f17-11e7-967c-130ecd6fb7dc"; + protected static final String ROLE_PIRAT_RELATIVEE_NAME = "Relative Pirate"; + protected static final File ROLE_BUCCANEER_GREEN_FILE = new File(COMMON_DIR, "role-buccaneer-green.xml"); protected static final String ROLE_BUCCANEER_GREEN_OID = "12345678-d34d-b33f-f00d-555555558888"; protected static final String ROLE_BUCCANEER_GREEN_NAME = "Bucaneers Green"; diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractInitializedModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractInitializedModelIntegrationTest.java index 332b1515e10..50b678c2e3c 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractInitializedModelIntegrationTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractInitializedModelIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * 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. @@ -160,6 +160,9 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti initDummyResourcePirate(RESOURCE_DUMMY_BLACK_NAME, RESOURCE_DUMMY_BLACK_FILE, RESOURCE_DUMMY_BLACK_OID, initTask, initResult); + initDummyResourcePirate(RESOURCE_DUMMY_RELATIVE_NAME, + RESOURCE_DUMMY_RELATIVE_FILE, RESOURCE_DUMMY_RELATIVE_OID, initTask, initResult); + dummyResourceCtlBlue = DummyResourceContoller.create(RESOURCE_DUMMY_BLUE_NAME, resourceDummyBlue); dummyResourceCtlBlue.extendSchemaPirate(); dummyResourceBlue = dummyResourceCtlBlue.getDummyResource(); @@ -253,6 +256,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti // Roles repoAddObjectFromFile(ROLE_PIRATE_FILE, initResult); repoAddObjectFromFile(ROLE_PIRATE_GREEN_FILE, initResult); + repoAddObjectFromFile(ROLE_PIRATE_RELATIVE_FILE, initResult); repoAddObjectFromFile(ROLE_CARIBBEAN_PIRATE_FILE, initResult); repoAddObjectFromFile(ROLE_BUCCANEER_GREEN_FILE, initResult); repoAddObjectFromFile(ROLE_NICE_PIRATE_FILENAME, initResult); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestAudit.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestAudit.java index 6b151f9a74d..876379f9875 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestAudit.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestAudit.java @@ -77,7 +77,7 @@ public class TestAudit extends AbstractInitializedModelIntegrationTest { public static final File TEST_DIR = new File("src/test/resources/audit"); - public static final int INITIAL_NUMBER_OF_AUDIT_RECORDS = 24; + public static final int INITIAL_NUMBER_OF_AUDIT_RECORDS = 26; private XMLGregorianCalendar initialTs; private XMLGregorianCalendar jackKidTs; diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPreviewChanges.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPreviewChanges.java index cb065e1674a..0e60492f21f 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPreviewChanges.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPreviewChanges.java @@ -36,6 +36,7 @@ import org.testng.AssertJUnit; import org.testng.annotations.Test; +import com.evolveum.icf.dummy.resource.DummyAccount; import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; import com.evolveum.midpoint.model.api.ModelExecuteOptions; import com.evolveum.midpoint.model.api.context.ModelContext; @@ -79,6 +80,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationPhaseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.RichHyperlinkType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; @@ -112,6 +114,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti initDummyResourcePirate(RESOURCE_DUMMY_LEMON_NAME, RESOURCE_DUMMY_LEMON_FILE, RESOURCE_DUMMY_LEMON_OID, initTask, initResult); + } @Test @@ -490,8 +493,8 @@ public void test150GetGuybrushRefinedObjectClassDef() throws Exception { } @Test - public void test200ModifyUserDeleteAccount() throws Exception { - final String TEST_NAME = "test200ModifyUserDeleteAccount"; + public void test200ModifyUserGuybrushDeleteAccount() throws Exception { + final String TEST_NAME = "test200ModifyUserGuybrushDeleteAccount"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN @@ -510,9 +513,11 @@ public void test200ModifyUserDeleteAccount() throws Exception { display("Input deltas: ", deltas); // WHEN + TestUtil.displayWhen(TEST_NAME); ModelContext modelContext = modelInteractionService.previewChanges(deltas, new ModelExecuteOptions(), task, result); // THEN + TestUtil.displayThen(TEST_NAME); display("Preview context", modelContext); assertNotNull("Null model context", modelContext); @@ -537,8 +542,8 @@ public void test200ModifyUserDeleteAccount() throws Exception { } @Test - public void test210AddAccount() throws Exception { - final String TEST_NAME = "test210AddAccount"; + public void test210GuybrushAddAccount() throws Exception { + final String TEST_NAME = "test210GuybrushAddAccount"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN @@ -552,9 +557,11 @@ public void test210AddAccount() throws Exception { display("Input deltas: ", deltas); // WHEN + TestUtil.displayWhen(TEST_NAME); ModelContext modelContext = modelInteractionService.previewChanges(deltas, new ModelExecuteOptions(), task, result); // THEN + TestUtil.displayThen(TEST_NAME); display("Preview context", modelContext); assertNotNull("Null model context", modelContext); @@ -586,8 +593,8 @@ public void test210AddAccount() throws Exception { } @Test - public void test221ModifyUserAddAccountRef() throws Exception { - final String TEST_NAME = "test221ModifyUserAddAccountRef"; + public void test212ModifyUserAddAccountRef() throws Exception { + final String TEST_NAME = "test212ModifyUserAddAccountRef"; TestUtil.displayTestTile(this, TEST_NAME); // GIVEN @@ -603,9 +610,11 @@ public void test221ModifyUserAddAccountRef() throws Exception { display("Input deltas: ", userDelta); // WHEN + TestUtil.displayWhen(TEST_NAME); ModelContext modelContext = modelInteractionService.previewChanges(deltas, new ModelExecuteOptions(), task, result); // THEN + TestUtil.displayThen(TEST_NAME); display("Preview context", modelContext); assertNotNull("Null model context", modelContext); @@ -637,6 +646,562 @@ public void test221ModifyUserAddAccountRef() throws Exception { dummyResourceCtl.getAttributePath(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_QUOTE_NAME), "Arr!"); } + /** + * MID-3079 + */ + @Test + public void test220PreviewJackAssignRolePirate() throws Exception { + final String TEST_NAME = "test220PreviewJackAssignRolePirate"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + ObjectDelta delta = createAssignmentFocusDelta(UserType.class, USER_JACK_OID, + ROLE_PIRATE_OID, RoleType.COMPLEX_TYPE, null, null, null, true); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + ModelContext modelContext = modelInteractionService.previewChanges(MiscSchemaUtil.createCollection(delta), + null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + display("Preview context", modelContext); + assertNotNull("Null model context", modelContext); + + result.computeStatus(); + TestUtil.assertSuccess(result); + + ModelElementContext focusContext = modelContext.getFocusContext(); + assertNotNull("Model focus context missing", focusContext); + + Collection projectionContexts = modelContext.getProjectionContexts(); + assertNotNull("Null model projection context list", projectionContexts); + assertEquals("Unexpected number of projection contexts", 1, projectionContexts.size()); + ModelProjectionContext accContext = projectionContexts.iterator().next(); + assertNotNull("Null model projection context", accContext); + + ObjectDelta accountPrimaryDelta = accContext.getPrimaryDelta(); + assertNull("Unexpected account primary delta", accountPrimaryDelta); + + ObjectDelta accountSecondaryDelta = accContext.getSecondaryDelta(); + assertEquals(ChangeType.MODIFY, accountSecondaryDelta.getChangeType()); + + assertAccountItemModify(accountSecondaryDelta, + SchemaConstants.PATH_ACTIVATION_ADMINISTRATIVE_STATUS, + null, // old + null, // add + null, // delete + new ActivationStatusType[] { ActivationStatusType.ENABLED }); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_WEAPON_NAME, + null, // old + new String[] { ROLE_PIRATE_WEAPON }, // add + null, // delete + null); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, + null, // old + new String[] { ROLE_PIRATE_TITLE }, // add + null, // delete + null); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DUMMY_ACCOUNT_ATTRIBUTE_SEA_NAME, + null, // old + null, // add + null, // delete + new String[] { "jack sailed The Seven Seas, immediately , role , with this The Seven Seas while focused on (in Pirate)" }); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, + null, // old + new String[] { "Jack Sparrow is the best pirate Caribbean has ever seen" }, // add + null, // delete + null); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_QUOTE_NAME, + null, // old + new String[] { RESOURCE_DUMMY_QUOTE }, // add + null, // delete + null); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_DRINK_NAME, + null, // old + new String[] { RESOURCE_DUMMY_DRINK }, // add + null, // delete + null); // replace + + PrismAsserts.assertModifications(accountSecondaryDelta, 14); + } + + /** + * Make sure that Guybrush has an existing account and that it is properly populated. + * We will use this setup in following tests. + */ + @Test + public void test230GuybrushAssignAccountDummy() throws Exception { + final String TEST_NAME = "test230GuybrushAssignAccountDummy"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + ObjectDelta delta = createAssignmentFocusDelta(UserType.class, USER_GUYBRUSH_OID, + ROLE_PIRATE_OID, RoleType.COMPLEX_TYPE, null, null, null, true); + + ModelExecuteOptions options = ModelExecuteOptions.createReconcile(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + modifyUserReplace(USER_GUYBRUSH_OID, new ItemPath(UserType.F_EXTENSION, PIRACY_WEAPON), task, result, + "tongue"); + assignAccount(USER_GUYBRUSH_OID, RESOURCE_DUMMY_OID, null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + DummyAccount dummyAccount = assertDummyAccount(null, USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME, true); + display("Dummy account after", dummyAccount); + dummyAccount.addAttributeValue(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, "Great Pirate"); + } + + /** + * MID-3079 + */ + @Test + public void test232PreviewGuybrushAddRolePirate() throws Exception { + final String TEST_NAME = "test232PreviewGuybrushAddRolePirate"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + ObjectDelta delta = createAssignmentFocusDelta(UserType.class, USER_GUYBRUSH_OID, + ROLE_PIRATE_OID, RoleType.COMPLEX_TYPE, null, null, null, true); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + ModelContext modelContext = modelInteractionService.previewChanges(MiscSchemaUtil.createCollection(delta), + null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + display("Preview context", modelContext); + assertNotNull("Null model context", modelContext); + + result.computeStatus(); + TestUtil.assertSuccess(result); + + ModelElementContext focusContext = modelContext.getFocusContext(); + assertNotNull("Model focus context missing", focusContext); + + Collection projectionContexts = modelContext.getProjectionContexts(); + assertNotNull("Null model projection context list", projectionContexts); + assertEquals("Unexpected number of projection contexts", 1, projectionContexts.size()); + ModelProjectionContext accContext = projectionContexts.iterator().next(); + assertNotNull("Null model projection context", accContext); + + ObjectDelta accountPrimaryDelta = accContext.getPrimaryDelta(); + assertNull("Unexpected account primary delta", accountPrimaryDelta); + + ObjectDelta accountSecondaryDelta = accContext.getSecondaryDelta(); + assertEquals(ChangeType.MODIFY, accountSecondaryDelta.getChangeType()); + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, + new String[] { "Great Pirate" }, // old + new String[] { ROLE_PIRATE_TITLE }, // add + null, // delete + null); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DUMMY_ACCOUNT_ATTRIBUTE_SEA_NAME, + null, // old + null, // add + null, // delete + new String[] { "guybrush sailed The Seven Seas, immediately , role , with this The Seven Seas while focused on (in Pirate)" }); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, + null, // old + new String[] { "Guybrush Threepwood is the best pirate Melee Island has ever seen" }, // add + null, // delete + null); // replace + + PrismAsserts.assertModifications(accountSecondaryDelta, 3); + } + + /** + * MID-3079 + */ + @Test + public void test234PreviewGuybrushAddRolePirateRecon() throws Exception { + final String TEST_NAME = "test234PreviewGuybrushAddRolePirateRecon"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + ObjectDelta delta = createAssignmentFocusDelta(UserType.class, USER_GUYBRUSH_OID, + ROLE_PIRATE_OID, RoleType.COMPLEX_TYPE, null, null, null, true); + + ModelExecuteOptions options = ModelExecuteOptions.createReconcile(); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + ModelContext modelContext = modelInteractionService.previewChanges(MiscSchemaUtil.createCollection(delta), + options, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + display("Preview context", modelContext); + assertNotNull("Null model context", modelContext); + + result.computeStatus(); + TestUtil.assertSuccess(result); + + ModelElementContext focusContext = modelContext.getFocusContext(); + assertNotNull("Model focus context missing", focusContext); + + Collection projectionContexts = modelContext.getProjectionContexts(); + assertNotNull("Null model projection context list", projectionContexts); + assertEquals("Unexpected number of projection contexts", 1, projectionContexts.size()); + ModelProjectionContext accContext = projectionContexts.iterator().next(); + assertNotNull("Null model projection context", accContext); + + ObjectDelta accountPrimaryDelta = accContext.getPrimaryDelta(); + assertNull("Unexpected account primary delta", accountPrimaryDelta); + + ObjectDelta accountSecondaryDelta = accContext.getSecondaryDelta(); + assertEquals(ChangeType.MODIFY, accountSecondaryDelta.getChangeType()); + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, + new String[] { "Great Pirate" }, // old + new String[] { ROLE_PIRATE_TITLE }, // add + null, // delete + null); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DUMMY_ACCOUNT_ATTRIBUTE_SEA_NAME, + null, // old + null, // add + null, // delete + new String[] { "guybrush sailed The Seven Seas, immediately , role , with this The Seven Seas while focused on (in Pirate)" }); // replace + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, + null, // old + new String[] { "Guybrush Threepwood is the best pirate Melee Island has ever seen" }, // add + null, // delete + null); // replace + + PrismAsserts.assertModifications(accountSecondaryDelta, 3); + } + + /** + * MID-3079 + */ + @Test + public void test236PreviewGuybrushAddRoleSailor() throws Exception { + final String TEST_NAME = "test236PreviewGuybrushAddRoleSailor"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + ObjectDelta delta = createAssignmentFocusDelta(UserType.class, USER_GUYBRUSH_OID, + ROLE_SAILOR_OID, RoleType.COMPLEX_TYPE, null, null, null, true); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + ModelContext modelContext = modelInteractionService.previewChanges(MiscSchemaUtil.createCollection(delta), + null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + display("Preview context", modelContext); + assertNotNull("Null model context", modelContext); + + result.computeStatus(); + TestUtil.assertSuccess(result); + + ModelElementContext focusContext = modelContext.getFocusContext(); + assertNotNull("Model focus context missing", focusContext); + + Collection projectionContexts = modelContext.getProjectionContexts(); + assertNotNull("Null model projection context list", projectionContexts); + assertEquals("Unexpected number of projection contexts", 1, projectionContexts.size()); + ModelProjectionContext accContext = projectionContexts.iterator().next(); + assertNotNull("Null model projection context", accContext); + + ObjectDelta accountPrimaryDelta = accContext.getPrimaryDelta(); + assertNull("Unexpected account primary delta", accountPrimaryDelta); + + ObjectDelta accountSecondaryDelta = accContext.getSecondaryDelta(); + assertEquals(ChangeType.MODIFY, accountSecondaryDelta.getChangeType()); + + assertAccountDefaultDummyAttributeModify(accountSecondaryDelta, + DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_DRINK_NAME, + new String[] { RESOURCE_DUMMY_DRINK }, // old + new String[] { ROLE_SAILOR_DRINK }, // add + null, // delete + null); // replace + + PrismAsserts.assertModifications(accountSecondaryDelta, 1); + } + + @Test + public void test239GuybrushUnAssignAccountDummy() throws Exception { + final String TEST_NAME = "test239GuybrushUnAssignAccountDummy"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + unassignAccount(USER_GUYBRUSH_OID, RESOURCE_DUMMY_OID, null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + assertNoDummyAccount(null, USER_GUYBRUSH_USERNAME); + } + + /** + * Make sure that Guybrush has an existing account and that it is properly populated. + * We will use this setup in following tests. + */ + @Test + public void test240GuybrushAssignAccountDummyRelative() throws Exception { + final String TEST_NAME = "test240GuybrushAssignAccountDummyRelative"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + assignAccount(USER_GUYBRUSH_OID, RESOURCE_DUMMY_RELATIVE_OID, null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + DummyAccount dummyAccount = assertDummyAccount(RESOURCE_DUMMY_RELATIVE_NAME, USER_GUYBRUSH_USERNAME, USER_GUYBRUSH_FULL_NAME, true); + display("Dummy account after", dummyAccount); + dummyAccount.addAttributeValue(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, "Great Pirate"); + } + + /** + * MID-3079 + * Relative operation on a relative resource. The account is not retrieved. + * There are no old values at all. + */ + @Test + public void test242PreviewGuybrushAddRolePirateRelative() throws Exception { + final String TEST_NAME = "test242PreviewGuybrushAddRolePirateRelative"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + ObjectDelta delta = createAssignmentFocusDelta(UserType.class, USER_GUYBRUSH_OID, + ROLE_PIRATE_RELATIVE_OID, RoleType.COMPLEX_TYPE, null, null, null, true); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + ModelContext modelContext = modelInteractionService.previewChanges(MiscSchemaUtil.createCollection(delta), + null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + display("Preview context", modelContext); + assertNotNull("Null model context", modelContext); + + result.computeStatus(); + TestUtil.assertSuccess(result); + + ModelElementContext focusContext = modelContext.getFocusContext(); + assertNotNull("Model focus context missing", focusContext); + + Collection projectionContexts = modelContext.getProjectionContexts(); + assertNotNull("Null model projection context list", projectionContexts); + assertEquals("Unexpected number of projection contexts", 1, projectionContexts.size()); + ModelProjectionContext accContext = projectionContexts.iterator().next(); + assertNotNull("Null model projection context", accContext); + + ObjectDelta accountPrimaryDelta = accContext.getPrimaryDelta(); + assertNull("Unexpected account primary delta", accountPrimaryDelta); + + ObjectDelta accountSecondaryDelta = accContext.getSecondaryDelta(); + assertEquals(ChangeType.MODIFY, accountSecondaryDelta.getChangeType()); + + assertAccountDummyAttributeModify(accountSecondaryDelta, + RESOURCE_DUMMY_RELATIVE_NAME, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_WEAPON_NAME, + null, // old + new String[] { ROLE_PIRATE_WEAPON }, // add + null, // delete + null); // replace + + assertAccountDummyAttributeModify(accountSecondaryDelta, + RESOURCE_DUMMY_RELATIVE_NAME, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, + null, // old + new String[] { ROLE_PIRATE_TITLE }, // add + null, // delete + null); // replace + + assertAccountDummyAttributeModify(accountSecondaryDelta, + RESOURCE_DUMMY_RELATIVE_NAME, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, + null, // old + new String[] { "Guybrush Threepwood is the best pirate Melee Island has ever seen" }, // add + null, // delete + null); // replace + + PrismAsserts.assertModifications(accountSecondaryDelta, 3); + } + + /** + * MID-3079 + */ + @Test + public void test244PreviewGuybrushAddRolePirateRelativeRecon() throws Exception { + final String TEST_NAME = "test244PreviewGuybrushAddRolePirateRelativeRecon"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + ObjectDelta delta = createAssignmentFocusDelta(UserType.class, USER_GUYBRUSH_OID, + ROLE_PIRATE_RELATIVE_OID, RoleType.COMPLEX_TYPE, null, null, null, true); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + ModelContext modelContext = modelInteractionService.previewChanges(MiscSchemaUtil.createCollection(delta), + ModelExecuteOptions.createReconcile(), task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + display("Preview context", modelContext); + assertNotNull("Null model context", modelContext); + + result.computeStatus(); + TestUtil.assertSuccess(result); + + ModelElementContext focusContext = modelContext.getFocusContext(); + assertNotNull("Model focus context missing", focusContext); + + Collection projectionContexts = modelContext.getProjectionContexts(); + assertNotNull("Null model projection context list", projectionContexts); + assertEquals("Unexpected number of projection contexts", 1, projectionContexts.size()); + ModelProjectionContext accContext = projectionContexts.iterator().next(); + assertNotNull("Null model projection context", accContext); + + ObjectDelta accountPrimaryDelta = accContext.getPrimaryDelta(); + assertNull("Unexpected account primary delta", accountPrimaryDelta); + + ObjectDelta accountSecondaryDelta = accContext.getSecondaryDelta(); + assertEquals(ChangeType.MODIFY, accountSecondaryDelta.getChangeType()); + + assertAccountDummyAttributeModify(accountSecondaryDelta, + RESOURCE_DUMMY_RELATIVE_NAME, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_WEAPON_NAME, + new String[] { "tongue" }, // old + new String[] { ROLE_PIRATE_WEAPON }, // add + null, // delete + null); // replace + + assertAccountDummyAttributeModify(accountSecondaryDelta, + RESOURCE_DUMMY_RELATIVE_NAME, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_TITLE_NAME, + new String[] { "Great Pirate" }, // old + new String[] { ROLE_PIRATE_TITLE }, // add + null, // delete + null); // replace + + assertAccountDummyAttributeModify(accountSecondaryDelta, + RESOURCE_DUMMY_RELATIVE_NAME, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, + null, // old + new String[] { "Guybrush Threepwood is the best pirate Melee Island has ever seen" }, // add + null, // delete + null); // replace + + PrismAsserts.assertModifications(accountSecondaryDelta, 3); + } + + @Test + public void test249GuybrushUnAssignAccountDummyRelative() throws Exception { + final String TEST_NAME = "test249GuybrushUnAssignAccountDummyRelative"; + TestUtil.displayTestTile(this, TEST_NAME); + + // GIVEN + Task task = taskManager.createTaskInstance(TestPreviewChanges.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + // WHEN + TestUtil.displayWhen(TEST_NAME); + unassignAccount(USER_GUYBRUSH_OID, RESOURCE_DUMMY_RELATIVE_OID, null, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + assertNoDummyAccount(RESOURCE_DUMMY_RELATIVE_NAME, USER_GUYBRUSH_USERNAME); + } + + + private void assertAccountDefaultDummyAttributeModify(ObjectDelta accountDelta, + String attrName, + T[] expectedOldValues, T[] expectedAddValues, T[] expectedDeleteValues, T[] expectedReplaceValues) { + ItemPath itemPath = getDummyResourceController().getAttributePath(attrName); + assertAccountItemModify(accountDelta, itemPath, expectedOldValues, expectedAddValues, expectedDeleteValues, expectedReplaceValues); + } + + private void assertAccountDummyAttributeModify(ObjectDelta accountDelta, + String dummyName, String attrName, + T[] expectedOldValues, T[] expectedAddValues, T[] expectedDeleteValues, T[] expectedReplaceValues) { + ItemPath itemPath = getDummyResourceController(dummyName).getAttributePath(attrName); + assertAccountItemModify(accountDelta, itemPath, expectedOldValues, expectedAddValues, expectedDeleteValues, expectedReplaceValues); + } + + private void assertAccountItemModify(ObjectDelta accountDelta, + ItemPath itemPath, + T[] expectedOldValues, T[] expectedAddValues, T[] expectedDeleteValues, T[] expectedReplaceValues) { + PropertyDelta attrDelta = accountDelta.findPropertyDelta(itemPath); + assertNotNull("No delta for "+itemPath+" in "+accountDelta, attrDelta); + PrismAsserts.assertPropertyDelta(attrDelta, expectedOldValues, expectedAddValues, expectedDeleteValues, expectedReplaceValues); + } + // MAPPING TESTS // following tests mostly check correct functions of mappings @@ -988,9 +1553,12 @@ public void test600ModifyElaineUserDummyReplace() throws Exception { display("Input deltas: ", deltas); // WHEN + TestUtil.displayWhen(TEST_NAME); ModelContext modelContext = modelInteractionService.previewChanges(deltas, new ModelExecuteOptions(), task, result); // THEN + TestUtil.displayThen(TEST_NAME); + display("Preview context", modelContext); assertNotNull("Null model context", modelContext); @@ -1097,9 +1665,12 @@ ACCOUNT_SHADOW_ELAINE_DUMMY_OID, getDummyResourceObject(), display("Input deltas: ", deltas); // WHEN + TestUtil.displayWhen(TEST_NAME); ModelContext modelContext = modelInteractionService.previewChanges(deltas, new ModelExecuteOptions(), task, result); // THEN + TestUtil.displayThen(TEST_NAME); + display("Preview context", modelContext); assertNotNull("Null model context", modelContext); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestSecurity.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestSecurity.java index 7d496e1212a..befd02230bd 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestSecurity.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestSecurity.java @@ -54,10 +54,12 @@ import com.evolveum.midpoint.schema.util.ObjectQueryUtil; import com.evolveum.midpoint.security.api.Authorization; import com.evolveum.midpoint.security.api.AuthorizationConstants; +import com.evolveum.midpoint.security.api.ItemSecurityDecisions; import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.Producer; +import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; @@ -84,8 +86,11 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Map.Entry; +import java.util.Set; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static org.testng.AssertJUnit.assertEquals; @@ -2102,6 +2107,9 @@ public void run(Task task, OperationResult result) throws Exception { assertRoleTypes(spec, "application", "nonexistent"); assertFilter(spec.getFilter(), TypeFilter.class); + assertAllowRequestItems(USER_JACK_OID, ROLE_APPLICATION_1_OID, null, + AssignmentType.F_TARGET_REF, ActivationType.F_VALID_FROM, ActivationType.F_VALID_TO); + assertGlobalStateUntouched(); } @@ -2155,6 +2163,8 @@ public void run(Task task, OperationResult result) throws Exception { assertRoleTypes(spec); assertFilter(spec.getFilter(), TypeFilter.class); + assertAllowRequestItems(USER_JACK_OID, ROLE_APPLICATION_1_OID, AuthorizationDecisionType.ALLOW); + assertGlobalStateUntouched(); } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestUserTemplate.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestUserTemplate.java index 245935b54b6..20fae42924c 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestUserTemplate.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestUserTemplate.java @@ -108,7 +108,7 @@ public class TestUserTemplate extends AbstractInitializedModelIntegrationTest { private static final String EMPLOYEE_TYPE_MAROONED = "marooned"; - private static final int NUMBER_OF_ROLES = 19; + private static final int NUMBER_OF_ROLES = 20; private static String jackEmployeeNumber; diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestRbac.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestRbac.java index b38add2d05b..25098c990c4 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestRbac.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestRbac.java @@ -29,6 +29,7 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; +import com.evolveum.midpoint.util.QNameUtil; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; @@ -41,7 +42,6 @@ import com.evolveum.midpoint.model.api.context.EvaluatedAssignmentTarget; import com.evolveum.midpoint.model.api.context.ModelContext; import com.evolveum.midpoint.model.intest.AbstractInitializedModelIntegrationTest; -import com.evolveum.midpoint.prism.Objectable; import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismProperty; @@ -212,6 +212,8 @@ public void initSystem(Task initTask, OperationResult initResult) repoAddObjectFromFile(ROLE_META_FOOL_FILE, RoleType.class, initResult); repoAddObjectFromFile(ROLE_BLOODY_FOOL_FILE, RoleType.class, initResult); + repoAddObjectFromFile(USER_RAPP_FILE, initResult); + dummyResourceCtl.addGroup(GROUP_FOOLS_NAME); dummyResourceCtl.addGroup(GROUP_SIMPLETONS_NAME); @@ -1844,6 +1846,43 @@ public void test612JackAssignRoleGovernor() throws Exception { assertAssignees(ROLE_GOVERNOR_OID, 1); } + /** + * Governor has maxAssignees=0 for 'approver' + */ + @Test + public void test613JackAssignRoleGovernorAsApprover() throws Exception { + + if (!testMultiplicityConstraintsForNonDefaultRelations()) { + return; + } + + final String TEST_NAME = "test613JackAssignRoleGovernorAsApprover"; + TestUtil.displayTestTile(this, TEST_NAME); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + Task task = taskManager.createTaskInstance(TestRbac.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + try { + // WHEN + assignRole(USER_JACK_OID, ROLE_GOVERNOR_OID, SchemaConstants.ORG_APPROVER, task, result); + + AssertJUnit.fail("Unexpected success"); + } catch (PolicyViolationException e) { + // this is expected + display("Expected exception", e); + } + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertFailure(result); + + assertNoAssignments(USER_JACK_OID); + + assertAssignees(ROLE_GOVERNOR_OID, 1); + } + /** * Role cannibal has minAssignees=2. It is assigned to nobody. Even though assigning * it to lemonhead would result in assignees=1 which violates the policy, the assignment @@ -2043,7 +2082,73 @@ public void test628RedskullUnassignRoleCanibal() throws Exception { assertAssignees(ROLE_CANNIBAL_OID, 2); assertAssignees(ROLE_GOVERNOR_OID, 1); } - + + @Test + public void test630RappAssignRoleCanibalAsOwner() throws Exception { + + if (!testMultiplicityConstraintsForNonDefaultRelations()) { + return; + } + + final String TEST_NAME = "test630RappAssignRoleCanibalAsOwner"; + TestUtil.displayTestTile(this, TEST_NAME); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + Task task = taskManager.createTaskInstance(TestRbac.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + assertAssignees(ROLE_CANNIBAL_OID, 2); + + // WHEN + assignRole(USER_RAPP_OID, ROLE_CANNIBAL_OID, SchemaConstants.ORG_OWNER, task, result); + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertSuccess(result); + + assertAssignees(ROLE_CANNIBAL_OID, 2); + assertAssignees(ROLE_CANNIBAL_OID, SchemaConstants.ORG_OWNER, 1); + } + + @Test + public void test632RappUnassignRoleCanibalAsOwner() throws Exception { + + if (!testMultiplicityConstraintsForNonDefaultRelations()) { + return; + } + + final String TEST_NAME = "test632RappUnassignRoleCanibalAsOwner"; + TestUtil.displayTestTile(this, TEST_NAME); + assumeAssignmentPolicy(AssignmentPolicyEnforcementType.RELATIVE); + + Task task = taskManager.createTaskInstance(TestRbac.class.getName() + "." + TEST_NAME); + OperationResult result = task.getResult(); + + assertAssignees(ROLE_CANNIBAL_OID, 2); + assertAssignees(ROLE_CANNIBAL_OID, SchemaConstants.ORG_OWNER, 1); + + try { + // WHEN + TestUtil.displayWhen(TEST_NAME); + // null namespace to test no-namespace "approver" relation + unassignRole(USER_RAPP_OID, ROLE_CANNIBAL_OID, QNameUtil.nullNamespace(SchemaConstants.ORG_OWNER), task, result); + + AssertJUnit.fail("Unexpected success"); + } catch (PolicyViolationException e) { + // this is expected + display("Expected exception", e); + } + + // THEN + TestUtil.displayThen(TEST_NAME); + result.computeStatus(); + TestUtil.assertFailure(result); + + assertAssignees(ROLE_CANNIBAL_OID, 2); + assertAssignees(ROLE_CANNIBAL_OID, SchemaConstants.ORG_OWNER, 1); + } + @Test public void test649ElaineUnassignRoleGovernor() throws Exception { final String TEST_NAME = "test649ElaineUnassignRoleGovernor"; @@ -4110,4 +4215,7 @@ public void test857JackReconcile() throws Exception { } + protected boolean testMultiplicityConstraintsForNonDefaultRelations() { + return true; + } } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestRbacDeprecated.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestRbacDeprecated.java index cc962aa144d..84ed9d97a3f 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestRbacDeprecated.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/rbac/TestRbacDeprecated.java @@ -15,64 +15,11 @@ */ package com.evolveum.midpoint.model.intest.rbac; -import static org.testng.AssertJUnit.assertNotNull; -import static org.testng.AssertJUnit.assertNull; -import static com.evolveum.midpoint.test.IntegrationTestTools.display; -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertTrue; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import javax.xml.namespace.QName; - -import com.evolveum.midpoint.prism.query.builder.QueryBuilder; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; -import org.testng.AssertJUnit; -import org.testng.annotations.Test; -import com.evolveum.icf.dummy.resource.DummyAccount; -import com.evolveum.midpoint.model.api.context.EvaluatedAssignmentTarget; -import com.evolveum.midpoint.model.api.context.EvaluatedAssignment; -import com.evolveum.midpoint.model.api.context.ModelContext; -import com.evolveum.midpoint.model.intest.AbstractInitializedModelIntegrationTest; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismProperty; -import com.evolveum.midpoint.prism.PrismPropertyDefinition; -import com.evolveum.midpoint.prism.delta.DeltaSetTriple; -import com.evolveum.midpoint.prism.delta.ItemDelta; -import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.path.IdItemPathSegment; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.path.NameItemPathSegment; -import com.evolveum.midpoint.prism.query.EqualFilter; -import com.evolveum.midpoint.prism.query.ObjectFilter; -import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.prism.schema.PrismSchema; -import com.evolveum.midpoint.prism.util.PrismAsserts; -import com.evolveum.midpoint.prism.util.PrismTestUtil; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.MiscSchemaUtil; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.test.DummyResourceContoller; -import com.evolveum.midpoint.test.IntegrationTestTools; -import com.evolveum.midpoint.test.util.TestUtil; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.exception.PolicyViolationException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentPolicyEnforcementType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; -import com.evolveum.prism.xml.ns._public.types_3.EvaluationTimeType; +import java.io.File; /** * @author semancik @@ -89,8 +36,14 @@ public class TestRbacDeprecated extends TestRbac { protected File getRoleGovernorFile() { return ROLE_GOVERNOR_DEPRECATED_FILE; } - + + @Override protected File getRoleCannibalFile() { return ROLE_CANNIBAL_DEPRECATED_FILE; } + + @Override + protected boolean testMultiplicityConstraintsForNonDefaultRelations() { + return false; + } } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java index 308f0e961ce..87e6e1f4f8d 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * 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. @@ -197,7 +197,7 @@ public void test205SearchForResources() throws Exception { IntegrationTestTools.display("output", output.getData()); result.computeStatus(); TestUtil.assertSuccess(result); - assertEquals(12, output.getData().size()); + assertEquals(13, output.getData().size()); } @Test diff --git a/model/model-intest/src/test/resources/common/resource-dummy-relative.xml b/model/model-intest/src/test/resources/common/resource-dummy-relative.xml new file mode 100644 index 00000000000..8ce27000990 --- /dev/null +++ b/model/model-intest/src/test/resources/common/resource-dummy-relative.xml @@ -0,0 +1,241 @@ + + + + + + + + Dummy Resource Relative + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + + relative + + + + false + false + false + + + + + + + account + default + true + ri:AccountObjectClass + + icfs:name + + + $user/name + + + + + + + + icfs:uid + UID + + + ri:fullname + + + $user/fullName + + + + + ri:title + + + ri:location + + + + $user/locality + + + + + + ri:ship + + + ri:loot + + + ri:weapon + + + + declare namespace piracy = "http://midpoint.evolveum.com/xml/ns/samples/piracy"; + $user/extension/piracy:weapon + + + + + + ri:drink + false + + + rum + + + + + ri:quote + true + + + Arr! + + + + + ri:gossip + Gossip + true + + + ri:water + + true + + + + + fishy + + + + + + ri:group + false + entitlement + group + objectToSubject + ri:members + icfs:name + + + + ri:priv + entitlement + privilege + subjectToObject + ri:privileges + icfs:name + + + + 5 + + + + + + + + + + + + + + + + + + + + + + entitlement + group + true + ri:GroupObjectClass + + icfs:name + + + $focus/name + + + declare namespace t="http://prism.evolveum.com/xml/ns/public/types-3"; + t:norm + + + + + ri:description + + + $focus/description + + + + + ri:members + minimal + + + + + entitlement + privilege + false + ri:CustomprivilegeObjectClass + + + + + diff --git a/model/model-intest/src/test/resources/common/role-pirate-relative.xml b/model/model-intest/src/test/resources/common/role-pirate-relative.xml new file mode 100644 index 00000000000..4768381a196 --- /dev/null +++ b/model/model-intest/src/test/resources/common/role-pirate-relative.xml @@ -0,0 +1,82 @@ + + + Relative Pirate + + + + account + + ri:title + + + Bloody Pirate + + + + + ri:location + + + locality + + + + + + + + ri:weapon + + + cutlass + + + + + ri:gossip + + + $user/fullName + + + $user/locality + + + + + + + + + + http://midpoint.evolveum.com/xml/ns/test/authorization#loot + execution + + diff --git a/model/model-intest/src/test/resources/rbac/role-cannibal.xml b/model/model-intest/src/test/resources/rbac/role-cannibal.xml index 6007852976e..e4a8ef3ade1 100644 --- a/model/model-intest/src/test/resources/rbac/role-cannibal.xml +++ b/model/model-intest/src/test/resources/rbac/role-cannibal.xml @@ -26,6 +26,10 @@ 2 + + 1 + owner + 3 diff --git a/model/model-intest/src/test/resources/rbac/role-governor.xml b/model/model-intest/src/test/resources/rbac/role-governor.xml index c8ebdbac29e..bf65f9ee761 100644 --- a/model/model-intest/src/test/resources/rbac/role-governor.xml +++ b/model/model-intest/src/test/resources/rbac/role-governor.xml @@ -21,7 +21,7 @@ Governor - min1 + max1 1 @@ -31,6 +31,20 @@ + + + + no-approvers + + + 0 + approver + + + + + + diff --git a/model/model-intest/src/test/resources/security/role-assign-application-roles.xml b/model/model-intest/src/test/resources/security/role-assign-application-roles.xml index c5de6f6847d..ecf2c01a663 100644 --- a/model/model-intest/src/test/resources/security/role-assign-application-roles.xml +++ b/model/model-intest/src/test/resources/security/role-assign-application-roles.xml @@ -23,7 +23,6 @@ http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#assign - http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#unassign request RoleType @@ -48,6 +47,24 @@ + assignment/targetRef + assignment/activation/validFrom + assignment/activation/validTo + + + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#unassign + request + + RoleType + + + + roleType + application + + + + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#add diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index e34e0397d10..c8b6dd80e92 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -71,6 +71,7 @@ import com.evolveum.midpoint.schema.util.*; import com.evolveum.midpoint.security.api.Authorization; import com.evolveum.midpoint.security.api.AuthorizationConstants; +import com.evolveum.midpoint.security.api.ItemSecurityDecisions; import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.security.api.SecurityEnforcer; import com.evolveum.midpoint.security.api.UserProfileService; @@ -98,6 +99,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.AdminGuiConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentSelectorType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationDecisionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationPhaseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType; @@ -926,7 +928,7 @@ protected void unassignAllRoles(String userOid, boolean useRawPlusRecompute) thr if (targetRef != null) { if (targetRef.getType().equals(RoleType.COMPLEX_TYPE)) { ContainerDelta assignmentDelta = ContainerDelta.createDelta(UserType.F_ASSIGNMENT, getUserDefinition()); - PrismContainerValue cval = new PrismContainerValue(prismContext); + PrismContainerValue cval = new PrismContainerValue<>(prismContext); cval.setId(assignment.getId()); assignmentDelta.addValueToDelete(cval); modifications.add(assignmentDelta); @@ -1201,18 +1203,29 @@ protected void unassignPrametricRole(String userOid, String roleOid, String orgO } protected void assertAssignees(String targetOid, int expectedAssignees) throws SchemaException { + assertAssignees(targetOid, SchemaConstants.ORG_DEFAULT, expectedAssignees); + } + + protected void assertAssignees(String targetOid, QName relation, int expectedAssignees) throws SchemaException { OperationResult result = new OperationResult(AbstractModelIntegrationTest.class.getName()+".assertAssignees"); - int count = countAssignees(targetOid, result); + int count = countAssignees(targetOid, relation, result); if (count != expectedAssignees) { SearchResultList> assignees = listAssignees(targetOid, result); - AssertJUnit.fail("Unexpected number of assignees of "+targetOid+", expected "+expectedAssignees+", but was " + count+ ": "+assignees); + AssertJUnit.fail("Unexpected number of assignees of "+targetOid+" as '"+relation+"', expected "+expectedAssignees+", but was " + count+ ": "+assignees); } } protected int countAssignees(String targetOid, OperationResult result) throws SchemaException { + return countAssignees(targetOid, SchemaConstants.ORG_DEFAULT, result); + } + + protected int countAssignees(String targetOid, QName relation, OperationResult result) throws SchemaException { + PrismReferenceValue refVal = new PrismReferenceValue(); + refVal.setOid(targetOid); + refVal.setRelation(relation); ObjectQuery query = QueryBuilder.queryFor(FocusType.class, prismContext) - .item(FocusType.F_ASSIGNMENT, AssignmentType.F_TARGET_REF).ref(targetOid) + .item(FocusType.F_ASSIGNMENT, AssignmentType.F_TARGET_REF).ref(refVal) .build(); return repositoryService.countObjects(FocusType.class, query, result); } @@ -3760,6 +3773,42 @@ protected void assertRoleTypes(RoleSelectionSpecification roleSpec, String... ex } } } + + protected void assertAllowRequestItems(String userOid, String targetRoleOid, AuthorizationDecisionType expectedDefaultDecision, QName... expectedAllowedItemQNames) throws SchemaException, SecurityViolationException, CommunicationException, ObjectNotFoundException, ConfigurationException { + PrismObject user = getUser(userOid); + PrismObject target = getRole(targetRoleOid); + + ItemSecurityDecisions decisions = modelInteractionService.getAllowedRequestAssignmentItems(user, target); + display("Request decisions for "+target, decisions); + assertEquals("Wrong assign default decision", expectedDefaultDecision, decisions.getDefaultDecision()); + assertEquals("Unexpected number of allowed items", expectedAllowedItemQNames.length, decisions.getItemDecisionMap().size()); + + decisions.getItemDecisionMap().forEach( + (path,decision) -> { + assertEquals("wrong item "+path+" decision", AuthorizationDecisionType.ALLOW, decision); + QName lastPathName = path.lastNamed().getName(); + if (!Arrays.stream(expectedAllowedItemQNames).anyMatch(qname -> QNameUtil.match(qname, lastPathName) )) { + AssertJUnit.fail("Unexpected path "+path); + } + } + ); + } + + + protected void assertEncryptedUserPassword(String userOid, String expectedClearPassword) throws EncryptionException, ObjectNotFoundException, SchemaException { + OperationResult result = new OperationResult(AbstractIntegrationTest.class.getName()+".assertEncryptedUserPassword"); + PrismObject user = repositoryService.getObject(UserType.class, userOid, null, result); + result.computeStatus(); + TestUtil.assertSuccess(result); + assertEncryptedUserPassword(user, expectedClearPassword); + } + + protected void assertEncryptedUserPassword(PrismObject user, String expectedClearPassword) throws EncryptionException { + UserType userType = user.asObjectable(); + ProtectedStringType protectedActualPassword = userType.getCredentials().getPassword().getValue(); + String actualClearPassword = protector.decryptString(protectedActualPassword); + assertEquals("Wrong password for "+user, expectedClearPassword, actualClearPassword); + } protected void assertPasswordMetadata(PrismObject user, boolean create, XMLGregorianCalendar start, XMLGregorianCalendar end, String actorOid, String channel) { PrismContainer metadataContainer = user.findContainer(new ItemPath(UserType.F_CREDENTIALS, CredentialsType.F_PASSWORD, PasswordType.F_METADATA)); diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/util/MiscDataUtil.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/util/MiscDataUtil.java index 494d20e15ce..879c6881f8b 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/util/MiscDataUtil.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/util/MiscDataUtil.java @@ -398,7 +398,7 @@ public boolean isMemberOfActivitiGroup(UserType userType, String activitiGroupId } public boolean matches(ObjectReferenceType groupRef, ObjectReferenceType targetRef) { - return (ObjectTypeUtil.isMembershipRelation(targetRef.getRelation())) // TODO!!!!!!!!! + return (ObjectTypeUtil.isMembershipRelation(targetRef.getRelation())) // TODO reconsider if we allow managers here && targetRef.getOid().equals(groupRef.getOid()); } diff --git a/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/ItemSecurityDecisions.java b/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/ItemSecurityDecisions.java new file mode 100644 index 00000000000..6ee2919a110 --- /dev/null +++ b/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/ItemSecurityDecisions.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 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.security.api; + +import java.util.HashMap; +import java.util.Map; + +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.util.DebugDumpable; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationDecisionType; + +/** + * @author semancik + * + */ +public class ItemSecurityDecisions implements DebugDumpable { + + private AuthorizationDecisionType defaultDecision = null; + private Map itemDecisionMap = new HashMap<>(); + + public AuthorizationDecisionType getDefaultDecision() { + return defaultDecision; + } + + public void setDefaultDecision(AuthorizationDecisionType defaultDecision) { + this.defaultDecision = defaultDecision; + } + + public Map getItemDecisionMap() { + return itemDecisionMap; + } + + @Override + public String debugDump(int indent) { + StringBuilder sb = new StringBuilder(); + DebugUtil.debugDumpLabelLn(sb, "ItemSecurityDecisions", indent); + DebugUtil.debugDumpWithLabelToStringLn(sb, "defaultDecision", defaultDecision, indent + 1); + DebugUtil.debugDumpLabelLn(sb, "itemDecisionMap", indent + 1); + DebugUtil.debugDumpMapMultiLine(sb, itemDecisionMap, indent + 2); + return sb.toString(); + } + + @Override + public String toString() { + return "ItemSecurityDecisions(defaultDecision=" + defaultDecision + ", itemDecisionMap=" + itemDecisionMap + + ")"; + } + +} diff --git a/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/SecurityEnforcer.java b/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/SecurityEnforcer.java index d9138497ce7..2ffc88ed02a 100644 --- a/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/SecurityEnforcer.java +++ b/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/SecurityEnforcer.java @@ -24,6 +24,7 @@ import com.evolveum.midpoint.util.Producer; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationPhaseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; @@ -95,4 +96,9 @@ ObjectFilter preProcessObjectFilter T runPrivileged(Producer producer); + /** + * Returns decisions for individual items for "assign" authorization. This is usually applicable to assignment parameters. + */ + ItemSecurityDecisions getAllowedRequestAssignmentItems( MidPointPrincipal midPointPrincipal, PrismObject object, PrismObject target, OwnerResolver ownerResolver) throws SchemaException; + } diff --git a/repo/security-impl/src/main/java/com/evolveum/midpoint/security/impl/SecurityEnforcerImpl.java b/repo/security-impl/src/main/java/com/evolveum/midpoint/security/impl/SecurityEnforcerImpl.java index 8bedb73979b..8c14fa20cc1 100644 --- a/repo/security-impl/src/main/java/com/evolveum/midpoint/security/impl/SecurityEnforcerImpl.java +++ b/repo/security-impl/src/main/java/com/evolveum/midpoint/security/impl/SecurityEnforcerImpl.java @@ -29,6 +29,7 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.schema.util.ObjectQueryUtil; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.security.api.*; import com.evolveum.midpoint.util.Producer; import com.evolveum.midpoint.util.exception.AuthorizationException; @@ -63,6 +64,68 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.Item; +import com.evolveum.midpoint.prism.PrismContainer; +import com.evolveum.midpoint.prism.PrismContainerValue; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismObjectDefinition; +import com.evolveum.midpoint.prism.PrismReferenceDefinition; +import com.evolveum.midpoint.prism.Visitable; +import com.evolveum.midpoint.prism.Visitor; +import com.evolveum.midpoint.prism.delta.ContainerDelta; +import com.evolveum.midpoint.prism.delta.ItemDelta; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.query.AllFilter; +import com.evolveum.midpoint.prism.query.InOidFilter; +import com.evolveum.midpoint.prism.query.NoneFilter; +import com.evolveum.midpoint.prism.query.NotFilter; +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.query.OrgFilter; +import com.evolveum.midpoint.prism.query.QueryJaxbConvertor; +import com.evolveum.midpoint.prism.query.RefFilter; +import com.evolveum.midpoint.prism.query.TypeFilter; +import com.evolveum.midpoint.repo.api.RepositoryService; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.MiscSchemaUtil; +import com.evolveum.midpoint.schema.util.ObjectQueryUtil; +import com.evolveum.midpoint.security.api.Authorization; +import com.evolveum.midpoint.security.api.AuthorizationConstants; +import com.evolveum.midpoint.security.api.ItemSecurityDecisions; +import com.evolveum.midpoint.security.api.MidPointPrincipal; +import com.evolveum.midpoint.security.api.ObjectSecurityConstraints; +import com.evolveum.midpoint.security.api.OwnerResolver; +import com.evolveum.midpoint.security.api.SecurityEnforcer; +import com.evolveum.midpoint.security.api.SecurityUtil; +import com.evolveum.midpoint.security.api.UserProfileService; +import com.evolveum.midpoint.util.Producer; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.AuthorizationException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; +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.AbstractRoleType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationDecisionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationPhaseType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SubjectedObjectSelectorType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgRelationObjectSpecificationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgScopeType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OwnedObjectSelectorType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SpecialObjectSpecificationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; /** * @author Radovan Semancik @@ -261,12 +324,31 @@ private boolean isAuthorizedInterna return allow; } - private boolean processAuthorizationObject(PrismObject object, final Collection allowedItems) { + private boolean processAuthorizationObject(PrismContainer object, final Collection allowedItems) { + final MutableBoolean itemDecision = new MutableBoolean(true); + object.accept(createItemVisitor(allowedItems, itemDecision)); + return itemDecision.booleanValue(); + } + + private boolean processAuthorizationContainerDelta(ContainerDelta cval, final Collection allowedItems) { final MutableBoolean itemDecision = new MutableBoolean(true); - Visitor visitor = new Visitor() { + cval.accept(createItemVisitor(allowedItems, itemDecision)); + return itemDecision.booleanValue(); + } + + private Visitor createItemVisitor(final Collection allowedItems, final MutableBoolean itemDecision) { + return new Visitor() { @Override public void visit(Visitable visitable) { if (visitable instanceof Item) { + + // TODO: problem with empty containers such as + // orderConstraint in assignment. Skip all + // empty items ... for now. + if (((Item)visitable).isEmpty()) { + return; + } + ItemPath itemPath = ((Item)visitable).getPath(); if (itemPath != null && !itemPath.isEmpty()) { if (!isInList(itemPath, allowedItems)) { @@ -277,8 +359,6 @@ public void visit(Visitable visitable) { } } }; - object.accept(visitor); - return itemDecision.booleanValue(); } private boolean processAuthorizationDelta(ObjectDelta delta, final Collection allowedItems) { @@ -287,9 +367,17 @@ private boolean processAuthorizationDelta(ObjectDelta } else { for (ItemDelta itemDelta: delta.getModifications()) { ItemPath itemPath = itemDelta.getPath(); - if (!isInList(itemPath, allowedItems)) { - LOGGER.trace(" DENY operation because item {} in the delta is not allowed", itemPath); - return false; + if (itemDelta instanceof ContainerDelta) { + if (!isInList(itemPath, allowedItems)) { + if (!processAuthorizationContainerDelta((ContainerDelta)itemDelta, allowedItems)) { + return false; + } + } + } else { + if (!isInList(itemPath, allowedItems)) { + LOGGER.trace(" DENY operation because item {} in the delta is not allowed", itemPath); + return false; + } } } return true; @@ -419,20 +507,20 @@ private boolean isApplicable(SubjectedObjectSelectorType ownerResolver = userProfileService; if (ownerResolver == null) { LOGGER.trace(" {}: owner object spec not applicable for {}, object OID {} because there is no owner resolver", - new Object[]{autzHumanReadableDesc, desc, object.getOid()}); + autzHumanReadableDesc, desc, object.getOid()); return false; } } - PrismObject owner = ownerResolver.resolveOwner((PrismObject)object); + PrismObject owner = ownerResolver.resolveOwner(object); if (owner == null) { LOGGER.trace(" {}: owner object spec not applicable for {}, object OID {} because it has no owner", - new Object[]{autzHumanReadableDesc, desc, object.getOid()}); + autzHumanReadableDesc, desc, object.getOid()); return false; } boolean ownerApplicable = isApplicable(ownerSpec, owner, principal, ownerResolver, "owner of "+desc, autzHumanReadableDesc); if (!ownerApplicable) { LOGGER.trace(" {}: owner object spec not applicable for {}, object OID {} because owner does not match (owner={})", - new Object[]{autzHumanReadableDesc, desc, object.getOid(), owner}); + autzHumanReadableDesc, desc, object.getOid(), owner); return false; } } @@ -476,7 +564,7 @@ private boolean hasParentOrgRef(PrismObject object, St } private boolean isApplicableItem(Authorization autz, - PrismObject object, ObjectDelta delta) { + PrismObject object, ObjectDelta delta) throws SchemaException { List itemPaths = autz.getItem(); if (itemPaths == null || itemPaths.isEmpty()) { // No item constraints. Applicable for all items. @@ -487,8 +575,7 @@ private boolean isApplicableItem(Au ItemPath itemPath = itemPathType.getItemPath(); if (delta == null) { if (object != null) { - Item item = object.findItem(itemPath); - if (item != null && !item.isEmpty()) { + if (object.containsItem(itemPath, false)) { LOGGER.trace(" applicable object item "+itemPath); return true; } @@ -653,51 +740,46 @@ public ObjectSecurityConstraints compileSecurityConstrain ObjectSecurityConstraintsImpl objectSecurityConstraints = new ObjectSecurityConstraintsImpl(); Collection authorities = getAuthorities(principal); if (authorities != null) { - for (GrantedAuthority authority: authorities) { - if (authority instanceof Authorization) { - Authorization autz = (Authorization)authority; - String autzHumanReadableDesc = autz.getHumanReadableDesc(); - LOGGER.trace("Evaluating {}", autzHumanReadableDesc); - - // skip action applicability evaluation. We are interested in all actions - - // object - if (isApplicable(autz.getObject(), object, principal, ownerResolver, "object", autzHumanReadableDesc)) { - LOGGER.trace(" {} applicable for object {} (continuing evaluation)", autzHumanReadableDesc, object); + for (Authorization autz: authorities) { + String autzHumanReadableDesc = autz.getHumanReadableDesc(); + LOGGER.trace("Evaluating {}", autzHumanReadableDesc); + + // skip action applicability evaluation. We are interested in all actions + + // object + if (isApplicable(autz.getObject(), object, principal, ownerResolver, "object", autzHumanReadableDesc)) { + LOGGER.trace(" {} applicable for object {} (continuing evaluation)", autzHumanReadableDesc, object); + } else { + LOGGER.trace(" {} not applicable for object {}, none of the object specifications match (breaking evaluation)", + autzHumanReadableDesc, object); + continue; + } + + // skip target applicability evaluation. We do not have a target here + + List actions = autz.getAction(); + AuthorizationPhaseType phase = autz.getPhase(); + AuthorizationDecisionType decision = autz.getDecision(); + if (decision == null || decision == AuthorizationDecisionType.ALLOW) { + Collection items = getItems(autz); + if (items == null || items.isEmpty()) { + applyDecision(objectSecurityConstraints.getActionDecisionMap(), actions, phase, AuthorizationDecisionType.ALLOW); } else { - LOGGER.trace(" {} not applicable for object {}, none of the object specifications match (breaking evaluation)", - autzHumanReadableDesc, object); - continue; - } - - // skip target applicability evaluation. We do not have a target here - - List actions = autz.getAction(); - AuthorizationPhaseType phase = autz.getPhase(); - AuthorizationDecisionType decision = autz.getDecision(); - if (decision == null || decision == AuthorizationDecisionType.ALLOW) { - Collection items = getItems(autz); - if (items == null || items.isEmpty()) { - applyDecision(objectSecurityConstraints.getActionDecisionMap(), actions, phase, AuthorizationDecisionType.ALLOW); - } else { - for (ItemPath item: items) { - applyItemDecision(objectSecurityConstraints.getItemConstraintMap(), item, actions, phase, AuthorizationDecisionType.ALLOW); - } + for (ItemPath item: items) { + applyItemDecision(objectSecurityConstraints.getItemConstraintMap(), item, actions, phase, AuthorizationDecisionType.ALLOW); } + } + } else { + Collection items = getItems(autz); + if (items == null || items.isEmpty()) { + applyDecision(objectSecurityConstraints.getActionDecisionMap(), actions, phase, AuthorizationDecisionType.DENY); } else { - Collection items = getItems(autz); - if (items == null || items.isEmpty()) { - applyDecision(objectSecurityConstraints.getActionDecisionMap(), actions, phase, AuthorizationDecisionType.DENY); - } else { - for (ItemPath item: items) { - applyItemDecision(objectSecurityConstraints.getItemConstraintMap(), item, actions, phase, AuthorizationDecisionType.DENY); - } + for (ItemPath item: items) { + applyItemDecision(objectSecurityConstraints.getItemConstraintMap(), item, actions, phase, AuthorizationDecisionType.DENY); } } - - } else { - LOGGER.warn("Unknown authority type {} in user {}", authority.getClass(), getUsername(principal)); } + } } @@ -882,8 +964,9 @@ private ObjectFilter preProcessObje PrismReferenceDefinition ownerRefDef = objectDefinition.findReferenceDefinition(ownerRefPath); S_AtomicFilterExit builder = QueryBuilder.queryFor(AbstractRoleType.class, prismContext) .item(ownerRefPath, ownerRefDef).ref(principal.getUser().getOid()); + // TODO don't understand this code for (ObjectReferenceType subjectParentOrgRef: principal.getUser().getParentOrgRef()) { - if (MiscSchemaUtil.compareRelation(null, subjectParentOrgRef.getRelation())) { // TODO!!!!!!!!! + if (ObjectTypeUtil.isDefaultRelation(subjectParentOrgRef.getRelation())) { builder = builder.or().item(ownerRefPath, ownerRefDef).ref(subjectParentOrgRef.getOid()); } } @@ -950,7 +1033,7 @@ private ObjectFilter preProcessObje ObjectFilter objSpecOrgRelationFilter = null; QName subjectRelation = specOrgRelation.getSubjectRelation(); for (ObjectReferenceType subjectParentOrgRef: principal.getUser().getParentOrgRef()) { - if (MiscSchemaUtil.compareRelation(subjectRelation, subjectParentOrgRef.getRelation())) { // TODO !!!!!!!!!!!!!!!!! + if (MiscSchemaUtil.compareRelation(subjectRelation, subjectParentOrgRef.getRelation())) { S_FilterEntryOrEmpty q = QueryBuilder.queryFor(ObjectType.class, prismContext); S_AtomicFilterExit q2; if (specOrgRelation.getScope() == null || specOrgRelation.getScope() == OrgScopeType.ALL_DESCENDANTS) { @@ -1055,8 +1138,88 @@ private Object getUsername(MidPointPrincipal principal) { return principal==null?null:principal.getUsername(); } + @Override + public ItemSecurityDecisions getAllowedRequestAssignmentItems( MidPointPrincipal midPointPrincipal, PrismObject object, PrismObject target, OwnerResolver ownerResolver) throws SchemaException { + + ItemSecurityDecisions decisions = new ItemSecurityDecisions(); + + for(Authorization autz: getAuthorities(midPointPrincipal)) { + String autzHumanReadableDesc = autz.getHumanReadableDesc(); + LOGGER.trace("Evaluating {}", autzHumanReadableDesc); + + // First check if the authorization is applicable. + + // action + if (!autz.getAction().contains(AuthorizationConstants.AUTZ_UI_ASSIGN_ACTION_URL) && !autz.getAction().contains(AuthorizationConstants.AUTZ_ALL_URL)) { + LOGGER.trace(" {} not applicable for operation {}", autzHumanReadableDesc, AuthorizationConstants.AUTZ_UI_ASSIGN_ACTION_URL); + continue; + } + + // phase + if (autz.getPhase() != null && autz.getPhase() != AuthorizationPhaseType.REQUEST) { + LOGGER.trace(" {} is not applicable for phase {} (breaking evaluation)", autzHumanReadableDesc, AuthorizationPhaseType.REQUEST); + continue; + } + + // object + if (isApplicable(autz.getObject(), object, midPointPrincipal, ownerResolver, "object", autzHumanReadableDesc)) { + LOGGER.trace(" {} applicable for object {} (continuing evaluation)", autzHumanReadableDesc, object); + } else { + LOGGER.trace(" {} not applicable for object {}, none of the object specifications match (breaking evaluation)", + autzHumanReadableDesc, object); + continue; + } + + // target + if (isApplicable(autz.getTarget(), target, midPointPrincipal, ownerResolver, "target", autzHumanReadableDesc)) { + LOGGER.trace(" {} applicable for target {} (continuing evaluation)", autzHumanReadableDesc, object); + } else { + LOGGER.trace(" {} not applicable for target {}, none of the target specifications match (breaking evaluation)", + autzHumanReadableDesc, object); + continue; + } + + // authority is applicable to this situation. now we can process the decision. + AuthorizationDecisionType decision = autz.getDecision(); + if (decision == null || decision == AuthorizationDecisionType.ALLOW) { + Collection items = getItems(autz); + if (items.isEmpty()) { + LOGGER.trace(" {}: ALLOW all items (but continue evaluation)", autzHumanReadableDesc); + if (decisions.getDefaultDecision() != AuthorizationDecisionType.DENY) { + decisions.setDefaultDecision(AuthorizationDecisionType.ALLOW); + } + } else { + for(ItemPath item: items) { + LOGGER.trace(" {}: ALLOW item {} (but continue evaluation)", autzHumanReadableDesc, item); + if (decisions.getItemDecisionMap().get(item) != AuthorizationDecisionType.DENY) { + decisions.getItemDecisionMap().put(item, AuthorizationDecisionType.ALLOW); + } + } + } + } else { + Collection items = getItems(autz); + if (items.isEmpty()) { + LOGGER.trace(" {}: DENY all items (breaking evaluation)", autzHumanReadableDesc); + // Total deny. Reset everything. Return just deny + decisions = new ItemSecurityDecisions(); + decisions.setDefaultDecision(AuthorizationDecisionType.DENY); + break; + } else { + for(ItemPath item: items) { + LOGGER.trace(" {}: DENY item {} (but continue evaluation)", autzHumanReadableDesc, item); + decisions.getItemDecisionMap().put(item, AuthorizationDecisionType.DENY); + } + } + } + + } + + return decisions; + } + @Override public T runAs(Producer producer, PrismObject user) throws SchemaException { + LOGGER.debug("Running {} as {}", producer, user); Authentication origAuthentication = SecurityContextHolder.getContext().getAuthentication(); setupPreAuthenticatedSecurityContext(user);