Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
skublik committed Dec 6, 2023
2 parents 9241b65 + d153085 commit 2e4eff7
Show file tree
Hide file tree
Showing 9 changed files with 470 additions and 3 deletions.
15 changes: 13 additions & 2 deletions docs/schema/archetypes/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,22 @@ Following table lists commonly-used archetypes and their mapping to primary midP


| ServiceType
| Application, Device, Printer, Provider, Network, Web API endpoint
| Application, Device, Printer, Provider, Network, Web API endpoint, Robot


|===

== Birthrights

Archetypes are the primary tool to define a glossref:birthright[birthright provisioning].
Archetypes behave as xref:/midpoint/reference/roles-policies/rbac/[roles].
Therefore, any privileges specified in archetype xref:/midpoint/reference/roles-policies/assignment/assignment-vs-inducement/[inducements] are automatically applied to all the objects that have the archetype.
This approach can be used to apply _birthright_ to the objects, e.g. it can specify baseline privileges for all employees or students.

Moreover, archetypes may work as xref:/midpoint/reference/roles-policies/metaroles/policy/[metaroles], specifying common behavior for object types.
For example, `Project` archetypes may specify common behavior for all projects, including baseline authorization for project members and managers.
Overall, archetypes provide very flexible and powerful mechanism to specify _birthrights_.

== Archetype Definition

