diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java index eadbd82675889f7a1372a238df23935ddf4690ad..9157966d3b1ca264d9575daf7ad5aca5a5889adf 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java @@ -86,9 +86,11 @@ public class LdapConfig public static final String LDAP_PASSWD = "passwd"; public static final String LDAP_USERS_DN = "usersDn"; public static final String LDAP_GROUPS_DN = "groupsDn"; + public static final String LDAP_ADMIN_GROUPS_DN = "adminGroupsDn"; private String usersDN; private String groupsDN; + private String adminGroupsDN; private String server; private int port; private String adminUserDN; @@ -156,18 +158,26 @@ public class LdapConfig throw new RuntimeException("failed to read property " + LDAP_GROUPS_DN); } + + String ldapAdminGroupsDn = config.getProperty(LDAP_ADMIN_GROUPS_DN); + if (!StringUtil.hasText(ldapAdminGroupsDn)) + { + throw new RuntimeException("failed to read property " + + LDAP_ADMIN_GROUPS_DN); + } return new LdapConfig(server, Integer.valueOf(port), ldapAdmin, - ldapPasswd, ldapUsersDn, ldapGroupsDn); + ldapPasswd, ldapUsersDn, ldapGroupsDn, + ldapAdminGroupsDn); } public LdapConfig(String server, int port, String adminUserDN, - String adminPasswd, String usersDN, String groupsDN) + String adminPasswd, String usersDN, String groupsDN, + String adminGroupsDN) { if (!StringUtil.hasText(server)) { - throw new IllegalArgumentException("Illegal LDAP server name: " + - server); + throw new IllegalArgumentException("Illegal LDAP server name"); } if (port < 0) { @@ -176,23 +186,23 @@ public class LdapConfig } if (!StringUtil.hasText(adminUserDN)) { - throw new IllegalArgumentException("Illegal Admin DN: " + - adminUserDN); + throw new IllegalArgumentException("Illegal Admin DN"); } if (!StringUtil.hasText(adminPasswd)) { - throw new IllegalArgumentException("Illegal Admin password: " + - adminPasswd); + throw new IllegalArgumentException("Illegal Admin password"); } if (!StringUtil.hasText(usersDN)) { - throw new IllegalArgumentException("Illegal users LDAP DN: " + - usersDN); + throw new IllegalArgumentException("Illegal users LDAP DN"); } if (!StringUtil.hasText(groupsDN)) { - throw new IllegalArgumentException("Illegal groups LDAP DN: " + - groupsDN); + throw new IllegalArgumentException("Illegal groups LDAP DN"); + } + if (!StringUtil.hasText(adminGroupsDN)) + { + throw new IllegalArgumentException("Illegal admin groups LDAP DN"); } this.server = server; @@ -201,6 +211,7 @@ public class LdapConfig this.adminPasswd = adminPasswd; this.usersDN = usersDN; this.groupsDN = groupsDN; + this.adminGroupsDN = adminGroupsDN; } public String getUsersDN() @@ -212,6 +223,11 @@ public class LdapConfig { return this.groupsDN; } + + public String getAdminGroupsDN() + { + return this.adminGroupsDN; + } public String getServer() { diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java index edd396bb76c551e1d976aba97130abe2bb7d17de..13580e05434cace85e7e44d8ecc8aad18d5ec9ca 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java @@ -72,8 +72,8 @@ import java.security.AccessControlException; import java.security.Principal; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.List; +import java.util.Set; import javax.security.auth.x500.X500Principal; @@ -102,20 +102,11 @@ import com.unboundid.ldap.sdk.SearchResult; import com.unboundid.ldap.sdk.SearchResultEntry; import com.unboundid.ldap.sdk.SearchScope; import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl; -import java.util.logging.Level; public class LdapGroupDAO<T extends Principal> extends LdapDAO { private static final Logger logger = Logger.getLogger(LdapGroupDAO.class); - - private static final String ACTUAL_GROUP_TOKEN = "<ACTUAL_GROUP>"; - private static final String GROUP_READ_ACI = "(targetattr = \"*\") " + - "(version 3.0;acl \"Group Read\";allow (read,compare,search)" + - "(groupdn = \"ldap:///<ACTUAL_GROUP>\");)"; - private static final String GROUP_WRITE_ACI = "(targetattr = \"*\") " + - "(version 3.0;acl \"Group Write\";allow " + - "(read,compare,search,selfwrite,write,add)" + - "(groupdn = \"ldap:///<ACTUAL_GROUP>\");)"; + private LdapUserDAO<T> userPersist; @@ -151,22 +142,11 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throw new IllegalArgumentException("Group owner must be specified"); } - try - { - User<X500Principal> subjectUser = - userPersist.getMember(getSubjectDN()); - if (!subjectUser.equals(group.getOwner())) - { - throw new AccessControlException("Group owner must be group " + - " creator"); - } - } - catch (LDAPException e) + if (!isCreatorOwner(group.getOwner())) { - e.printStackTrace(); - throw new RuntimeException(e); + throw new AccessControlException("Group owner must be creator"); } - + try { getGroup(group.getID()); @@ -175,127 +155,39 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO catch (GroupNotFoundException ex) { try - { - try - { - Group inactiveGroup = getInactiveGroup(group.getID()); - - // Check requestor owns the group. - DN ownerDN = userPersist.getUserDN(group.getOwner()); - if (!ownerDN.equals(getSubjectDN())) - { - throw new AccessControlException( - "Unable to activate group " + group.getID() + - " because " + getSubjectDN().toString() - + " is not the owner"); - } - - List<Modification> mods = new ArrayList<Modification>(); - Modification mod = - new Modification(ModificationType.DELETE, - "nsaccountlock"); - mods.add(mod); - - Group modifiedGroup = modifyGroup(group, inactiveGroup, - mods); - Group activatedGroup = - new ActivatedGroup(modifiedGroup.getID(), - modifiedGroup.getOwner()); - activatedGroup.description = modifiedGroup.description; - activatedGroup.groupRead = modifiedGroup.groupRead; - activatedGroup.groupWrite = modifiedGroup.groupWrite; - activatedGroup.getProperties() - .addAll(modifiedGroup.getProperties()); - activatedGroup.getGroupMembers() - .addAll(modifiedGroup.getGroupMembers()); - activatedGroup.getUserMembers() - .addAll(modifiedGroup.getUserMembers()); - return activatedGroup; - } - catch (GroupNotFoundException ignore) {} - + { if (!group.getProperties().isEmpty()) { throw new UnsupportedOperationException( "Support for groups properties not available"); } - - DN ownerDN = userPersist.getUserDN(group.getOwner()); - String groupWriteAci = null; - String groupReadAci = null; - if (group.groupWrite != null) - { - DN groupWrite = getGroupDN(group.groupWrite.getID()); - groupWriteAci = GROUP_WRITE_ACI.replace( - ACTUAL_GROUP_TOKEN, - groupWrite.toNormalizedString()); - } - - if (group.groupRead != null) - { - DN groupRead = getGroupDN(group.groupRead.getID()); - groupReadAci = GROUP_READ_ACI.replace( - ACTUAL_GROUP_TOKEN, - groupRead.toNormalizedString()); - } - - // add new group - List<Attribute> attributes = new ArrayList<Attribute>(); - attributes.add(new Attribute("objectClass", - "groupofuniquenames")); - - attributes.add(new Attribute("cn", group.getID())); - if (group.description != null) - { - attributes.add(new Attribute("description", - group.description)); - } - - attributes.add(new Attribute("owner", - ownerDN.toNormalizedString())); - - // acis - List<String> acis = new ArrayList<String>(); - if (groupWriteAci != null) - { - acis.add(groupWriteAci); - } - if (groupReadAci != null) - { - acis.add(groupReadAci); - } - - if (!acis.isEmpty()) - { - attributes.add(new Attribute("aci", - (String[]) acis.toArray(new String[acis.size()]))); - } - - List<String> members = new ArrayList<String>(); - for (User<?> member : group.getUserMembers()) - { - DN memberDN = this.userPersist.getUserDN(member); - members.add(memberDN.toNormalizedString()); - } - for (Group gr : group.getGroupMembers()) + + try { - DN grDN = getGroupDN(gr.getID()); - members.add(grDN.toNormalizedString()); + getInactiveGroup(group); + return reactivateGroup(group); } - if (!members.isEmpty()) + catch (GroupNotFoundException e) { - attributes.add(new Attribute("uniquemember", - (String[]) members.toArray(new String[members.size()]))); + // ignore } - - AddRequest addRequest = - new AddRequest(getGroupDN(group.getID()), attributes); - - addRequest.addControl( - new ProxiedAuthorizationV2RequestControl( - "dn:" + getSubjectDN().toNormalizedString())); - - LDAPResult result = getConnection().add(addRequest); + + DN ownerDN = userPersist.getUserDN(group.getOwner()); + + // add group to groups tree + LDAPResult result = addGroup(getGroupDN(group.getID()), + group.getID(), ownerDN, + group.description, + group.getUserMembers(), + group.getGroupMembers()); + + // add group to admin groups tree + result = addGroup(getAdminGroupDN(group.getID()), + group.getID(), ownerDN, + group.description, + group.getUserMembers(), + group.getGroupMembers()); + try { return getGroup(group.getID()); @@ -309,10 +201,143 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO { e.printStackTrace(); throw new RuntimeException(e); + } + } + } + + private LDAPResult addGroup(final DN groupDN, final String groupID, + final DN ownerDN, final String description, + final Set<User<? extends Principal>> users, + final Set<Group> groups) + throws UserNotFoundException, LDAPException + { + // add new group + List<Attribute> attributes = new ArrayList<Attribute>(); + Attribute ownerAttribute = + new Attribute("owner", ownerDN.toNormalizedString()); + attributes.add(ownerAttribute); + attributes.add(new Attribute("objectClass", "groupofuniquenames")); + attributes.add(new Attribute("cn", groupID)); + + if (description != null) + { + attributes.add(new Attribute("description", description)); + } + + List<String> members = new ArrayList<String>(); + for (User<? extends Principal> userMember : users) + { + DN memberDN = this.userPersist.getUserDN(userMember); + members.add(memberDN.toNormalizedString()); + } + for (Group groupMember : groups) + { + DN memberDN = getGroupDN(groupMember.getID()); + members.add(memberDN.toNormalizedString()); + } + if (!members.isEmpty()) + { + attributes.add(new Attribute("uniquemember", + (String[]) members.toArray(new String[members.size()]))); + } + + AddRequest addRequest = new AddRequest(groupDN, attributes); + + addRequest.addControl( + new ProxiedAuthorizationV2RequestControl( + "dn:" + getSubjectDN().toNormalizedString())); + + return getConnection().add(addRequest); + } + + private Group getInactiveGroup(final Group group) + throws AccessControlException, UserNotFoundException, + GroupNotFoundException + { + Group inactiveGroup; + try + { + inactiveGroup = getInactiveGroup(getGroupDN(group.getID()) + .toNormalizedString(), group.getID()); + + if (inactiveGroup == null) + { + return null; } + + if (!group.getOwner().equals(inactiveGroup.getOwner())) + { + throw new AccessControlException( + "Inactive group not owned be requestor"); + } + + Group inactiveAdminGroup = getInactiveGroup( + getAdminGroupDN(group.getID()).toNormalizedString(), + group.getID()); + + if (inactiveAdminGroup == null) + { + throw new RuntimeException( + "BUG: adminGroup not found for group " + group.getID()); + } + + if (!group.getOwner().equals(inactiveAdminGroup.getOwner())) + { + throw new RuntimeException( + "Bug: adminGroup owner doesn't match " + + "group owner for group " + group.getID()); + } + return inactiveGroup; + } + catch (LDAPException e) + { + // TODO Auto-generated catch block + throw new RuntimeException("BUG: LDAP Exception: ", e); + } + + + } + + private Group getInactiveGroup(final String groupDN, final String groupID) + throws UserNotFoundException, LDAPException, GroupNotFoundException + { + Filter filter = Filter.createANDFilter( + Filter.createEqualityFilter("cn", groupID), + Filter.createEqualityFilter("nsaccountlock", "true")); + + SearchRequest searchRequest = + new SearchRequest(groupDN, SearchScope.SUB, filter, + new String[] {"cn", "owner"}); + + searchRequest.addControl( + new ProxiedAuthorizationV2RequestControl("dn:" + + getSubjectDN().toNormalizedString())); + + SearchResultEntry searchResult = + getConnection().searchForEntry(searchRequest); + + if (searchResult == null) + { + String msg = "Inactive Group not found " + groupID; + logger.debug(msg); + throw new GroupNotFoundException(msg); } + + String groupCN = searchResult.getAttributeValue("cn"); + DN groupOwner = searchResult.getAttributeValueAsDN("owner"); + + User<X500Principal> owner = userPersist.getMember(groupOwner); + + return new Group(groupCN, owner); + } + + private Group reactivateGroup(final Group group) + throws UserNotFoundException, LDAPException, TransientException, AccessControlException, GroupNotFoundException + { + return modifyGroup(group, true); } + /** * Get the group with the given Group ID. * @@ -323,14 +348,50 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO * @throws GroupNotFoundException If the group was not found. * @throws TransientException If an temporary, unexpected problem occurred. */ - public Group getGroup(String groupID) + public Group getGroup(final String groupID) throws GroupNotFoundException, TransientException, AccessControlException { return getGroup(groupID, true); } + + public Group getGroup(final String groupID, final boolean withMembers) + throws GroupNotFoundException, TransientException, + AccessControlException + { + Group group = getGroup(getGroupDN(groupID), groupID, true); + + Group adminGroup = getAdminGroup(getAdminGroupDN(groupID), groupID, + true); + + group.getGroupAdmins().addAll(adminGroup.getGroupMembers()); + group.getUserAdmins().addAll(adminGroup.getUserMembers()); + return group; + } + + private Group getGroup(final DN groupDN, final String groupID, + final boolean withMembers) + throws GroupNotFoundException, TransientException, + AccessControlException + { + String [] attributes = new String[] {"entrydn", "cn", "description", + "owner", "uniquemember", + "modifytimestamp"}; + return getGroup(groupDN, groupID, withMembers, attributes); + } + + private Group getAdminGroup(final DN groupDN, final String groupID, + final boolean withMembers) + throws GroupNotFoundException, TransientException, + AccessControlException + { + String [] attributes = new String[] {"entrydn", "cn", "owner", + "uniquemember"}; + return getGroup(groupDN, groupID, withMembers, attributes); + } - private Group getGroup(String groupID, boolean withMembers) + private Group getGroup(final DN groupDN, final String groupID, + final boolean withMembers, String[] attributes) throws GroupNotFoundException, TransientException, AccessControlException { @@ -341,28 +402,26 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO Filter.createNOTFilter( Filter.createEqualityFilter("nsaccountlock", "TRUE"))); - SearchRequest searchRequest = new SearchRequest( - config.getGroupsDN(), SearchScope.SUB, - filter, new String[] {"entrydn", "cn", "description", - "owner", "uniquemember", "aci", - "modifytimestamp"}); + SearchRequest searchRequest = + new SearchRequest(groupDN.toNormalizedString(), + SearchScope.SUB, filter, attributes); searchRequest.addControl( new ProxiedAuthorizationV2RequestControl("dn:" + getSubjectDN().toNormalizedString())); - SearchResultEntry group = + SearchResultEntry searchResult = getConnection().searchForEntry(searchRequest); - if (group == null) + + if (searchResult == null) { String msg = "Group not found " + groupID; logger.debug(msg); throw new GroupNotFoundException(groupID); } - String groupCN = group.getAttributeValue("cn"); - DN groupOwner = group.getAttributeValueAsDN("owner"); - Date lastModified = - group.getAttributeValueAsDate("modifytimestamp"); + + String groupCN = searchResult.getAttributeValue("cn"); + DN groupOwner = searchResult.getAttributeValueAsDN("owner"); User<X500Principal> owner; try @@ -375,14 +434,22 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } Group ldapGroup = new Group(groupCN, owner); - ldapGroup.description = group.getAttributeValue("description"); - ldapGroup.lastModified = lastModified; + if (searchResult.hasAttribute("description")) + { + ldapGroup.description = + searchResult.getAttributeValue("description"); + } + if (searchResult.hasAttribute("modifytimestamp")) + { + ldapGroup.lastModified = + searchResult.getAttributeValueAsDate("modifytimestamp"); + } if (withMembers) { - if (group.getAttributeValues("uniquemember") != null) + if (searchResult.getAttributeValues("uniquemember") != null) { - for (String member : group + for (String member : searchResult .getAttributeValues("uniquemember")) { DN memberDN = new DN(member); @@ -391,7 +458,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO User<X500Principal> user; try { - user = userPersist.getMember(memberDN, false); + user = userPersist.getMember(memberDN); } catch (UserNotFoundException e) { @@ -412,36 +479,6 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } } } - - // TODO not sure this is going to fly... - if (group.getAttributeValues("aci") != null) - { - for (String aci : group.getAttributeValues("aci")) - { - if (aci.contains("Group Read")) - { - // TODO it's gotta be a better way to do this. - String grRead = aci.substring( - aci.indexOf("ldap:///")); - grRead = grRead.substring(grRead.indexOf("cn=") + 3, - grRead.indexOf(',')); - - Group groupRead = new Group(grRead.trim()); - ldapGroup.groupRead = groupRead; - } - else if (aci.contains("Group Write")) - { - // TODO it's gotta be a better way to do this. - String grWrite = aci.substring( - aci.indexOf("ldap:///")); - grWrite = grWrite.substring(grWrite.indexOf("cn=") + 3, - grWrite.indexOf(',')); - - Group groupWrite = getGroup(grWrite.trim()); - ldapGroup.groupWrite = groupWrite; - } - } - } } return ldapGroup; @@ -458,7 +495,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO /** * Modify the given group. * - * @param group The group to update. + * @param group The group to update. It must be an existing group * * @return The newly updated group. * @@ -471,141 +508,108 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throws GroupNotFoundException, TransientException, AccessControlException, UserNotFoundException { - // check if group exists - Group oldGroup = getGroup(group.getID()); - - return modifyGroup(group, oldGroup, null); + return modifyGroup(group, false); } - private Group modifyGroup(Group newGroup, Group oldGroup, - List<Modification> modifications) + private Group modifyGroup(final Group group, + boolean withActivate) throws UserNotFoundException, TransientException, - AccessControlException + AccessControlException, GroupNotFoundException { - if (!newGroup.getProperties().isEmpty()) + if (!group.getProperties().isEmpty()) { throw new UnsupportedOperationException( "Support for groups properties not available"); } - - List<Modification> modifs = new ArrayList<Modification>(); - if (modifications != null) - { - modifs.addAll(modifications); - } - - if (newGroup.description == null && oldGroup.description != null) + + // check if group exists + if (withActivate) { - modifs.add(new Modification(ModificationType.DELETE, - "description")); + getInactiveGroup(group); } - else if (newGroup.description != null && oldGroup.description == null) + else { - modifs.add(new Modification(ModificationType.ADD, "description", - newGroup.description)); + getGroup(group.getID()); } - else if (newGroup.description != null && oldGroup.description != null) + + List<Modification> mods = new ArrayList<Modification>(); + List<Modification> adminMods = new ArrayList<Modification>(); + if (withActivate) { - modifs.add(new Modification(ModificationType.REPLACE, "description", - newGroup.description)); + mods.add(new Modification(ModificationType.DELETE, "nsaccountlock")); + adminMods.add(new Modification(ModificationType.DELETE, "nsaccountlock")); } - List<String> acis = new ArrayList<String>(); - if (newGroup.groupRead != null) + if (group.description == null) { - if (newGroup.groupRead.equals(newGroup)) - { - throw new IllegalArgumentException( - "cyclical reference from groupRead to group"); - } - - DN readGrDN = getGroupDN(newGroup.groupRead.getID()); - acis.add(GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, - readGrDN.toNormalizedString())); + mods.add(new Modification(ModificationType.REPLACE, "description")); } - - if (newGroup.groupWrite != null) + else { - if (newGroup.groupWrite.equals(newGroup)) - { - throw new IllegalArgumentException( - "cyclical reference from groupWrite to group"); - } - - DN writeGrDN = getGroupDN(newGroup.groupWrite.getID()); - acis.add(GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, - writeGrDN.toNormalizedString())); + mods.add(new Modification(ModificationType.REPLACE, "description", group.description)); } - modifs.add(new Modification(ModificationType.REPLACE, "aci", (String[]) - acis.toArray(new String[acis.size()]))); - List<String> newMembers = new ArrayList<String>(); - for (User<?> member : newGroup.getUserMembers()) + for (User<?> member : group.getUserMembers()) { - if (!oldGroup.getUserMembers().remove(member)) + DN memberDN; + try { - DN memberDN; - try - { - memberDN = userPersist.getUserDN(member); - } - catch (LDAPException e) - { - throw new UserNotFoundException( - "User not found " + member.getUserID()); - } - newMembers.add(memberDN.toNormalizedString()); + memberDN = userPersist.getUserDN(member); + } + catch (LDAPException e) + { + throw new UserNotFoundException("User not found " + + member.getUserID()); } + newMembers.add(memberDN.toNormalizedString()); } - for (Group gr : newGroup.getGroupMembers()) + for (Group gr : group.getGroupMembers()) { - if (gr.equals(newGroup)) - { - throw new IllegalArgumentException( - "cyclical reference from group member to group"); - } - - if (!oldGroup.getGroupMembers().remove(gr)) - { DN grDN = getGroupDN(gr.getID()); newMembers.add(grDN.toNormalizedString()); - } } - if (!newMembers.isEmpty()) - { - modifs.add(new Modification(ModificationType.ADD, "uniquemember", - (String[]) newMembers.toArray(new String[newMembers.size()]))); - } - - List<String> delMembers = new ArrayList<String>(); - for (User<?> member : oldGroup.getUserMembers()) + List<String> newAdmins = new ArrayList<String>(); + for (User<?> member : group.getUserAdmins()) { DN memberDN; try { - memberDN = this.userPersist.getUserDN(member); + memberDN = userPersist.getUserDN(member); } catch (LDAPException e) { throw new UserNotFoundException( "User not found " + member.getUserID()); } - delMembers.add(memberDN.toNormalizedString()); + newAdmins.add(memberDN.toNormalizedString()); } - for (Group gr : oldGroup.getGroupMembers()) + for (Group gr : group.getGroupAdmins()) { DN grDN = getGroupDN(gr.getID()); - delMembers.add(grDN.toNormalizedString()); + newMembers.add(grDN.toNormalizedString()); + } + + mods.add(new Modification(ModificationType.REPLACE, "uniquemember", + (String[]) newMembers.toArray(new String[newMembers.size()]))); + adminMods.add(new Modification(ModificationType.REPLACE, "uniquemember", + (String[]) newAdmins.toArray(new String[newAdmins.size()]))); + + // modify admin group first + ModifyRequest modifyRequest = new ModifyRequest(getAdminGroupDN(group.getID()), adminMods); + try + { + modifyRequest.addControl( + new ProxiedAuthorizationV2RequestControl( + "dn:" + getSubjectDN().toNormalizedString())); + LDAPResult result = getConnection().modify(modifyRequest); } - if (!delMembers.isEmpty()) + catch (LDAPException e1) { - modifs.add(new Modification(ModificationType.DELETE, "uniquemember", - (String[]) delMembers.toArray(new String[delMembers.size()]))); + throw new RuntimeException("LDAP problem", e1); } - - ModifyRequest modifyRequest = - new ModifyRequest(getGroupDN(newGroup.getID()), modifs); + // modify the group itself now + modifyRequest = new ModifyRequest(getGroupDN(group.getID()), mods); try { modifyRequest.addControl( @@ -619,11 +623,19 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } try { - return getGroup(newGroup.getID()); + if (withActivate) + { + return new ActivatedGroup(getGroup(group.getID())); + } + else + { + return getGroup(group.getID()); + } } catch (GroupNotFoundException e) { - throw new RuntimeException("BUG: modified group not found"); + throw new RuntimeException( + "BUG: modified group not found (" + group.getID() + ")"); } } @@ -639,29 +651,37 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throws GroupNotFoundException, TransientException, AccessControlException { - Group group = getGroup(groupID); + deleteGroup(getGroupDN(groupID), groupID, false); + deleteGroup(getAdminGroupDN(groupID), groupID, true); + } + + private void deleteGroup(final DN groupDN, final String groupID, + final boolean isAdmin) + throws GroupNotFoundException, TransientException, + AccessControlException + { + Group group = getGroup(groupDN, groupID, false); List<Modification> modifs = new ArrayList<Modification>(); modifs.add(new Modification(ModificationType.ADD, "nsaccountlock", "true")); - if (group.description != null) - { - modifs.add(new Modification(ModificationType.DELETE, "description")); - } - - if (group.groupRead != null || - group.groupWrite != null) + if (isAdmin) { - modifs.add(new Modification(ModificationType.DELETE, "aci")); + if (!group.getGroupAdmins().isEmpty() || + !group.getUserAdmins().isEmpty()) + { + modifs.add(new Modification(ModificationType.DELETE, "uniquemember")); + } } - - if (!group.getGroupMembers().isEmpty() || - !group.getUserMembers().isEmpty()) + else { - modifs.add(new Modification(ModificationType.DELETE, "uniquemember")); + if (!group.getGroupMembers().isEmpty() || + !group.getUserMembers().isEmpty()) + { + modifs.add(new Modification(ModificationType.DELETE, "uniquemember")); + } } - ModifyRequest modifyRequest = - new ModifyRequest(getGroupDN(group.getID()), modifs); + ModifyRequest modifyRequest = new ModifyRequest(groupDN, modifs); try { modifyRequest.addControl( @@ -726,7 +746,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } else if (role == Role.RW) { - return getRWGroups(user, userDN, groupID); + return getAdminGroups(user, userDN, groupID); } throw new IllegalArgumentException("Unknown role " + role); } @@ -793,17 +813,45 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO Collection<Group> groups = new ArrayList<Group>(); if (userPersist.isMember(user.getUserID(), groupID)) { - groups.add(getGroup(groupID)); + groups.add(getGroup(groupID, false)); } return groups; } else { - return userPersist.getUserGroups(user.getUserID()); + try + { + Collection<Group> groups = + userPersist.getUserGroups(user.getUserID()); + + List<Filter> filters = new ArrayList<Filter>(); + for (Group group : groups) + { + filters.add(Filter.createEqualityFilter("cn", + group.getID())); + } + + Filter filter = Filter.createORFilter(filters); + SearchRequest searchRequest = new SearchRequest( + config.getAdminGroupsDN(), SearchScope.SUB, filter, + "cn"); + + SearchResult results = getConnection().search(searchRequest); + for (SearchResultEntry result : results.getSearchEntries()) + { + String groupName = result.getAttributeValue("cn"); + + } + return groups; + } + catch (LDAPException e) + { + throw new TransientException(e.getDiagnosticMessage()); + } } } - protected Collection<Group> getRWGroups(User<T> user, DN userDN, + protected Collection<Group> getAdminGroups(User<T> user, DN userDN, String groupID) throws TransientException, AccessControlException, GroupNotFoundException, UserNotFoundException @@ -829,25 +877,25 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO List<Filter> filters = new ArrayList<Filter>(); for (Group member : queryGroups) { - // Require both groupRead and groupWrite - if (member.groupRead != null && member.groupWrite != null) - { - DN groupRead = getGroupDN(member.groupRead.getID()); - String groupReadAci = - GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, - groupRead.toNormalizedString()); - DN groupWrite = getGroupDN(member.groupRead.getID()); - String groupWriteAci = - GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, - groupWrite.toNormalizedString()); - System.out.println(groupReadAci); - System.out.println(groupWriteAci); - - Filter filter = Filter.createANDFilter( - Filter.createEqualityFilter("aci", groupReadAci), - Filter.createEqualityFilter("aci", groupWriteAci)); - filters.add(filter); - } +// // Require both groupRead and groupWrite +// if (member.groupRead != null && member.groupWrite != null) +// { +// DN groupRead = getGroupDN(member.groupRead.getID()); +// String groupReadAci = +// GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, +// groupRead.toNormalizedString()); +// DN groupWrite = getGroupDN(member.groupRead.getID()); +// String groupWriteAci = +// GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, +// groupWrite.toNormalizedString()); +// System.out.println(groupReadAci); +// System.out.println(groupWriteAci); +// +// Filter filter = Filter.createANDFilter( +// Filter.createEqualityFilter("aci", groupReadAci), +// Filter.createEqualityFilter("aci", groupWriteAci)); +// filters.add(filter); +// } } Collection<Group> groups = new ArrayList<Group>(); @@ -895,119 +943,86 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } } - protected Collection<Group> getRWGroups2(User<T> user, DN userDN, - String groupID) - throws TransientException, AccessControlException, - GroupNotFoundException, UserNotFoundException - { - try - { - Collection<Group> groups = new ArrayList<Group>(); - - Collection<Group> queryGroups = new ArrayList<Group>(); - if (groupID != null) - { - queryGroups.add(new Group(groupID, user)); - } - else - { - // List of Groups the user belongs to. - queryGroups.addAll(getMemberGroups(user, userDN, groupID)); - - // List of Groups the user owns; - queryGroups.addAll(getOwnerGroups(user, userDN, groupID)); - } - - for (Group member : queryGroups) - { - // Require both groupRead and groupWrite - if (member.groupRead != null && member.groupWrite != null) - { - DN groupRead = getGroupDN(member.groupRead.getID()); - String groupReadAci = - GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, - groupRead.toNormalizedString()); - DN groupWrite = getGroupDN(member.groupWrite.getID()); - String groupWriteAci = - GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, - groupWrite.toNormalizedString()); - - Filter filter = Filter.createANDFilter( - Filter.createEqualityFilter("aci", groupReadAci), - Filter.createEqualityFilter("aci", groupWriteAci)); - - SearchRequest searchRequest = new SearchRequest( - config.getGroupsDN(), SearchScope.SUB, filter, - new String[] {"cn", "owner", "description", - "modifytimestamp"}); - - searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + - getSubjectDN().toNormalizedString())); - - SearchResult results = getConnection().search(searchRequest); - for (SearchResultEntry result : results.getSearchEntries()) - { - String groupName = result.getAttributeValue("cn"); - DN ownerDN = result.getAttributeValueAsDN("owner"); - User<X500Principal> owner = userPersist.getMember(ownerDN); - - // Ignore existing illegal group names. - try - { - Group group = new Group(groupName, owner); - group.description = result.getAttributeValue("description"); - group.lastModified = - result.getAttributeValueAsDate("modifytimestamp"); - groups.add(group); - } - catch (IllegalArgumentException ignore) { } - } - } - } - return groups; - } - catch (LDAPException e1) - { - // TODO check which LDAP exceptions are transient and which - // ones are - // access control - throw new TransientException("Error getting groups", e1); - } - } - - private Group getInactiveGroup(String groupID) - throws UserNotFoundException, GroupNotFoundException, LDAPException - { - Filter filter = Filter.createANDFilter( - Filter.createEqualityFilter("cn", groupID), - Filter.createEqualityFilter("nsaccountlock", "true")); - - SearchRequest searchRequest = new SearchRequest( - config.getGroupsDN(), SearchScope.SUB, - filter, new String[] {"cn", "owner"}); - - searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + - getSubjectDN().toNormalizedString())); - - SearchResultEntry searchResult = - getConnection().searchForEntry(searchRequest); - - if (searchResult == null) - { - String msg = "Inactive Group not found " + groupID; - logger.debug(msg); - throw new GroupNotFoundException(msg); - } - - String groupCN = searchResult.getAttributeValue("cn"); - DN groupOwner = searchResult.getAttributeValueAsDN("owner"); - - User<X500Principal> owner = userPersist.getMember(groupOwner); - - return new Group(groupCN, owner); - } +// protected Collection<Group> getRWGroups2(User<T> user, DN userDN, +// String groupID) +// throws TransientException, AccessControlException, +// GroupNotFoundException, UserNotFoundException +// { +// try +// { +// Collection<Group> groups = new ArrayList<Group>(); +// +// Collection<Group> queryGroups = new ArrayList<Group>(); +// if (groupID != null) +// { +// queryGroups.add(new Group(groupID, user)); +// } +// else +// { +// // List of Groups the user belongs to. +// queryGroups.addAll(getMemberGroups(user, userDN, groupID)); +// +// // List of Groups the user owns; +// queryGroups.addAll(getOwnerGroups(user, userDN, groupID)); +// } +// +// for (Group member : queryGroups) +// { +// // Require both groupRead and groupWrite +// if (member.groupRead != null && member.groupWrite != null) +// { +// DN groupRead = getGroupDN(member.groupRead.getID()); +// String groupReadAci = +// GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, +// groupRead.toNormalizedString()); +// DN groupWrite = getGroupDN(member.groupWrite.getID()); +// String groupWriteAci = +// GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, +// groupWrite.toNormalizedString()); +// +// Filter filter = Filter.createANDFilter( +// Filter.createEqualityFilter("aci", groupReadAci), +// Filter.createEqualityFilter("aci", groupWriteAci)); +// +// SearchRequest searchRequest = new SearchRequest( +// config.getGroupsDN(), SearchScope.SUB, filter, +// new String[] {"cn", "owner", "description", +// "modifytimestamp"}); +// +// searchRequest.addControl( +// new ProxiedAuthorizationV2RequestControl("dn:" + +// getSubjectDN().toNormalizedString())); +// +// SearchResult results = getConnection().search(searchRequest); +// for (SearchResultEntry result : results.getSearchEntries()) +// { +// String groupName = result.getAttributeValue("cn"); +// DN ownerDN = result.getAttributeValueAsDN("owner"); +// User<X500Principal> owner = userPersist.getMember(ownerDN); +// +// // Ignore existing illegal group names. +// try +// { +// Group group = new Group(groupName, owner); +// group.description = result.getAttributeValue("description"); +// group.lastModified = +// result.getAttributeValueAsDate("modifytimestamp"); +// groups.add(group); +// } +// catch (IllegalArgumentException ignore) { } +// } +// } +// } +// return groups; +// } +// catch (LDAPException e) +// { +// // TODO check which LDAP exceptions are transient and which +// // ones are +// // access control +// throw new TransientException("Error getting groups", e); +// } +// } /** * Returns a group based on its LDAP DN. The returned group is bare @@ -1019,35 +1034,82 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO * @throws ca.nrc.cadc.ac.GroupNotFoundException * @throws ca.nrc.cadc.ac.UserNotFoundException */ - protected Group getGroup(DN groupDN) - throws LDAPException, GroupNotFoundException, UserNotFoundException - { - SearchResultEntry searchResult = - getConnection().getEntry(groupDN.toNormalizedString(), - new String[] {"cn", "description"}); +// protected Group getGroup(DN groupDN) +// throws LDAPException, GroupNotFoundException, UserNotFoundException +// { +// SearchResultEntry searchResult = +// getConnection().getEntry(groupDN.toNormalizedString(), +// new String[] {"cn", "description"}); +// +// if (searchResult == null) +// { +// String msg = "Group not found " + groupDN; +// logger.debug(msg); +// throw new GroupNotFoundException(groupDN.toNormalizedString()); +// } +// +// Group group = new Group(searchResult.getAttributeValue("cn")); +// group.description = searchResult.getAttributeValue("description"); +// return group; +// } - if (searchResult == null) + /** + * + * @param groupID + * @return + */ + protected DN getGroupDN(final String groupID) + { + try { - String msg = "Group not found " + groupDN; - logger.debug(msg); - throw new GroupNotFoundException(groupDN.toNormalizedString()); + return new DN("cn=" + groupID + "," + config.getGroupsDN()); } - - Group group = new Group(searchResult.getAttributeValue("cn")); - group.description = searchResult.getAttributeValue("description"); - return group; + catch (LDAPException e) + { + } + throw new IllegalArgumentException(groupID + " not a valid group ID"); } - - protected DN getGroupDN(String groupID) + + /** + * + * @param groupID + * @return + */ + protected DN getAdminGroupDN(final String groupID) { try { - return new DN("cn=" + groupID + "," + config.getGroupsDN()); + return new DN("cn=" + groupID + "," + config.getAdminGroupsDN()); } catch (LDAPException e) { } throw new IllegalArgumentException(groupID + " not a valid group ID"); } + + /** + * + * @param owner + * @return + * @throws UserNotFoundException + */ + protected boolean isCreatorOwner(final User<? extends Principal> owner) + throws UserNotFoundException + { + try + { + User<X500Principal> subjectUser = + userPersist.getMember(getSubjectDN()); + if (subjectUser.equals(owner)) + { + return true; + } + return false; + } + catch (LDAPException e) + { + throw new RuntimeException(e); + } + } } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java index b58ade6f8eec036820fbff5e77c8d1640c964039..bb11f40ad609a5184f01357d47ffb5feb90eb3e7 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java @@ -68,12 +68,25 @@ */ package ca.nrc.cadc.ac.server.ldap; +import java.security.AccessControlException; +import java.security.Principal; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import javax.security.auth.x500.X500Principal; + +import org.apache.log4j.Logger; + import ca.nrc.cadc.ac.Group; +import ca.nrc.cadc.ac.PersonalDetails; import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.UserNotFoundException; import ca.nrc.cadc.auth.HttpPrincipal; import ca.nrc.cadc.auth.NumericPrincipal; import ca.nrc.cadc.net.TransientException; + import com.unboundid.ldap.sdk.CompareRequest; import com.unboundid.ldap.sdk.CompareResult; import com.unboundid.ldap.sdk.DN; @@ -82,30 +95,39 @@ import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.SearchRequest; import com.unboundid.ldap.sdk.SearchResultEntry; import com.unboundid.ldap.sdk.SearchScope; -import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV1RequestControl; import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl; -import java.security.AccessControlException; -import java.security.Principal; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import javax.security.auth.x500.X500Principal; -import org.apache.log4j.Logger; public class LdapUserDAO<T extends Principal> extends LdapDAO { private static final Logger logger = Logger.getLogger(LdapUserDAO.class); // Map of identity type to LDAP attribute - private Map<Class<?>, String> attribType = new HashMap<Class<?>, String>(); + private Map<Class<?>, String> userLdapAttrib = new HashMap<Class<?>, String>(); + + // User attributes returned to the GMS + private static final String LDAP_FNAME = "givenname"; + private static final String LDAP_LNAME = "sn"; + //TODO to add the rest + private String[] userAttribs = new String[]{LDAP_FNAME, LDAP_LNAME}; + private String[] memberAttribs = new String[]{LDAP_FNAME, LDAP_LNAME}; public LdapUserDAO(LdapConfig config) { super(config); - this.attribType.put(HttpPrincipal.class, "cn"); - this.attribType.put(X500Principal.class, "distinguishedname"); - this.attribType.put(NumericPrincipal.class, "entryid"); + this.userLdapAttrib.put(HttpPrincipal.class, "uid"); + this.userLdapAttrib.put(X500Principal.class, "distinguishedname"); + + // add the id attributes to user and member attributes + String[] princs = userLdapAttrib.values().toArray(new String[userLdapAttrib.values().size()]); + String[] tmp = new String[userAttribs.length + princs.length]; + System.arraycopy(princs, 0, tmp, 0, princs.length); + System.arraycopy(userAttribs, 0, tmp, princs.length, userAttribs.length); + userAttribs = tmp; + + tmp = new String[memberAttribs.length + princs.length]; + System.arraycopy(princs, 0, tmp, 0, princs.length); + System.arraycopy(memberAttribs, 0, tmp, princs.length, memberAttribs.length); + memberAttribs = tmp; } /** @@ -122,7 +144,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO public User<T> getUser(T userID) throws UserNotFoundException, TransientException, AccessControlException { - String searchField = (String) attribType.get(userID.getClass()); + String searchField = (String) userLdapAttrib.get(userID.getClass()); if (searchField == null) { throw new IllegalArgumentException( @@ -135,8 +157,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO try { SearchRequest searchRequest = new SearchRequest(config.getUsersDN(), - SearchScope.SUB, searchField, - new String[] {"cn", "entryid", "entrydn", "dn"}); + SearchScope.SUB, searchField, userAttribs); searchRequest.addControl( new ProxiedAuthorizationV2RequestControl("dn:" + @@ -157,12 +178,13 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO } User<T> user = new User<T>(userID); user.getIdentities().add( - new HttpPrincipal(searchResult.getAttributeValue("cn"))); - - user.getIdentities().add( - new NumericPrincipal( - searchResult.getAttributeValueAsInteger("entryid"))); - + new HttpPrincipal(searchResult.getAttributeValue(userLdapAttrib + .get(HttpPrincipal.class)))); + + String fname = searchResult.getAttributeValue(LDAP_FNAME); + String lname = searchResult.getAttributeValue(LDAP_LNAME); + user.details.add(new PersonalDetails(fname, lname)); + //TODO populate user with the other returned personal or posix attributes return user; } @@ -182,14 +204,14 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO { try { - String searchField = (String) attribType.get(userID.getClass()); + String searchField = (String) userLdapAttrib.get(userID.getClass()); if (searchField == null) { throw new IllegalArgumentException( "Unsupported principal type " + userID.getClass()); } - User user = getUser(userID); + User<T> user = getUser(userID); Filter filter = Filter.createANDFilter( Filter.createEqualityFilter(searchField, user.getUserID().getName()), @@ -256,14 +278,14 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO { try { - String searchField = (String) attribType.get(userID.getClass()); + String searchField = (String) userLdapAttrib.get(userID.getClass()); if (searchField == null) { throw new IllegalArgumentException( "Unsupported principal type " + userID.getClass()); } - User user = getUser(userID); + User<T> user = getUser(userID); Filter filter = Filter.createANDFilter( Filter.createEqualityFilter(searchField, user.getUserID().getName()), @@ -301,14 +323,14 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO { try { - String searchField = (String) attribType.get(userID.getClass()); + String searchField = (String) userLdapAttrib.get(userID.getClass()); if (searchField == null) { throw new IllegalArgumentException( "Unsupported principal type " + userID.getClass()); } - User user = getUser(userID); + User<T> user = getUser(userID); DN userDN = getUserDN(user); CompareRequest compareRequest = @@ -333,16 +355,16 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO } /** - * Returns a member user identified by the X500Principal only. + * Returns a member user identified by the X500Principal only. The + * returned object has the fields required by the GMS. + * Note that this method binds as a proxy user and not as the + * subject. * @param userDN - * @param bindAsSubject - true if Ldap commands executed as subject - * (proxy authorization) or false if they are executed as the user - * in the connection. * @return * @throws UserNotFoundException * @throws LDAPException */ - User<X500Principal> getMember(DN userDN, boolean bindAsSubject) + User<X500Principal> getMember(DN userDN) throws UserNotFoundException, LDAPException { Filter filter = @@ -351,50 +373,37 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO SearchRequest searchRequest = new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, - filter, - (String[]) this.attribType.values().toArray( - new String[this.attribType.values().size()])); - - if (bindAsSubject) - { - searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + - getSubjectDN().toNormalizedString())); - } + filter, memberAttribs); SearchResultEntry searchResult = getConnection().searchForEntry(searchRequest); if (searchResult == null) { - String msg = "User not found " + userDN; + String msg = "Member not found " + userDN; logger.debug(msg); throw new UserNotFoundException(msg); } User<X500Principal> user = new User<X500Principal>( new X500Principal(searchResult.getAttributeValue( - (String) attribType.get(X500Principal.class)))); - + (String) userLdapAttrib.get(X500Principal.class)))); + String princ = searchResult.getAttributeValue( + (String) userLdapAttrib.get(HttpPrincipal.class)); + if (princ != null) + { + user.getIdentities().add(new HttpPrincipal(princ)); + } + String fname = searchResult.getAttributeValue(LDAP_FNAME); + String lname = searchResult.getAttributeValue(LDAP_LNAME); + user.details.add(new PersonalDetails(fname, lname)); return user; } - /** - * Returns a member user identified by the X500Principal only. - * @param userDN - * @return - * @throws UserNotFoundException - * @throws LDAPException - */ - User<X500Principal> getMember(DN userDN) - throws UserNotFoundException, LDAPException - { - return getMember(userDN, true); - } DN getUserDN(User<? extends Principal> user) throws LDAPException, UserNotFoundException { - String searchField = (String) attribType.get(user.getUserID().getClass()); + String searchField = (String) userLdapAttrib.get(user.getUserID().getClass()); if (searchField == null) { throw new IllegalArgumentException( @@ -408,9 +417,9 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, searchField, new String[] {"entrydn"}); - searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + - getSubjectDN().toNormalizedString())); +// searchRequest.addControl( +// new ProxiedAuthorizationV2RequestControl("dn:" + +// getSubjectDN().toNormalizedString())); SearchResultEntry searchResult = getConnection().searchForEntry(searchRequest); diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java index 9a2c2caddd06d278b8fd4ff6e13e7047e55019a0..a79aa1a456ba2e629c331b1143f5d08900dec9ea 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java @@ -89,10 +89,11 @@ public class LdapDAOTest static int port = 389; static String adminDN = "uid=webproxy,ou=WebProxy,ou=topologymanagement,o=netscaperoot"; static String adminPW = "go4it"; - static String userBaseDN = "ou=Users,ou=ds,dc=canfartest,dc=net"; - static String groupBaseDN = "ou=Groups,ou=ds,dc=canfartest,dc=net"; + static String usersDN = "ou=Users,ou=ds,dc=canfartest,dc=net"; + static String groupsDN = "ou=Groups,ou=ds,dc=canfartest,dc=net"; + static String adminGroupsDN = "ou=adminGroups,ou=ds,dc=canfartest,dc=net"; - LdapConfig config = new LdapConfig(server, port, adminDN, adminPW, userBaseDN, groupBaseDN); + LdapConfig config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN); @Test public void testLdapBindConnection() throws Exception diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java index 94081f73add469471820754ac17ba6c20d5e452d..922432fb10ded92b85ab633190739bb5aa37479d 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java @@ -69,10 +69,11 @@ public class LdapGroupDAOTest static int port = 389; static String adminDN = "uid=webproxy,ou=webproxy,ou=topologymanagement,o=netscaperoot"; static String adminPW = "go4it"; - static String userBaseDN = "ou=Users,ou=ds,dc=canfartest,dc=net"; - static String groupBaseDN = "ou=Groups,ou=ds,dc=canfartest,dc=net"; - //static String userBaseDN = "ou=Users,ou=ds,dc=canfar,dc=net"; - //static String groupBaseDN = "ou=Groups,ou=ds,dc=canfar,dc=net"; + static String usersDN = "ou=Users,ou=ds,dc=canfartest,dc=net"; + static String groupsDN = "ou=Groups,ou=ds,dc=canfartest,dc=net"; + static String adminGroupsDN = "ou=adminGroups,ou=ds,dc=canfartest,dc=net"; + //static String usersDN = "ou=Users,ou=ds,dc=canfar,dc=net"; + //static String groupsDN = "ou=Groups,ou=ds,dc=canfar,dc=net"; static String daoTestDN1 = "cn=cadcdaotest1,ou=cadc,o=hia,c=ca"; static String daoTestDN2 = "cn=cadcdaotest2,ou=cadc,o=hia,c=ca"; @@ -115,7 +116,7 @@ public class LdapGroupDAOTest anonSubject = new Subject(); anonSubject.getPrincipals().add(unknownUser.getUserID()); - config = new LdapConfig(server, port, adminDN, adminPW, userBaseDN, groupBaseDN); + config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN); } LdapGroupDAO<X500Principal> getGroupDAO() @@ -158,24 +159,6 @@ public class LdapGroupDAOTest actualGroup = getGroupDAO().modifyGroup(expectGroup); assertGroupsEqual(expectGroup, actualGroup); - // groupRead - expectGroup.groupRead = otherGroup; - actualGroup = getGroupDAO().modifyGroup(expectGroup); - assertGroupsEqual(expectGroup, actualGroup); - - expectGroup.groupRead = null; - actualGroup = getGroupDAO().modifyGroup(expectGroup); - assertGroupsEqual(expectGroup, actualGroup); - - // groupWrite - expectGroup.groupWrite = otherGroup; - actualGroup = getGroupDAO().modifyGroup(expectGroup); - assertGroupsEqual(expectGroup, actualGroup); - - expectGroup.groupWrite = null; - actualGroup = getGroupDAO().modifyGroup(expectGroup); - assertGroupsEqual(expectGroup, actualGroup); - // userMembers expectGroup.getUserMembers().add(daoTestUser2); actualGroup = getGroupDAO().modifyGroup(expectGroup); @@ -196,8 +179,6 @@ public class LdapGroupDAOTest // delete the group expectGroup.description = "Happy testing"; - expectGroup.groupRead = otherGroup; - expectGroup.groupWrite = otherGroup; expectGroup.getUserMembers().add(daoTestUser2); expectGroup.getGroupMembers().add(otherGroup); @@ -398,9 +379,9 @@ public class LdapGroupDAOTest { getGroupDAO().addGroup(new Group("foo", unknownUser)); fail("addGroup with unknown user should throw " + - "UserNotFoundException"); + "AccessControlException"); } - catch (UserNotFoundException ignore) {} + catch (AccessControlException ignore) {} Group group = getGroupDAO().addGroup(new Group(getGroupID(), daoTestUser1)); @@ -476,15 +457,6 @@ public class LdapGroupDAOTest public Object run() throws Exception { getGroupDAO().addGroup(new Group(groupID, daoTestUser1)); - -// try -// { -// getGroupDAO().modifyGroup(new Group(groupID, unknownUser)); -// fail("modifyGroup with unknown user should throw " + -// "UserNotFoundException"); -// } -// catch (UserNotFoundException ignore) {} - try { getGroupDAO().modifyGroup(new Group("foo", daoTestUser1)); @@ -651,9 +623,6 @@ public class LdapGroupDAOTest { assertTrue(gr2.getUserMembers().contains(user)); } - assertEquals(gr1.groupRead, gr2.groupRead); - assertEquals(gr1.groupWrite, gr2.groupWrite); - assertEquals(gr1.groupWrite, gr2.groupWrite); assertEquals(gr1.getProperties(), gr2.getProperties()); for (GroupProperty prop : gr1.getProperties()) { diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java index a742fe5b6d586fa00596cc5df4939262c9b54188..f02c0f12c21689c61b6625bac768e3fd91efe835 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java @@ -68,22 +68,33 @@ */ package ca.nrc.cadc.ac.server.ldap; -import ca.nrc.cadc.ac.Group; -import ca.nrc.cadc.ac.User; -import ca.nrc.cadc.util.Log4jInit; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.security.Principal; import java.security.PrivilegedExceptionAction; import java.util.Collection; + import javax.security.auth.Subject; import javax.security.auth.x500.X500Principal; + import org.apache.log4j.Level; import org.apache.log4j.Logger; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import org.junit.BeforeClass; import org.junit.Test; +import ca.nrc.cadc.ac.Group; +import ca.nrc.cadc.ac.PersonalDetails; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.UserDetails; +import ca.nrc.cadc.auth.HttpPrincipal; +import ca.nrc.cadc.auth.NumericPrincipal; +import ca.nrc.cadc.util.Log4jInit; + +import com.unboundid.ldap.sdk.DN; + /** * * @author jburke @@ -96,12 +107,15 @@ public class LdapUserDAOTest static int port = 389; static String adminDN = "uid=webproxy,ou=Webproxy,ou=topologymanagement,o=netscaperoot"; static String adminPW = "go4it"; - static String userBaseDN = "ou=Users,ou=ds,dc=canfartest,dc=net"; - static String groupBaseDN = "ou=Groups,ou=ds,dc=canfartest,dc=net"; + static String usersDN = "ou=Users,ou=ds,dc=canfartest,dc=net"; + static String groupsDN = "ou=Groups,ou=ds,dc=canfartest,dc=net"; + static String adminGroupsDN = "ou=adminGroups,ou=ds,dc=canfartest,dc=net"; // static String userBaseDN = "ou=Users,ou=ds,dc=canfar,dc=net"; // static String groupBaseDN = "ou=Groups,ou=ds,dc=canfar,dc=net"; - static final String testUserDN = "cn=cadcdaotest1,ou=cadc,o=hia,c=ca"; + static final String testUserX509DN = "cn=cadcdaotest1,ou=cadc,o=hia,c=ca"; + static final String testUserDN = "uid=cadcdaotest1," + usersDN; + static User<X500Principal> testUser; static LdapConfig config; @@ -112,9 +126,12 @@ public class LdapUserDAOTest { Log4jInit.setLevel("ca.nrc.cadc.ac", Level.DEBUG); - testUser = new User<X500Principal>(new X500Principal(testUserDN)); + testUser = new User<X500Principal>(new X500Principal(testUserX509DN)); - config = new LdapConfig(server, port, adminDN, adminPW, userBaseDN, groupBaseDN); + config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN); + + testUser.details.add(new PersonalDetails("CADC", "DAOTest1")); + testUser.getIdentities().add(new HttpPrincipal("CadcDaoTest1")); } LdapUserDAO<X500Principal> getUserDAO() @@ -138,8 +155,8 @@ public class LdapUserDAOTest { try { - User actual = getUserDAO().getUser(testUser.getUserID()); - assertEquals(testUser, actual); + User<X500Principal> actual = getUserDAO().getUser(testUser.getUserID()); + check(testUser, actual); return null; } @@ -149,6 +166,7 @@ public class LdapUserDAOTest } } }); + } /** @@ -216,4 +234,90 @@ public class LdapUserDAOTest }); } + + /** + * Test of getMember. + */ + @Test + public void testGetMember() throws Exception + { + Subject subject = new Subject(); + subject.getPrincipals().add(testUser.getUserID()); + + // do everything as owner + Subject.doAs(subject, new PrivilegedExceptionAction<Object>() + { + public Object run() throws Exception + { + try + { + User<X500Principal> actual = getUserDAO().getMember(new DN(testUserDN)); + check(testUser, actual); + return null; + } + catch (Exception e) + { + throw new Exception("Problems", e); + } + } + }); + + + // should also work as a different user + subject = new Subject(); + subject.getPrincipals().add(new HttpPrincipal("CadcDaoTest2")); + + // do everything as owner + Subject.doAs(subject, new PrivilegedExceptionAction<Object>() + { + public Object run() throws Exception + { + try + { + User<X500Principal> actual = getUserDAO().getMember(new DN(testUserDN)); + check(testUser, actual); + return null; + } + catch (Exception e) + { + throw new Exception("Problems", e); + } + } + }); + } + + + private static void check(final User<? extends Principal> user1, final User<? extends Principal> user2) + { + assertEquals(user1, user2); + assertEquals(user1.details, user2.details); + assertEquals(user1.details.size(), user2.details.size()); + assertEquals(user1.getIdentities(), user2.getIdentities()); + for(UserDetails d1 : user1.details) + { + assertTrue(user2.details.contains(d1)); + if(d1 instanceof PersonalDetails) + { + PersonalDetails pd1 = (PersonalDetails)d1; + boolean found = false; + for(UserDetails d2 : user2.details) + { + if(d2 instanceof PersonalDetails) + { + PersonalDetails pd2 = (PersonalDetails)d2; + assertEquals(pd1, pd2); // already done in contains above but just in case + assertEquals(pd1.address, pd2.address); + assertEquals(pd1.city, pd2.city); + assertEquals(pd1.country, pd2.country); + assertEquals(pd1.email, pd2.email); + assertEquals(pd1.institute, pd2.institute); + found = true; + } + assertTrue(found); + } + } + } + + } + } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ActivatedGroup.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ActivatedGroup.java index 189088c9de5af0f082e88032c846ae859c18c4ba..22e445ede8750b77db6dd5f0bc07db03420531db 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ActivatedGroup.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ActivatedGroup.java @@ -68,19 +68,18 @@ */ package ca.nrc.cadc.ac; -import java.security.Principal; public class ActivatedGroup extends Group { - - public ActivatedGroup(String groupID) - { - super(groupID); - } - - public ActivatedGroup(String groupID, User<? extends Principal> owner) + public ActivatedGroup(Group group) { - super(groupID, owner); + super(group.getID(), group.getOwner()); + this.description = group.description; + this.properties = group.getProperties(); + this.lastModified = group.lastModified; + this.getUserMembers().addAll(group.getUserMembers()); + this.getGroupMembers().addAll(group.getGroupMembers()); + this.getUserAdmins().addAll(group.getUserAdmins()); + this.getGroupAdmins().addAll(group.getGroupAdmins()); } - } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java index 5172ddb934629adcc018d4cdccc02dbed47e9912..c8e8048ee08a45712e47a93380c8441dd42f650a 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java @@ -88,21 +88,14 @@ public class Group // group's group members private Set<Group> groupMembers = new HashSet<Group>(); - public String description; - public Date lastModified; + // group's user admins + private Set<User<? extends Principal>> userAdmins = new HashSet<User<? extends Principal>>(); - // Access Control properties - /** - * group that can read details of this group - * Note: this class does not enforce any access control rules - */ - public Group groupRead; + // group's group admins + private Set<Group> groupAdmins = new HashSet<Group>(); - /** - * group that can read and write details of this group - * Note: this class does not enforce any access control rules - */ - public Group groupWrite; + public String description; + public Date lastModified; /** * Ctor. @@ -186,6 +179,24 @@ public class Group { return groupMembers; } + + /** + * + * @return individual user admins of this group + */ + public Set<User<? extends Principal>> getUserAdmins() + { + return userAdmins; + } + + /** + * + * @return group admins of this group + */ + public Set<Group> getGroupAdmins() + { + return groupAdmins; + } /* (non-Javadoc) * @see java.lang.Object#hashCode() diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java index 4b666bf54f3cb023faaac7ae1f26f630465d8bf0..8061f48773eeb3913a4b8020aba06af37e703fed 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java @@ -263,38 +263,37 @@ public class GroupReader } - // groupRead - Element groupReadElement = groupElement.getChild("groupRead"); - if (groupReadElement != null) + // userMembers + Element userMembersElement = groupElement.getChild("userMembers"); + if (userMembersElement != null) { - Element groupReadGroupElement = groupReadElement.getChild("group"); - if (groupReadGroupElement != null) + List<Element> userElements = userMembersElement.getChildren("user"); + for (Element userMember : userElements) { - group.groupRead = parseGroup(groupReadGroupElement); + group.getUserMembers().add(UserReader.parseUser(userMember)); } - } - - // groupWrite - Element groupWriteElement = groupElement.getChild("groupWrite"); - if (groupWriteElement != null) + + // groupAdmins + Element groupAdminsElement = groupElement.getChild("groupAdmins"); + if (groupAdminsElement != null) { - Element groupWriteGroupElement = groupWriteElement.getChild("group"); - if (groupWriteGroupElement != null) + List<Element> groupElements = groupAdminsElement.getChildren("group"); + for (Element groupMember : groupElements) { - group.groupWrite = parseGroup(groupWriteGroupElement); + group.getGroupAdmins().add(parseGroup(groupMember)); } } - // userMembers - Element userMembersElement = groupElement.getChild("userMembers"); - if (userMembersElement != null) + // userAdmins + Element userAdminsElement = groupElement.getChild("userAdmins"); + if (userAdminsElement != null) { - List<Element> userElements = userMembersElement.getChildren("user"); + List<Element> userElements = userAdminsElement.getChildren("user"); for (Element userMember : userElements) { - group.getUserMembers().add(UserReader.parseUser(userMember)); + group.getUserAdmins().add(UserReader.parseUser(userMember)); } } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java index 3c4e54471dd9e156be95c2c8aa7b24fa09c4a1bc..93fcd13c61f9a5492beb3468049997cbeb908b25 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java @@ -213,22 +213,6 @@ public class GroupWriter groupElement.addContent(groupMembersElement); } - // Group groupRead. - if (group.groupRead != null) - { - Element groupReadElement = new Element("groupRead"); - groupReadElement.addContent(getGroupElement(group.groupRead, false)); - groupElement.addContent(groupReadElement); - } - - // Group groupWrite. - if (group.groupWrite != null) - { - Element groupWriteElement = new Element("groupWrite"); - groupWriteElement.addContent(getGroupElement(group.groupWrite, false)); - groupElement.addContent(groupWriteElement); - } - // Group userMembers if ((group.getUserMembers() != null) && (!group.getUserMembers().isEmpty())) { @@ -239,6 +223,28 @@ public class GroupWriter } groupElement.addContent(userMembersElement); } + + // Group groupAdmins. + if ((group.getGroupAdmins() != null) && (!group.getGroupAdmins().isEmpty())) + { + Element groupAdminsElement = new Element("groupAdmins"); + for (Group groupMember : group.getGroupAdmins()) + { + groupAdminsElement.addContent(getGroupElement(groupMember, false)); + } + groupElement.addContent(groupAdminsElement); + } + + // Group userAdmins + if ((group.getUserAdmins() != null) && (!group.getUserAdmins().isEmpty())) + { + Element userAdminsElement = new Element("userAdmins"); + for (User<? extends Principal> userMember : group.getUserAdmins()) + { + userAdminsElement.addContent(UserWriter.getUserElement(userMember)); + } + groupElement.addContent(userAdminsElement); + } } return groupElement; diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java index 83675c64da17bab6cdc482c4da213357340d82da..c54f6ee56d0787551e5c3fb7c45a1fad85b67523 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java @@ -85,8 +85,8 @@ import org.apache.log4j.Logger; import org.junit.Test; import ca.nrc.cadc.auth.HttpPrincipal; -import ca.nrc.cadc.auth.NumericPrincipal; import ca.nrc.cadc.auth.OpenIdPrincipal; +import static org.junit.Assert.assertTrue; /** * @@ -161,15 +161,15 @@ public class GroupReaderWriterTest expected.lastModified = new Date(); expected.properties.add(new GroupProperty("key", "value", true)); - Group readGroup = new Group("read", new User<Principal>(new X500Principal("cn=foo,o=ca"))); - Group writeGroup = new Group("write", new User<Principal>(new NumericPrincipal(123l))); Group groupMember = new Group("member", new User<Principal>(new OpenIdPrincipal("bar"))); User<Principal> userMember = new User<Principal>(new HttpPrincipal("baz")); + Group groupAdmin = new Group("admin", new User<Principal>(new X500Principal("cn=foo,o=ca"))); + User<Principal> userAdmin = new User<Principal>(new HttpPrincipal("admin")); - expected.groupRead = readGroup; - expected.groupWrite = writeGroup; expected.getGroupMembers().add(groupMember); expected.getUserMembers().add(userMember); + expected.getGroupAdmins().add(groupAdmin); + expected.getUserAdmins().add(userAdmin); StringBuilder xml = new StringBuilder(); GroupWriter.write(expected, xml); @@ -181,8 +181,6 @@ public class GroupReaderWriterTest assertEquals(expected.description, actual.description); assertEquals(expected.lastModified, actual.lastModified); assertEquals(expected.getProperties(), actual.getProperties()); - assertEquals(expected.groupRead, actual.groupRead); - assertEquals(expected.groupWrite, actual.groupWrite); assertEquals(expected.getGroupMembers(), actual.getGroupMembers()); assertEquals(expected.getUserMembers(), actual.getUserMembers()); } diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java index af09cf893e6144d347dac2eed526d9496ecf1361..6451d53418baf3c5fa1099b97b738c508f27b70b 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java @@ -115,17 +115,15 @@ public class GroupTest assertEquals(group3.hashCode(), group4.hashCode()); assertEquals(group3,group4); - group3.description = "Test group"; + group4.getUserAdmins().add(user); assertEquals(group3.hashCode(), group4.hashCode()); assertEquals(group3,group4); - // group read and write equality tests - group3.groupRead = group4; + group3.getGroupAdmins().add(group4); assertEquals(group3.hashCode(), group4.hashCode()); assertEquals(group3,group4); - // group write equality tests - group3.groupWrite = group4; + group3.description = "Test group"; assertEquals(group3.hashCode(), group4.hashCode()); assertEquals(group3,group4);