Skip to content

Commit

Permalink
ModelInteractionService.canSearch(), many authorization improvements,…
Browse files Browse the repository at this point in the history
… items in search (MID-3916)
  • Loading branch information
semancik committed May 22, 2017
1 parent d8507e9 commit 6311e49
Show file tree
Hide file tree
Showing 11 changed files with 297 additions and 42 deletions.
Expand Up @@ -198,6 +198,13 @@ public <T extends ObjectType, O extends ObjectType> ObjectFilter preProcessObjec
return securityEnforcer.preProcessObjectFilter(operationUrl, phase, objectType, object, origFilter);
}

@Override
public <T extends ObjectType, O extends ObjectType> boolean canSearch(String operationUrl,
AuthorizationPhaseType phase, Class<T> objectType, PrismObject<O> object, ObjectFilter filter)
throws SchemaException {
return securityEnforcer.canSearch(operationUrl, phase, objectType, object, filter);
}

@Override
public <T> T runAs(Producer<T> producer, PrismObject<UserType> user) throws SchemaException {
return securityEnforcer.runAs(producer, user);
Expand Down
@@ -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.
Expand Down Expand Up @@ -30,7 +30,7 @@
* @author lazyman
* @author mederly
*/
public class ExistsFilter extends ObjectFilter {
public class ExistsFilter extends ObjectFilter implements ItemFilter {

@NotNull private final ItemPath fullPath;
private ItemDefinition definition;
Expand All @@ -44,6 +44,7 @@ public ExistsFilter(@NotNull ItemPath fullPath, ItemDefinition definition, Objec
}

@NotNull
@Override
public ItemPath getFullPath() {
return fullPath;
}
Expand Down
@@ -0,0 +1,26 @@
/*
* 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.prism.query;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.path.ItemPath;

public interface ItemFilter {

@NotNull ItemPath getFullPath();

}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2015 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.
Expand Down Expand Up @@ -35,7 +35,7 @@
import java.util.List;
import java.util.Objects;

public abstract class ValueFilter<V extends PrismValue, D extends ItemDefinition> extends ObjectFilter implements Itemable {
public abstract class ValueFilter<V extends PrismValue, D extends ItemDefinition> extends ObjectFilter implements Itemable, ItemFilter {
private static final long serialVersionUID = 1L;

@NotNull private final ItemPath fullPath;
Expand Down Expand Up @@ -80,6 +80,7 @@ protected ValueFilter(@NotNull ItemPath fullPath, @Nullable D definition, @Nulla
}

@NotNull
@Override
public ItemPath getFullPath() {
return fullPath;
}
Expand Down
Expand Up @@ -233,7 +233,7 @@ public abstract class SchemaConstants {
UserType.F_CREDENTIALS, CredentialsType.F_SECURITY_QUESTIONS, PasswordType.F_FAILED_LOGINS);
public static final ItemPath PATH_LINK_REF = new ItemPath(FocusType.F_LINK_REF);
public static final ItemPath PATH_LIFECYCLE_STATE = new ItemPath(ObjectType.F_LIFECYCLE_STATE);
public static final Object PATH_ROLE_MEMBERSHIP_REF = new ItemPath(FocusType.F_ROLE_MEMBERSHIP_REF);
public static final ItemPath PATH_ROLE_MEMBERSHIP_REF = new ItemPath(FocusType.F_ROLE_MEMBERSHIP_REF);

public static final String NS_PROVISIONING = NS_MIDPOINT_PUBLIC + "/provisioning";
public static final String NS_PROVISIONING_LIVE_SYNC = NS_PROVISIONING + "/liveSync-3";
Expand Down
Expand Up @@ -22,6 +22,7 @@
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.ResourceShadowDiscriminator;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.statistics.ConnectorOperationalStatus;
Expand Down Expand Up @@ -142,6 +143,8 @@ <F extends ObjectType> ModelContext<F> previewChanges(
*/
<F extends FocusType> RoleSelectionSpecification getAssignableRoleSpecification(PrismObject<F> focus, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ConfigurationException;

<T extends ObjectType, O extends ObjectType> boolean canSearch(Class<T> resultType, Class<O> objectType, String objectOid, ObjectQuery query, Task task, OperationResult result) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException ;

/**
* 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.
Expand Down
Expand Up @@ -49,6 +49,7 @@
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.OrFilter;
import com.evolveum.midpoint.prism.query.RefFilter;
import com.evolveum.midpoint.prism.query.TypeFilter;
Expand Down Expand Up @@ -597,6 +598,18 @@ private RoleSelectionSpecEntry getRoleSelectionSpecEq(EqualFilter<String> eqFilt
return null;
}



@Override
public <T extends ObjectType, O extends ObjectType> boolean canSearch(Class<T> resultType,
Class<O> objectType, String objectOid, ObjectQuery query, Task task, OperationResult result) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
PrismObject<O> object = null;
if (objectOid != null) {
object = (PrismObject<O>) objectResolver.getObject(objectType, objectOid, null, task, result).asPrismObject();
}
return securityEnforcer.canSearch(ModelAuthorizationAction.READ.getUrl(), null, resultType, object, query.getFilter());
}

@Override
public AuthenticationsPolicyType getAuthenticationPolicy(PrismObject<UserType> user, Task task,
OperationResult parentResult) throws ObjectNotFoundException, SchemaException {
Expand Down
Expand Up @@ -25,10 +25,13 @@
import org.springframework.test.context.ContextConfiguration;
import org.testng.annotations.Test;

import com.evolveum.midpoint.model.api.ModelAuthorizationAction;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.IntegrationTestTools;
import com.evolveum.midpoint.test.util.TestUtil;
Expand Down Expand Up @@ -306,7 +309,7 @@ public void test150AutzJackApproverUnassignRoles() throws Exception {
// WHEN
TestUtil.displayWhen(TEST_NAME);

assertGetAllow(RoleType.class, ROLE_ORDINARY_OID);
PrismObject<RoleType> assertGetAllow = assertGetAllow(RoleType.class, ROLE_ORDINARY_OID);
assertGetDeny(RoleType.class, ROLE_PERSONA_ADMIN_OID); // no assignment
assertGetDeny(RoleType.class, ROLE_APPROVER_UNASSIGN_ROLES_OID); // assignment exists, but wrong relation

Expand All @@ -326,14 +329,32 @@ public void test150AutzJackApproverUnassignRoles() throws Exception {
// allow read, so no users are returned
assertSearch(UserType.class, null, 0);

assertSearch(UserType.class,
QueryBuilder.queryFor(UserType.class, prismContext).item(UserType.F_ROLE_MEMBERSHIP_REF).ref(ROLE_APPROVER_UNASSIGN_ROLES_OID).build(),
0);
assertSearch(UserType.class, createMembersQuery(ROLE_APPROVER_UNASSIGN_ROLES_OID), 0);

assert15xCommon();
}

@Test
private void assertCanSearchRoleMemberUsers(String roleOid, boolean expectedResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
assertCanSearch("Search user members of role "+roleOid, UserType.class,
null, null, createMembersQuery(roleOid), expectedResult);
}


private <T extends ObjectType, O extends ObjectType> void assertCanSearch(String message, Class<T> resultType, Class<O> objectType, String objectOid, ObjectQuery query, boolean expectedResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException {
Task task = createTask("assertCanSearch");
OperationResult result = task.getResult();
boolean decision = modelInteractionService.canSearch(resultType, objectType, objectOid, query, task, result);
assertSuccess(result);
assertEquals(message+": wrong search decision", expectedResult, decision);
}


private ObjectQuery createMembersQuery(String roleOid) {
return QueryBuilder.queryFor(UserType.class, prismContext).item(UserType.F_ROLE_MEMBERSHIP_REF).ref(roleOid).build();
}


@Test
public void test152AutzJackApproverUnassignRolesAndRead() throws Exception {
final String TEST_NAME = "test152AutzJackApproverUnassignRolesAndRead";
TestUtil.displayTestTile(this, TEST_NAME);
Expand Down Expand Up @@ -373,22 +394,22 @@ public void test152AutzJackApproverUnassignRolesAndRead() throws Exception {
private void assert15xCommon() throws Exception {

// list ordinary role members, this is allowed
assertSearch(UserType.class,
QueryBuilder.queryFor(UserType.class, prismContext).item(UserType.F_ROLE_MEMBERSHIP_REF).ref(ROLE_ORDINARY_OID).build(),
2);
assertSearch(UserType.class, createMembersQuery(ROLE_ORDINARY_OID), 2);

// MID-3916
// list approver role members, this is not allowed
// assertSearch(UserType.class,
// QueryBuilder.queryFor(UserType.class, prismContext).item(UserType.F_ROLE_MEMBERSHIP_REF).ref(ROLE_APPROVER_UNASSIGN_ROLES_OID).build(),
// 0);

assertCanSearchRoleMemberUsers(ROLE_ORDINARY_OID, true);
assertCanSearchRoleMemberUsers(ROLE_UNINTERESTING_OID, false);
assertCanSearchRoleMemberUsers(ROLE_APPROVER_UNASSIGN_ROLES_OID, false);

assertAllow("unassign ordinary role from cobb",
(task,result) -> unassignRole(userCobbOid, ROLE_ORDINARY_OID, task, result));

assertSearch(UserType.class,
QueryBuilder.queryFor(UserType.class, prismContext).item(UserType.F_ROLE_MEMBERSHIP_REF).ref(ROLE_ORDINARY_OID).build(),
1);
assertSearch(UserType.class, createMembersQuery(ROLE_ORDINARY_OID), 1);

// Jack is not approver of uninteresting role, so this should be denied
assertDeny("unassign uninteresting role from cobb",
Expand Down
Expand Up @@ -91,7 +91,10 @@ <O extends ObjectType, T extends ObjectType> void authorize(String operationUrl,
* The objectType parameter defines the class of the object for which should be the returned filter applicable.
*/
<T extends ObjectType, O extends ObjectType> ObjectFilter preProcessObjectFilter(String operationUrl, AuthorizationPhaseType phase,
Class<T> objectType, PrismObject<O> object, ObjectFilter origFilter) throws SchemaException;
Class<T> searchResultType, PrismObject<O> object, ObjectFilter origFilter) throws SchemaException;

<T extends ObjectType, O extends ObjectType> boolean canSearch(String operationUrl, AuthorizationPhaseType phase,
Class<T> searchResultType, PrismObject<O> object, ObjectFilter filter) throws SchemaException;

<T> T runAs(Producer<T> producer, PrismObject<UserType> user) throws SchemaException ;

Expand Down
@@ -0,0 +1,118 @@
/**
* 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.impl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.ItemFilter;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.security.api.Authorization;
import com.evolveum.midpoint.util.PrettyPrinter;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;

/**
* Helper class to SecurityEnforcer, used to evaluate query item authorizations.
*
* @author semancik
*/
public class QueryItemsSpec {

private static final Trace LOGGER = TraceManager.getTrace(QueryItemsSpec.class);

private List<ItemPath> requiredItems = new ArrayList<>();
private List<ItemPath> allowedItems = new ArrayList<>();
private boolean allItemsAllowed = false;

public List<ItemPath> getRequiredItems() {
return requiredItems;
}

public void addRequiredItem(ItemPath path) {
requiredItems.add(path);
}

public List<ItemPath> getAllowedItems() {
return allowedItems;
}

public void addAllowedItem(ItemPath path) {
allowedItems.add(path);
}

public void addAllowedItems(Authorization autz) {
if (autz.getItem().isEmpty()) {
allItemsAllowed = true;
} else {
for (ItemPathType itemPathType: autz.getItem()) {
addAllowedItem(itemPathType.getItemPath());
}
}
}

public void addRequiredItems(ObjectFilter filter) {
filter.accept(visitable -> {
if (visitable instanceof ItemFilter) {
requiredItems.add(((ItemFilter)visitable).getFullPath());
}
});
}


public List<ItemPath> evaluateUnsatisfierItems() {
List<ItemPath> unsatisfiedItems = new ArrayList<>();
if (!allItemsAllowed) {
for (ItemPath requiredItem: requiredItems) {
if (!ItemPath.containsEquivalent(allowedItems, requiredItem)) {
unsatisfiedItems.add(requiredItem);
}
}
}
return unsatisfiedItems;
}

public Object shortDump() {
StringBuilder sb = new StringBuilder();
sb.append("required: ");
dumpItems(sb, requiredItems);
sb.append("; allowed: ");
if (allItemsAllowed) {
sb.append("[all]");
} else {
dumpItems(sb, allowedItems);
}
return sb.toString();
}

private void dumpItems(StringBuilder sb, List<ItemPath> items) {
if (items.isEmpty()) {
sb.append("[none]");
} else {
Iterator<ItemPath> iterator = items.iterator();
while (iterator.hasNext()) {
sb.append(PrettyPrinter.prettyPrint(iterator.next()));
if (iterator.hasNext()) {
sb.append(",");
}
}
}
}

}

0 comments on commit 6311e49

Please sign in to comment.