Archetype definition is a special midPoint object (ArchetypeType).
Expand Down Expand Up @@ -149,4 +160,4 @@ See xref:/midpoint/features/planned/archetypes/[Archetype Improvements (Planned

* xref:/midpoint/reference/roles-policies/rbac/[Advanced Hybrid RBAC]

* xref:/midpoint/reference/misc/services/[Services]
* xref:/midpoint/reference/misc/services/[Services]
2 changes: 1 addition & 1 deletion docs/security/credentials/identity-recovery/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ More https://github.com/Evolveum/schrodinger/tree/main/schrodinger-tests/src/tes

=== Archetype selection and Correlation authentication modules
Archetype selection and Correlation authentication modules are configured in the `modules` part of the `authentication` section in the security policy object.
Information about Archetype selection and Correlation authentication modules can be found in the xref:/midpoint/reference/security/authentication/flexible-authentication/configuration.adoc/[flexible authentication configuration].
Information about Archetype selection and Correlation authentication modules can be found in the xref:/midpoint/reference/security/authentication/flexible-authentication/configuration/[flexible authentication configuration].

The following Archetype selection module gives the possibility to select among 3 archetypes: Student, Applicant and Teacher.
Undefined archetype option is also allowed.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.schema.util.cleanup;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;

import javax.xml.namespace.QName;
import java.util.*;

/**
* Utility class that can be used to process objects and remove unwanted items.
* By default, it removes all operational items and all items marked with optionalCleanup.
*
* This behaviour can be configured via {@link CleanupActionProcessor#removeAskActionItemsByDefault}
* and {@link CleanupActionProcessor#setPaths(List)}.
*/
public class CleanupActionProcessor {

private CleanupEventListener listener;

private boolean removeAskActionItemsByDefault = true;

private final Map<QName, Map<ItemPath, CleanupPathAction>> paths = new HashMap<>();

public void setListener(CleanupEventListener listener) {
this.listener = listener;
}

public void setPaths(List<CleanupPath> paths) {
if (paths == null) {
paths = new ArrayList<>();
}

this.paths.clear();
for (CleanupPath path : paths) {
Map<ItemPath, CleanupPathAction> actions = this.paths.computeIfAbsent(path.getType(), k -> new HashMap<>());
actions.put(path.getPath(), path.getAction());
}
}

public void setRemoveAskActionItemsByDefault(boolean removeAskActionItemsByDefault) {
this.removeAskActionItemsByDefault = removeAskActionItemsByDefault;
}

public <O extends ObjectType> void process(PrismObject<O> object) {
processItemRecursively(object, ItemPath.EMPTY_PATH, new HashMap<>());
}

private boolean processItemRecursively(Item<?, ?> item, ItemPath currentPath, Map<Item<?, ?>, CleanupPathAction> customItemActions) {
boolean remove = processItem(item, currentPath, customItemActions);
if (remove) {
return true;
}

if (item instanceof PrismContainer<?>) {
boolean emptyBefore = item.isEmpty();

final List<Item<?, ?>> toBeRemoved = new ArrayList<>();

for (PrismContainerValue<?> value : (List<PrismContainerValue<?>>) item.getValues()) {
Collection<Item<?, ?>> items = value.getItems();
for (Item<?, ?> i : items) {
if (processItemRecursively(i, currentPath.append(i.getElementName()), customItemActions)) {
toBeRemoved.add(i);
}
}

items.removeAll(toBeRemoved);
}

return !emptyBefore && item.isEmpty();
}

return false;

// probably nothing to do for PrismProperty, PrismReference
// todo maybe connectorRef, passwords...
}

/**
* @param item
* @param currentPath
* @param customItemActions
* @return true if item should be removed, false otherwise
*/
private boolean processItem(Item<?, ?> item, ItemPath currentPath, Map<Item<?, ?>, CleanupPathAction> customItemActions) {
final ItemDefinition<?> def = item.getDefinition();
if (def != null) {
updateCustomItemActions(item, customItemActions, def.getTypeName());
}

CleanupPathAction customAction = customItemActions.get(item);
if (customAction != null) {
return switch (customAction) {
case REMOVE -> true;
case ASK -> fireOnCleanupItemEvent(item, currentPath);
default -> false;
};
}

if (def == null) {
return false;
}

if (def.isOperational()) {
return true;
}

if (def.isOptionalCleanup()) {
return fireOnCleanupItemEvent(item, currentPath);
}

return false;
}

private void updateCustomItemActions(Item<?, ?> item, Map<Item<?, ?>, CleanupPathAction> customItemActions, QName type) {
Map<ItemPath, CleanupPathAction> actions = paths.getOrDefault(type, Map.of());
if (actions.isEmpty()) {
return;
}

actions.forEach((path, action) -> {
if (path.isEmpty()) {
customItemActions.put(item, action);
return;
}

List<Item<?, ?>> foundItems = findItems(item, path);
foundItems.forEach(i -> customItemActions.put(i, action));
});
}

private List<Item<?, ?>> findItems(Item<?, ?> parent, ItemPath named) {
List<Item<?, ?>> foundItems = new ArrayList<>();

if (!(parent instanceof PrismContainer<?>)) {
return foundItems;
}

for (PrismContainerValue<?> value : (List<PrismContainerValue<?>>) parent.getValues()) {
findItems(value, named, foundItems);
}

return foundItems;
}

private void findItems(PrismContainerValue<?> parent, ItemPath named, List<Item<?, ?>> foundItems) {
if (named.isEmpty()) {
return;
}

ItemPath first = named.firstToName();
ItemPath rest = named.rest();

Item<?, ?> found = parent.findItem(first);
if (rest.isEmpty()) {
if (found != null) {
foundItems.add(found);
}

return;
}

if (!(found instanceof PrismContainer<?>)) {
return;
}

for (PrismContainerValue<?> value : (List<PrismContainerValue<?>>) found.getValues()) {
findItems(value, rest, foundItems);
}
}

private boolean fireOnCleanupItemEvent(Item<?, ?> item, ItemPath path) {
if (listener == null) {
return removeAskActionItemsByDefault;
}

return listener.onCleanupItem(new CleanupEvent(item, path));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.schema.util.cleanup;

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

public class CleanupEvent {

private final Item<?, ?> item;

private final ItemPath path;

public CleanupEvent(Item<?, ?> item, ItemPath path) {
this.item = item;
this.path = path;
}

public Item<?, ?> getItem() {
return item;
}

public ItemPath getPath() {
return path;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.schema.util.cleanup;

/**
* Listener that can be used to react on cleanup events created for items that are marked
* with action {@link CleanupPathAction#ASK}.
*/
@FunctionalInterface
public interface CleanupEventListener {

/**
* Method that allows consumers to react on cleanup event marked with action {@link CleanupPathAction#ASK}.
*
* @return true if the item should be removed, false otherwise
*/
boolean onCleanupItem(CleanupEvent event);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.evolveum.midpoint.schema.util.cleanup;

import java.io.Serializable;
import java.util.Comparator;
import java.util.Objects;
import javax.xml.namespace.QName;

import org.jetbrains.annotations.NotNull;

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

/**
* Class that defines schema type (using {@link QName}). item path and action that should be used during cleanup operation
*/
public class CleanupPath implements Serializable, Comparable<CleanupPath> {

private QName type;

private ItemPath path;

private CleanupPathAction action;

@SuppressWarnings("unused")
public CleanupPath() {
}

public CleanupPath(QName type, ItemPath path, CleanupPathAction action) {
this.type = type;
this.path = path;
this.action = action;
}

public QName getType() {
return type;
}

public void setType(QName type) {
this.type = type;
}

public ItemPath getPath() {
return path;
}

public void setPath(ItemPath path) {
this.path = path;
}

public CleanupPathAction getAction() {
return action;
}

public void setAction(CleanupPathAction action) {
this.action = action;
}

@Override
public boolean equals(Object o) {
if (this == o) {return true;}
if (o == null || getClass() != o.getClass()) {return false;}
CleanupPath that = (CleanupPath) o;
return type == that.type && Objects.equals(path, that.path) && action == that.action;
}

@Override
public int hashCode() {
return Objects.hash(type, path, action);
}

private String getTypeAsString() {
return type != null ? QNameUtil.qNameToUri(type) : null;
}

private String getPathAsString() {
return path != null ? path.toString() : null;
}

@Override
public int compareTo(@NotNull CleanupPath o) {
return Comparator.nullsLast(Comparator.comparing(CleanupPath::getTypeAsString))
.thenComparing(Comparator.nullsLast(Comparator.comparing(CleanupPath::getPathAsString)))
.thenComparing(Comparator.nullsLast(Comparator.comparing(CleanupPath::getAction)))
.compare(this, o);
}

public CleanupPath copy() {
return new CleanupPath(type, path, action);
}
}

0 comments on commit 2e4eff7

Please sign in to comment.