From e023e9027edc0d734d11cb759259eaebb6d68bc9 Mon Sep 17 00:00:00 2001 From: Mathieu Date: Tue, 8 Nov 2022 05:35:05 +0100 Subject: [PATCH] Improve role management --- .../api/cms/directory/HierarchyUnit.java | 13 +++- .../src/org/argeo/cms/auth/CurrentUser.java | 2 +- .../src/org/argeo/cms/auth/RoleNameUtils.java | 38 +++++++---- .../src/org/argeo/cms/auth/SystemRole.java | 15 ++++- .../directory/ldap/AbstractLdapDirectory.java | 4 +- .../cms/directory/ldap/LdapHierarchyUnit.java | 20 ++++-- .../cms/internal/auth/ImpliedByPrincipal.java | 65 ++----------------- 7 files changed, 77 insertions(+), 80 deletions(-) diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/directory/HierarchyUnit.java b/org.argeo.api.cms/src/org/argeo/api/cms/directory/HierarchyUnit.java index 04593d94e..f04dc4648 100644 --- a/org.argeo.api.cms/src/org/argeo/api/cms/directory/HierarchyUnit.java +++ b/org.argeo.api.cms/src/org/argeo/api/cms/directory/HierarchyUnit.java @@ -26,7 +26,11 @@ public interface HierarchyUnit { * @return true if functional, false is technical * (e.g. People, Groups, etc.) */ - boolean isFunctional(); + default boolean isFunctional() { + return isType(Type.FUNCTIONAL); + } + + boolean isType(Type type); /** * The base of this organisational unit within the hierarchy. This would @@ -39,4 +43,11 @@ public interface HierarchyUnit { /** Its metadata (typically LDAP attributes). */ Dictionary getProperties(); + + enum Type { + PEOPLE, // + GROUPS, // + ROLES, // + FUNCTIONAL; + } } diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java index ee522a580..f2b4f0a58 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java @@ -75,7 +75,7 @@ public final class CurrentUser { return role.implied(currentSubject(), context); } - /** Implies this {@link SystemRole} in this context. */ + /** Implies this role name, also independently of the context. */ public final static boolean implies(String role, String context) { return SystemRole.implied(NamespaceUtils.parsePrefixedName(role), currentSubject(), context); } diff --git a/org.argeo.cms/src/org/argeo/cms/auth/RoleNameUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/RoleNameUtils.java index bf91f3941..a281c2f7d 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/RoleNameUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/RoleNameUtils.java @@ -1,25 +1,41 @@ package org.argeo.cms.auth; +import static org.argeo.api.acr.RuntimeNamespaceContext.getNamespaceContext; + +import javax.xml.namespace.QName; + +import org.argeo.api.acr.ArgeoNamespace; +import org.argeo.api.acr.NamespaceUtils; +import org.argeo.cms.directory.ldap.LdapNameUtils; + +/** Simplifies analysis of system roles. */ public class RoleNameUtils { + public static String getLastRdnValue(String dn) { + return LdapNameUtils.getLastRdnValue(dn); +// // we don't use LdapName for portability with Android +// // TODO make it more robust +// String[] parts = dn.split(","); +// String[] rdn = parts[0].split("="); +// return rdn[1]; + } + + public static QName getLastRdnAsName(String dn) { + String cn = getLastRdnValue(dn); + QName roleName = NamespaceUtils.parsePrefixedName(getNamespaceContext(), cn); + return roleName; + } - /* - * UTILITIES - */ - public final static String getLastRdnValue(String dn) { - // we don't use LdapName for portability with Android - // TODO make it more robust - String[] parts = dn.split(","); - String[] rdn = parts[0].split("="); - return rdn[1]; + public static boolean isSystemRole(QName roleName) { + return roleName.getNamespaceURI().equals(ArgeoNamespace.ROLE_NAMESPACE_URI); } - public final static String getParent(String dn) { + public static String getParent(String dn) { int index = dn.indexOf(','); return dn.substring(index + 1); } /** Up two levels. */ - public final static String getContext(String dn) { + public static String getContext(String dn) { return getParent(getParent(dn)); } } diff --git a/org.argeo.cms/src/org/argeo/cms/auth/SystemRole.java b/org.argeo.cms/src/org/argeo/cms/auth/SystemRole.java index 5d62d9803..646752d41 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/SystemRole.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/SystemRole.java @@ -8,13 +8,27 @@ import javax.xml.namespace.QName; import org.argeo.api.cms.CmsConstants; import org.argeo.cms.internal.auth.ImpliedByPrincipal; +/** A programmatic role. */ public interface SystemRole { QName getName(); + /** Whether this role is implied for this authenticated user. */ default boolean implied(Subject subject, String context) { return implied(getName(), subject, context); } + /** Whether this role is implied for this distinguished name. */ + default boolean implied(String dn, String context) { + String roleContext = RoleNameUtils.getContext(dn); + QName roleName = RoleNameUtils.getLastRdnAsName(dn); + return roleContext.equalsIgnoreCase(context) && getName().equals(roleName); + } + + /** + * Whether this role is implied for this authenticated subject. If context is + * null, it is not considered; this should be used to build user + * interfaces, but not to authorise. + */ static boolean implied(QName name, Subject subject, String context) { Set roles = subject.getPrincipals(ImpliedByPrincipal.class); for (ImpliedByPrincipal role : roles) { @@ -30,6 +44,5 @@ public interface SystemRole { } } return false; - } } diff --git a/org.argeo.cms/src/org/argeo/cms/directory/ldap/AbstractLdapDirectory.java b/org.argeo.cms/src/org/argeo/cms/directory/ldap/AbstractLdapDirectory.java index 06c33b011..9bc13ef00 100644 --- a/org.argeo.cms/src/org/argeo/cms/directory/ldap/AbstractLdapDirectory.java +++ b/org.argeo.cms/src/org/argeo/cms/directory/ldap/AbstractLdapDirectory.java @@ -320,8 +320,8 @@ public abstract class AbstractLdapDirectory implements Directory, XAResourceProv } @Override - public boolean isFunctional() { - return true; + public boolean isType(Type type) { + return Type.FUNCTIONAL.equals(type); } @Override diff --git a/org.argeo.cms/src/org/argeo/cms/directory/ldap/LdapHierarchyUnit.java b/org.argeo.cms/src/org/argeo/cms/directory/ldap/LdapHierarchyUnit.java index 7abf09885..0e005133a 100644 --- a/org.argeo.cms/src/org/argeo/cms/directory/ldap/LdapHierarchyUnit.java +++ b/org.argeo.cms/src/org/argeo/cms/directory/ldap/LdapHierarchyUnit.java @@ -9,14 +9,24 @@ import org.argeo.api.cms.directory.HierarchyUnit; /** LDIF/LDAP based implementation of {@link HierarchyUnit}. */ public class LdapHierarchyUnit extends DefaultLdapEntry implements HierarchyUnit { - private final boolean functional; +// private final boolean functional; + + private final Type type; public LdapHierarchyUnit(AbstractLdapDirectory directory, LdapName dn) { super(directory, dn); Rdn rdn = LdapNameUtils.getLastRdn(dn); - functional = !(directory.getUserBaseRdn().equals(rdn) || directory.getGroupBaseRdn().equals(rdn) - || directory.getSystemRoleBaseRdn().equals(rdn)); + if (directory.getUserBaseRdn().equals(rdn)) + type = Type.PEOPLE; + else if (directory.getGroupBaseRdn().equals(rdn)) + type = Type.GROUPS; + else if (directory.getSystemRoleBaseRdn().equals(rdn)) + type = Type.ROLES; + else + type = Type.FUNCTIONAL; +// functional = !(directory.getUserBaseRdn().equals(rdn) || directory.getGroupBaseRdn().equals(rdn) +// || directory.getSystemRoleBaseRdn().equals(rdn)); } @Override @@ -30,8 +40,8 @@ public class LdapHierarchyUnit extends DefaultLdapEntry implements HierarchyUnit } @Override - public boolean isFunctional() { - return functional; + public boolean isType(Type type) { + return this.type.equals(type); } @Override diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java index 7472908cd..15c47293e 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/ImpliedByPrincipal.java @@ -1,15 +1,11 @@ package org.argeo.cms.internal.auth; -import static org.argeo.api.acr.RuntimeNamespaceContext.getNamespaceContext; - import java.security.Principal; import java.util.HashSet; import java.util.Set; import javax.xml.namespace.QName; -import org.argeo.api.acr.ArgeoNamespace; -import org.argeo.api.acr.NamespaceUtils; import org.argeo.cms.auth.RoleNameUtils; import org.osgi.service.useradmin.Authorization; @@ -23,71 +19,25 @@ import org.osgi.service.useradmin.Authorization; */ public final class ImpliedByPrincipal implements Principal { private final String name; - private Set causes = new HashSet(); - - private QName roleName; -// private int type = Role.ROLE; + private final QName roleName; + private final boolean systemRole; + private final String context; - private boolean systemRole = false; - private String context; + private Set causes = new HashSet(); public ImpliedByPrincipal(String name, Principal userPrincipal) { this.name = name; - String cn = RoleNameUtils.getLastRdnValue(name); - roleName = NamespaceUtils.parsePrefixedName(getNamespaceContext(), cn); - if (roleName.getNamespaceURI().equals(ArgeoNamespace.ROLE_NAMESPACE_URI)) { - systemRole = true; - } + roleName = RoleNameUtils.getLastRdnAsName(name); + systemRole = RoleNameUtils.isSystemRole(roleName); context = RoleNameUtils.getContext(name); -// try { -// this.name = new LdapName(name); -// } catch (InvalidNameException e) { -// throw new IllegalArgumentException("Badly formatted role name", e); -// } if (userPrincipal != null) causes.add(userPrincipal); } -// public ImpliedByPrincipal(LdapName name, Principal userPrincipal) { -// this.name = name; -// if (userPrincipal != null) -// causes.add(userPrincipal); -// } - public String getName() { return name; } - /* - * USER ADMIN - */ -// public boolean addMember(Principal user) { -// throw new UnsupportedOperationException(); -// } -// -// public boolean removeMember(Principal user) { -// throw new UnsupportedOperationException(); -// } -// -// public boolean isMember(Principal member) { -// return causes.contains(member); -// } -// -// public Enumeration members() { -// return Collections.enumeration(causes); -// } -// -// -// /** Type of {@link Role}, if known. */ -// public int getType() { -// return type; -// } -// -// /** Not supported for the time being. */ -// public Dictionary getProperties() { -// throw new UnsupportedOperationException(); -// } - /* * OBJECT */ @@ -111,8 +61,6 @@ public final class ImpliedByPrincipal implements Principal { @Override public boolean equals(Object obj) { - // if (this == obj) - // return true; if (obj instanceof ImpliedByPrincipal) { ImpliedByPrincipal that = (ImpliedByPrincipal) obj; // TODO check members too? @@ -123,7 +71,6 @@ public final class ImpliedByPrincipal implements Principal { @Override public String toString() { - // return name.toString() + " implied by " + causes; return name.toString(); } } -- 2.30.2