diff --git a/projects/cadcAccessControl-Server/build.xml b/projects/cadcAccessControl-Server/build.xml index 8a87c3bca324f82a7393879f24f870c304c20d78..b5ad9f91fe28e1e3606f3b6ab652d333b722271e 100644 --- a/projects/cadcAccessControl-Server/build.xml +++ b/projects/cadcAccessControl-Server/build.xml @@ -148,7 +148,7 @@ <pathelement path="${jars}:${testingJars}"/> </classpath> <sysproperty key="ca.nrc.cadc.util.PropertiesReader.dir" value="test"/> - <test name="ca.nrc.cadc.ac.server.web.users.GetUserActionTest" /> + <test name="ca.nrc.cadc.ac.server.web.users.UserActionFactoryTest" /> <formatter type="plain" usefile="false" /> </junit> </target> diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupDetailSelector.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupDetailSelector.java new file mode 100644 index 0000000000000000000000000000000000000000..6874d550ed26406ebbaae8cd9489f1a82b23f11a --- /dev/null +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupDetailSelector.java @@ -0,0 +1,89 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2011. (c) 2011. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* <http://www.gnu.org/licenses/>. pas le cas, consultez : +* <http://www.gnu.org/licenses/>. +* +* $Revision: 5 $ +* +************************************************************************ +*/ + +package ca.nrc.cadc.ac.server; + +import ca.nrc.cadc.ac.Group; +import ca.nrc.cadc.ac.Role; + +/** + * + * @author pdowler + */ +public interface GroupDetailSelector +{ + /** + * Check if group details should be filled in for the group when + * querying for role. + * @param g + * @param r + * @return true if group details should be filled + */ + boolean isDetailedSearch(Group g, Role r); +} diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java index f88faffeb2851833b99c606de1f63e317703960f..070080806602418dc3d0b5cacfc8849e375b0869 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java @@ -68,13 +68,15 @@ */ package ca.nrc.cadc.ac.server; -import java.security.AccessControlException; -import java.security.Principal; -import java.util.Map; - -import ca.nrc.cadc.ac.*; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.UserAlreadyExistsException; +import ca.nrc.cadc.ac.UserNotFoundException; +import ca.nrc.cadc.ac.UserRequest; import ca.nrc.cadc.net.TransientException; +import java.security.AccessControlException; +import java.security.Principal; +import java.util.Collection; public interface UserPersistence<T extends Principal> { @@ -85,7 +87,7 @@ public interface UserPersistence<T extends Principal> * @throws TransientException If an temporary, unexpected problem occurred. * @throws AccessControlException If the operation is not permitted. */ - Map<String, PersonalDetails> getUsers() + Collection<User<Principal>> getUsers() throws TransientException, AccessControlException; /** diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java index affb8c52a77107c2bc02eb85189be23281fc0dcd..dae9d245d651331b3a0994cbbfa5d63141c60eaf 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java @@ -94,7 +94,7 @@ import java.util.Set; public abstract class LdapDAO { private static final Logger logger = Logger.getLogger(LdapDAO.class); - + private LDAPConnection conn; LdapConfig config; @@ -186,12 +186,12 @@ public abstract class LdapDAO } if (p instanceof NumericPrincipal) { - ldapField = "(&(objectclass=cadcaccount)(numericid=" + p.getName() + "))"; + ldapField = "(numericid=" + p.getName() + ")"; break; } if (p instanceof X500Principal) { - ldapField = "(&(objectclass=cadcaccount)(distinguishedname=" + p.getName() + "))"; + ldapField = "(distinguishedname=" + p.getName() + ")"; break; } if (p instanceof OpenIdPrincipal) @@ -234,7 +234,11 @@ public abstract class LdapDAO throws TransientException { logger.debug("Ldap result: " + code); - System.out.println("Ldap result: " + code); + + if (code == ResultCode.SUCCESS || code == ResultCode.NO_SUCH_OBJECT) + { + return; + } if (code == ResultCode.INSUFFICIENT_ACCESS_RIGHTS) { @@ -244,10 +248,6 @@ public abstract class LdapDAO { throw new AccessControlException("Invalid credentials "); } - else if ((code == ResultCode.SUCCESS) || (code == ResultCode.NO_SUCH_OBJECT)) - { - // all good. nothing to do - } else if (code == ResultCode.PARAM_ERROR) { throw new IllegalArgumentException("Error in Ldap parameters "); @@ -256,10 +256,16 @@ public abstract class LdapDAO { throw new TransientException("Connection problems "); } - else + else if (code == ResultCode.TIMEOUT || code == ResultCode.TIME_LIMIT_EXCEEDED) { - throw new RuntimeException("Ldap error (" + code.getName() + ")"); + throw new TransientException("ldap timeout"); } + else if (code == ResultCode.INVALID_DN_SYNTAX) + { + throw new IllegalArgumentException("Invalid DN syntax"); + } + + throw new RuntimeException("Ldap error (" + code.getName() + ")"); } } 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 71ddf32316e6de250ab5ef5b9a9c2afdb26657b6..1cee54aed419269958d481b45a0614ffb1180692 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 @@ -68,6 +68,18 @@ */ package ca.nrc.cadc.ac.server.ldap; +import java.security.AccessControlException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +import org.apache.log4j.Logger; + import ca.nrc.cadc.ac.ActivatedGroup; import ca.nrc.cadc.ac.Group; import ca.nrc.cadc.ac.GroupAlreadyExistsException; @@ -75,21 +87,44 @@ import ca.nrc.cadc.ac.GroupNotFoundException; import ca.nrc.cadc.ac.Role; import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.UserNotFoundException; +import ca.nrc.cadc.ac.server.GroupDetailSelector; import ca.nrc.cadc.net.TransientException; import ca.nrc.cadc.util.StringUtil; -import com.unboundid.ldap.sdk.*; -import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl; -import org.apache.log4j.Logger; -import javax.security.auth.x500.X500Principal; -import java.security.AccessControlException; -import java.security.Principal; -import java.util.*; +import com.unboundid.ldap.sdk.AddRequest; +import com.unboundid.ldap.sdk.Attribute; +import com.unboundid.ldap.sdk.DN; +import com.unboundid.ldap.sdk.Filter; +import com.unboundid.ldap.sdk.LDAPException; +import com.unboundid.ldap.sdk.LDAPResult; +import com.unboundid.ldap.sdk.LDAPSearchException; +import com.unboundid.ldap.sdk.Modification; +import com.unboundid.ldap.sdk.ModificationType; +import com.unboundid.ldap.sdk.ModifyRequest; +import com.unboundid.ldap.sdk.ResultCode; +import com.unboundid.ldap.sdk.SearchRequest; +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; public class LdapGroupDAO<T extends Principal> extends LdapDAO { private static final Logger logger = Logger.getLogger(LdapGroupDAO.class); - + + private static final String[] PUB_GROUP_ATTRS = new String[] + { + "entrydn", "cn" + }; + private static final String[] GROUP_ATTRS = new String[] + { + "entrydn", "cn", "nsaccountlock", "owner", "modifytimestamp", "description" + }; + private static final String[] GROUP_AND_MEMBER_ATTRS = new String[] + { + "entrydn", "cn", "nsaccountlock", "owner", "modifytimestamp", "description", "uniquemember" + }; + private LdapUserDAO<T> userPersist; public LdapGroupDAO(LdapConfig config, LdapUserDAO<T> userPersist) @@ -105,33 +140,33 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO /** * Persists a group. - * + * * @param group The group to create - * + * * @return created group - * - * @throws GroupAlreadyExistsException If a group with the same ID already + * + * @throws GroupAlreadyExistsException If a group with the same ID already * exists. * @throws TransientException If an temporary, unexpected problem occurred. * @throws UserNotFoundException If owner or a member not valid user. - * @throws GroupNotFoundException + * @throws GroupNotFoundException */ public Group addGroup(final Group group) throws GroupAlreadyExistsException, TransientException, - UserNotFoundException, AccessControlException, + UserNotFoundException, AccessControlException, GroupNotFoundException { if (group.getOwner() == null) { throw new IllegalArgumentException("Group owner must be specified"); } - + if (!group.getProperties().isEmpty()) { throw new UnsupportedOperationException( "Support for groups properties not available"); } - + if (!isCreatorOwner(group.getOwner())) { throw new AccessControlException("Group owner must be creator"); @@ -146,22 +181,22 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } else { - + DN ownerDN = userPersist.getUserDN(group.getOwner()); - + // add group to groups tree - LDAPResult result = addGroup(getGroupDN(group.getID()), - group.getID(), ownerDN, - group.description, - group.getUserMembers(), + LDAPResult result = addGroup(getGroupDN(group.getID()), + group.getID(), ownerDN, + group.description, + group.getUserMembers(), group.getGroupMembers()); LdapDAO.checkLdapResult(result.getResultCode()); - + // add group to admin groups tree - result = addGroup(getAdminGroupDN(group.getID()), - group.getID(), ownerDN, - group.description, - group.getUserAdmins(), + result = addGroup(getAdminGroupDN(group.getID()), + group.getID(), ownerDN, + group.description, + group.getUserAdmins(), group.getGroupAdmins()); LdapDAO.checkLdapResult(result.getResultCode()); // AD: Search results sometimes come incomplete if @@ -182,24 +217,24 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO logger.debug("addGroup Exception: " + e, e); LdapDAO.checkLdapResult(e.getResultCode()); throw new RuntimeException("Unexpected LDAP exception", e); - } + } } - + private LDAPResult addGroup(final DN groupDN, final String groupID, - final DN ownerDN, final String description, - final Set<User<? extends Principal>> users, + final DN ownerDN, final String description, + final Set<User<? extends Principal>> users, final Set<Group> groups) - throws UserNotFoundException, LDAPException, TransientException, + throws UserNotFoundException, LDAPException, TransientException, AccessControlException, GroupNotFoundException { // add new group List<Attribute> attributes = new ArrayList<Attribute>(); - Attribute ownerAttribute = + Attribute ownerAttribute = new Attribute("owner", ownerDN.toNormalizedString()); attributes.add(ownerAttribute); attributes.add(new Attribute("objectClass", "groupofuniquenames")); attributes.add(new Attribute("cn", groupID)); - + if (StringUtil.hasText(description)) { attributes.add(new Attribute("description", description)); @@ -223,7 +258,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } if (!members.isEmpty()) { - attributes.add(new Attribute("uniquemember", + attributes.add(new Attribute("uniquemember", (String[]) members.toArray(new String[members.size()]))); } @@ -232,10 +267,11 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO new ProxiedAuthorizationV2RequestControl( "dn:" + getSubjectDN().toNormalizedString())); + logger.debug("addGroup: " + groupDN); return getConnection().add(addRequest); } - - + + /** * Checks whether group name available for the user or already in use. * @param group @@ -244,7 +280,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO * @throws UserNotFoundException * @throws GroupNotFoundException * @throws TransientException - * @throws GroupAlreadyExistsException + * @throws GroupAlreadyExistsException */ private Group reactivateGroup(final Group group) throws AccessControlException, UserNotFoundException, @@ -252,22 +288,20 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO { try { - // check group name exists + // check group name exists Filter filter = Filter.createEqualityFilter("cn", group.getID()); - SearchRequest searchRequest = - new SearchRequest( - getGroupDN(group.getID()) - .toNormalizedString(), SearchScope.SUB, filter, + DN groupDN = getGroupDN(group.getID()); + SearchRequest searchRequest = + new SearchRequest(groupDN.toNormalizedString(), SearchScope.BASE, filter, new String[] {"nsaccountlock"}); searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + + new ProxiedAuthorizationV2RequestControl("dn:" + getSubjectDN().toNormalizedString())); - SearchResultEntry searchResult = - getConnection().searchForEntry(searchRequest); - + SearchResultEntry searchResult = getConnection().searchForEntry(searchRequest); + if (searchResult == null) { return null; @@ -277,18 +311,18 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO { throw new GroupAlreadyExistsException("Group already exists " + group.getID()); } - - // activate group + + // activate group try { return modifyGroup(null, group, true); - } + } catch (GroupNotFoundException e) { throw new RuntimeException( "BUG: group to modify does not exist" + group.getID()); - } - } + } + } catch (LDAPException e) { logger.debug("reactivateGroup Exception: " + e, e); @@ -296,67 +330,72 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throw new RuntimeException("Unexpected LDAP exception", e); } } - - + + /** * Get all group names. - * + * * @return A collection of strings - * + * * @throws TransientException If an temporary, unexpected problem occurred. */ - public Collection<String> getGroupNames() throws TransientException + public Collection<String> getGroupNames() + throws TransientException { try { - final Filter filter = Filter.createPresenceFilter("cn"); - final String [] attributes = new String[] {"cn", "nsaccountlock"}; - final Collection<String> groupNames = new ArrayList<String>(); - final long begin = System.currentTimeMillis(); + Filter filter = Filter.createPresenceFilter("cn"); + String [] attributes = new String[] {"cn", "nsaccountlock"}; + + SearchRequest searchRequest = + new SearchRequest(config.getGroupsDN(), + SearchScope.SUB, filter, attributes); - final SearchResult searchResult = - getConnection().search(new SearchResultListener() + SearchResult searchResult = null; + try + { + searchResult = getConnection().search(searchRequest); + } + catch (LDAPSearchException e) { - @Override - public void searchEntryReturned( - final SearchResultEntry searchEntry) + logger.debug("Could not find groups root", e); + LdapDAO.checkLdapResult(e.getResultCode()); + if (e.getResultCode() == ResultCode.NO_SUCH_OBJECT) { - groupNames.add(searchEntry.getAttributeValue("cn")); + throw new IllegalStateException("Could not find groups root"); } - @Override - public void searchReferenceReturned( - final SearchResultReference searchReference) - { - - } - }, config.getGroupsDN(), SearchScope.ONE, filter, attributes); + throw new IllegalStateException("unexpected failure", e); + } LdapDAO.checkLdapResult(searchResult.getResultCode()); - long end = System.currentTimeMillis(); + List<String> groupNames = new ArrayList<String>(); + for (SearchResultEntry next : searchResult.getSearchEntries()) + { + if (!next.hasAttribute("nsaccountlock")) + { + groupNames.add(next.getAttributeValue("cn")); + } + } - logger.info("<-- groupNames in " + ((new Long(end).doubleValue() - - new Long(begin).doubleValue()) - / 1000.0) + " seconds."); return groupNames; } catch (LDAPException e1) { - logger.debug("getGroupNames Exception: " + e1, e1); + logger.debug("getGroupNames Exception: " + e1, e1); LdapDAO.checkLdapResult(e1.getResultCode()); - throw new IllegalStateException("Unexpected exception: " - + e1.getMatchedDN(), e1); + throw new IllegalStateException("Unexpected exception: " + e1.getMatchedDN(), e1); } - + } /** - * Get the group with the given Group ID. - * + * Get the group with members. + * * @param groupID The Group unique ID. - * + * * @return A Group instance - * + * * @throws GroupNotFoundException If the group was not found. * @throws TransientException If an temporary, unexpected problem occurred. */ @@ -364,185 +403,96 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO 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 group = getGroup(getGroupDN(groupID), groupID, GROUP_AND_MEMBER_ATTRS); + + Group adminGroup = getGroup(getAdminGroupDN(groupID), null, GROUP_AND_MEMBER_ATTRS); + 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", "nsaccountlock"}; - 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(final DN groupDN, final String groupID, - final boolean withMembers, final String[] attributes) - throws GroupNotFoundException, TransientException, + // groupID is here so exceptions and loggiong have plain groupID instead of DN + private Group getGroup(final DN groupDN, final String xgroupID, String[] attributes) + throws GroupNotFoundException, TransientException, AccessControlException { + logger.debug("getGroup: " + groupDN + " attrs: " + attributes.length); + String loggableGroupID = xgroupID; + if (loggableGroupID == null) + loggableGroupID = groupDN.toString(); // member or admin group: same name, internal tree + try { - Filter filter = Filter.createEqualityFilter("cn", groupID); - - SearchRequest searchRequest = - new SearchRequest(groupDN.toNormalizedString(), - SearchScope.SUB, filter, attributes); + Filter filter = Filter.createNOTFilter(Filter.createPresenceFilter("nsaccountlock")); + + SearchRequest searchRequest = + new SearchRequest(groupDN.toNormalizedString(), + SearchScope.BASE, filter, attributes); searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + + new ProxiedAuthorizationV2RequestControl("dn:" + getSubjectDN().toNormalizedString())); - SearchResult searchResult = null; - try - { - searchResult = getConnection().search(searchRequest); - } - catch (LDAPSearchException e) - { - if (e.getResultCode() == ResultCode.NO_SUCH_OBJECT) - { - String msg = "Group not found " + groupID; - logger.debug(msg); - throw new GroupNotFoundException(groupID); - } - else - { - LdapDAO.checkLdapResult(e.getResultCode()); - } - } - - if (searchResult.getEntryCount() == 0) - { - LdapDAO.checkLdapResult(searchResult.getResultCode()); - //access denied - String msg = "Not authorized to access " + groupID; - logger.debug(msg); - throw new AccessControlException(groupID); - } - - if (searchResult.getEntryCount() >1) - { - throw new RuntimeException("BUG: multiple results when retrieving group " + groupID); - } - - SearchResultEntry searchEntry = searchResult.getSearchEntries().get(0); - - if (searchEntry.getAttribute("nsaccountlock") != null) - { - // deleted group - String msg = "Group not found " + groupID; - logger.debug(msg); - throw new GroupNotFoundException(groupID); - } - - DN groupOwner = searchEntry.getAttributeValueAsDN("owner"); - if (groupOwner == null) - { - //TODO assume user not allowed to read group - throw new AccessControlException(groupID); - } - - User<X500Principal> owner; - try - { - owner = userPersist.getX500User(groupOwner); - } - catch (UserNotFoundException e) - { - throw new RuntimeException("BUG: group owner not found"); - } - - Group ldapGroup = new Group(groupID, owner); - if (searchEntry.hasAttribute("description")) - { - ldapGroup.description = - searchEntry.getAttributeValue("description"); - } - if (searchEntry.hasAttribute("modifytimestamp")) + + SearchResultEntry searchEntry = getConnection().searchForEntry(searchRequest); + + if (searchEntry == null) { - ldapGroup.lastModified = - searchEntry.getAttributeValueAsDate("modifytimestamp"); + String msg = "Group not found " + loggableGroupID; + logger.debug(msg + " cause: null"); + throw new GroupNotFoundException(loggableGroupID); } - if (withMembers) + Group ldapGroup = createGroupFromEntry(searchEntry, attributes); + + if (searchEntry.getAttributeValues("uniquemember") != null) { - if (searchEntry.getAttributeValues("uniquemember") != null) + for (String member : searchEntry.getAttributeValues("uniquemember")) { - for (String member : searchEntry - .getAttributeValues("uniquemember")) + DN memberDN = new DN(member); + if (memberDN.isDescendantOf(config.getUsersDN(), false)) { - DN memberDN = new DN(member); - if (memberDN.isDescendantOf(config.getUsersDN(), false)) + User<X500Principal> user; + try { - User<X500Principal> user; - try - { - user = userPersist.getX500User(memberDN); - } - catch (UserNotFoundException e) - { - throw new RuntimeException( - "BUG: group member not found"); - } + user = userPersist.getX500User(memberDN); ldapGroup.getUserMembers().add(user); } - else if (memberDN.isDescendantOf(config.getGroupsDN(), - false)) + catch (UserNotFoundException e) { - try - { - ldapGroup.getGroupMembers(). - add(new Group(getGroupID(memberDN))); - } - catch(GroupNotFoundException e) - { - // ignore as we are not cleaning up - // deleted groups from the group members - } + // ignore as we do not cleanup deleted users + // from groups they belong to } - else + } + else if (memberDN.isDescendantOf(config.getGroupsDN(), false)) + { + try + { + ldapGroup.getGroupMembers().add(getGroup(memberDN, null, PUB_GROUP_ATTRS)); + } + catch(GroupNotFoundException e) { - throw new RuntimeException( - "BUG: unknown member DN type: " + memberDN); + // ignore as we are not cleaning up + // deleted groups from the group members } } + else + { + throw new RuntimeException( + "BUG: unknown member DN type: " + memberDN); + } } } - + return ldapGroup; } catch (LDAPException e1) { - logger.debug("getGroup Exception: " + e1, e1); + logger.debug("getGroup Exception: " + e1, e1); LdapDAO.checkLdapResult(e1.getResultCode()); - throw new GroupNotFoundException("Not found " + groupID); + throw new RuntimeException("BUG: checkLdapResult didn't throw an exception"); } } @@ -550,9 +500,9 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO * Modify the given group. * * @param group The group to update. It must be an existing group - * + * * @return The newly updated group. - * + * * @throws GroupNotFoundException If the group was not found. * @throws TransientException If an temporary, unexpected problem occurred. * @throws AccessControlException If the operation is not permitted. @@ -563,9 +513,9 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO AccessControlException, UserNotFoundException { Group existing = getGroup(group.getID()); //group must exists first - return modifyGroup(existing, group, false); + return modifyGroup(existing, group, false); } - + private Group modifyGroup(final Group existing, final Group group, boolean withActivate) throws UserNotFoundException, TransientException, AccessControlException, GroupNotFoundException @@ -575,7 +525,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throw new UnsupportedOperationException( "Support for groups properties not available"); } - + boolean adminChanges = false; List<Modification> mods = new ArrayList<Modification>(); @@ -595,90 +545,89 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO { mods.add(new Modification(ModificationType.REPLACE, "description", group.description)); } - - Set<String> newMembers = new HashSet<String>(); - for (User<?> member : group.getUserMembers()) - { - DN memberDN = userPersist.getUserDN(member); - newMembers.add(memberDN.toNormalizedString()); - } - for (Group gr : group.getGroupMembers()) + try { - if (!checkGroupExists(gr.getID())) + Set<String> newMembers = new HashSet<String>(); + for (User<?> member : group.getUserMembers()) { - throw new GroupNotFoundException(gr.getID()); + DN memberDN = userPersist.getUserDN(member); + newMembers.add(memberDN.toNormalizedString()); } - DN grDN = getGroupDN(gr.getID()); - newMembers.add(grDN.toNormalizedString()); - } - - Set<String> newAdmins = new HashSet<String>(); - Set<User<? extends Principal>> existingUserAdmins = new HashSet<User<? extends Principal>>(0); - if (existing != null) - { - existingUserAdmins = existing.getUserAdmins(); - } - for (User<?> member : group.getUserAdmins()) - { - DN memberDN = userPersist.getUserDN(member); - newAdmins.add(memberDN.toNormalizedString()); - if (!existingUserAdmins.contains(member)) + for (Group gr : group.getGroupMembers()) { - adminChanges = true; + if (!checkGroupExists(gr.getID())) + { + throw new GroupNotFoundException(gr.getID()); + } + DN grDN = getGroupDN(gr.getID()); + newMembers.add(grDN.toNormalizedString()); } - } - - Set<Group> existingGroupAdmins = new HashSet<Group>(0); - if (existing != null) - { - existingGroupAdmins = existing.getGroupAdmins(); - } - for (Group gr : group.getGroupAdmins()) - { - if (!checkGroupExists(gr.getID())) + + Set<String> newAdmins = new HashSet<String>(); + Set<User<? extends Principal>> existingUserAdmins = new HashSet<User<? extends Principal>>(0); + if (existing != null) { - throw new GroupNotFoundException(gr.getID()); + existingUserAdmins = existing.getUserAdmins(); + } + for (User<?> member : group.getUserAdmins()) + { + DN memberDN = userPersist.getUserDN(member); + newAdmins.add(memberDN.toNormalizedString()); + if (!existingUserAdmins.contains(member)) + { + adminChanges = true; + } } - DN grDN = getGroupDN(gr.getID()); - newAdmins.add(grDN.toNormalizedString()); - if (!existingGroupAdmins.contains(gr)) + Set<Group> existingGroupAdmins = new HashSet<Group>(0); + if (existing != null) { - adminChanges = true; + existingGroupAdmins = existing.getGroupAdmins(); + } + for (Group gr : group.getGroupAdmins()) + { + if (!checkGroupExists(gr.getID())) + { + throw new GroupNotFoundException(gr.getID()); + } + + DN grDN = getGroupDN(gr.getID()); + newAdmins.add(grDN.toNormalizedString()); + if (!existingGroupAdmins.contains(gr)) + { + adminChanges = true; + } + } + + 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 (if necessary) + if (adminChanges) + { + ModifyRequest modifyRequest = new ModifyRequest(getAdminGroupDN(group.getID()), adminMods); + + modifyRequest.addControl( + new ProxiedAuthorizationV2RequestControl( + "dn:" + getSubjectDN().toNormalizedString())); + + LdapDAO.checkLdapResult(getConnection().modify(modifyRequest).getResultCode()); } - } - 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()]))); - - try - { - // modify admin group first (if necessary) - if (adminChanges) - { - ModifyRequest modifyRequest = new ModifyRequest(getAdminGroupDN(group.getID()), adminMods); - - modifyRequest.addControl( - new ProxiedAuthorizationV2RequestControl( - "dn:" + getSubjectDN().toNormalizedString())); - LdapDAO.checkLdapResult(getConnection(). - modify(modifyRequest).getResultCode()); - } - // modify the group itself now - ModifyRequest modifyRequest = new ModifyRequest(getGroupDN(group.getID()), mods); + ModifyRequest modifyRequest = new ModifyRequest(getGroupDN(group.getID()), mods); modifyRequest.addControl( new ProxiedAuthorizationV2RequestControl( "dn:" + getSubjectDN().toNormalizedString())); - LdapDAO.checkLdapResult(getConnection(). - modify(modifyRequest).getResultCode()); + + LdapDAO.checkLdapResult(getConnection().modify(modifyRequest).getResultCode()); } catch (LDAPException e1) { - logger.debug("Modify Exception: " + e1, e1); + logger.debug("Modify Exception: " + e1, e1); LdapDAO.checkLdapResult(e1.getResultCode()); } try @@ -694,16 +643,15 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } catch (GroupNotFoundException e) { - throw new RuntimeException( - "BUG: modified group not found (" + group.getID() + ")"); + throw new RuntimeException("BUG: modified group not found (" + group.getID() + ")"); } } /** * Deletes the group. - * + * * @param groupID The group to delete - * + * * @throws GroupNotFoundException If the group was not found. * @throws TransientException If an temporary, unexpected problem occurred. */ @@ -714,19 +662,19 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO deleteGroup(getGroupDN(groupID), groupID, false); deleteGroup(getAdminGroupDN(groupID), groupID, true); } - - private void deleteGroup(final DN groupDN, final String groupID, + + private void deleteGroup(final DN groupDN, final String groupID, final boolean isAdmin) throws GroupNotFoundException, TransientException, AccessControlException { - Group group = getGroup(groupDN, groupID, true); + Group group = getGroup(groupDN, groupID, GROUP_AND_MEMBER_ATTRS); List<Modification> modifs = new ArrayList<Modification>(); modifs.add(new Modification(ModificationType.ADD, "nsaccountlock", "true")); - + if (isAdmin) { - if (!group.getGroupAdmins().isEmpty() || + if (!group.getGroupAdmins().isEmpty() || !group.getUserAdmins().isEmpty()) { modifs.add(new Modification(ModificationType.DELETE, "uniquemember")); @@ -734,7 +682,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } else { - if (!group.getGroupMembers().isEmpty() || + if (!group.getGroupMembers().isEmpty() || !group.getUserMembers().isEmpty()) { modifs.add(new Modification(ModificationType.DELETE, "uniquemember")); @@ -752,35 +700,32 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } catch (LDAPException e1) { - logger.debug("Delete Exception: " + e1, e1); + logger.debug("Delete Exception: " + e1, e1); LdapDAO.checkLdapResult(e1.getResultCode()); } - + try { - getGroup(group.getID()); - throw new RuntimeException("BUG: group not deleted " + - group.getID()); + getGroup(getGroupDN(group.getID()), null, GROUP_ATTRS); + throw new RuntimeException("BUG: group not deleted " + group.getID()); } - catch (GroupNotFoundException ignore) {} + catch (GroupNotFoundException ignore) { } } - + /** - * Obtain a Collection of Groups that fit the given query. - * + * Obtain a Collection of Groups that fit the given query. The returned groups + * will not include members. + * * @param userID The userID. * @param role Role of the user, either owner, member, or read/write. * @param groupID The Group ID. - * - * @return Collection of Groups - * matching GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, - * readGrDN.toNormalizedString()) the query, or empty - * Collection. Never null. + * + * @return possibly empty collection of Group that match the query * @throws TransientException If an temporary, unexpected problem occurred. * @throws UserNotFoundException * @throws GroupNotFoundException */ - public Collection<Group> getGroups(final T userID, final Role role, + public Collection<Group> getGroups(final T userID, final Role role, final String groupID) throws TransientException, AccessControlException, GroupNotFoundException, UserNotFoundException @@ -796,107 +741,162 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO // no anonymous searches throw new AccessControlException("Not authorized to search"); } - - Collection<DN> groupDNs = new HashSet<DN>(); + + Collection<Group> ret; if (role == Role.OWNER) { - groupDNs.addAll(getOwnerGroups(user, userDN, groupID)); - } - else if (role == Role.MEMBER) - { - groupDNs.addAll(getMemberGroups(user, userDN, groupID, false)); - } - else if (role == Role.ADMIN) - { - groupDNs.addAll(getMemberGroups(user, userDN, groupID, true)); + ret = getOwnerGroups(user, userDN, groupID); } - - if (logger.isDebugEnabled()) + else { - for (DN dn : groupDNs) + Collection<DN> groupDNs = null; + + if (role == Role.MEMBER) { - logger.debug("Search adding DN: " + dn); + groupDNs = getMemberGroups(user, userDN, groupID, false); } - } - - Collection<Group> groups = new HashSet<Group>(); - try - { - for (DN groupDN : groupDNs) + else if (role == Role.ADMIN) { - if (role == Role.ADMIN) - { - groupDN = new DN(groupDN.getRDNString() + "," + config.getGroupsDN()); - } - try - { - groups.add(getGroup(groupDN)); - logger.debug("Search adding group: " + groupDN); - } - catch (GroupNotFoundException e) + groupDNs = getMemberGroups(user, userDN, groupID, true); + } + else + throw new IllegalArgumentException("null role"); + + ret = new ArrayList<Group>(); + try + { + for (DN groupDN : groupDNs) { - final String message = "BUG: group " + groupDN + " not found but " + - "membership exists (" + userID + ")"; - logger.error(message); - //throw new IllegalStateException(message); + if (role == Role.ADMIN) + { + groupDN = new DN(groupDN.getRDNString() + "," + config.getGroupsDN()); + } + try + { + Group g = createGroupFromDN(groupDN); + if (isDetailedSearch(g, role)) + { + g = getGroup(groupDN, null, GROUP_ATTRS); + } + logger.debug("found group: " + g.getID()); + ret.add(g); + } + catch (GroupNotFoundException e) + { + final String message = "BUG: group " + groupDN + " not found but " + + "membership exists (" + userID + ")"; + logger.error(message); + } } } + catch (LDAPException e) + { + logger.debug("getGroups Exception: " + e, e); + throw new TransientException("Error getting group", e); + } } - catch (LDAPException e) + + logger.debug("found: " + ret.size() + "groups matching " + userID + "," + role + "," + groupID); + return ret; + } + + // some pretty horrible hacks to avoid querying LDAP for group details... + private Group createGroupFromDN(DN groupDN) + { + String cn = groupDN.getRDNString(); + String[] parts = cn.split("="); + if (parts.length == 2 && parts[0].equals("cn")) { - logger.debug("getGroups Exception: " + e, e); - throw new TransientException("Error getting group", e); + return new Group(parts[1]); } - return groups; + throw new RuntimeException("BUG: failed to extract group name from " + groupDN.toString()); } - - protected Collection<DN> getOwnerGroups(final User<T> user, + // this gets filled by the LdapgroupPersistence + GroupDetailSelector searchDetailSelector; + + private boolean isDetailedSearch(Group g, Role r) + { + if (searchDetailSelector == null) + return true; + return searchDetailSelector.isDetailedSearch(g, r); + } + // end of horribleness + + protected Collection<Group> getOwnerGroups(final User<T> user, final DN userDN, final String groupID) - throws TransientException, AccessControlException, - GroupNotFoundException, UserNotFoundException + throws TransientException, AccessControlException { - Collection<DN> groupDNs = new HashSet<DN>(); + Collection<Group> ret = new ArrayList<Group>(); try - { - Filter filter = Filter.createEqualityFilter("owner", - userDN.toString()); + { + Filter filter = Filter.createNOTFilter(Filter.createPresenceFilter("nsaccountlock")); + + filter = Filter.createANDFilter(filter, + Filter.createEqualityFilter("owner", userDN.toNormalizedString())); + if (groupID != null) { - getGroup(groupID); - filter = Filter.createANDFilter(filter, - Filter.createEqualityFilter("cn", groupID)); + DN groupDN = getGroupDN(groupID); + filter = Filter.createANDFilter(filter, + Filter.createEqualityFilter("entrydn", groupDN.toNormalizedString())); } - + SearchRequest searchRequest = new SearchRequest( - config.getGroupsDN(), SearchScope.SUB, filter, "entrydn", "nsaccountlock"); - + config.getGroupsDN(), SearchScope.SUB, filter, GROUP_ATTRS); + searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + + new ProxiedAuthorizationV2RequestControl("dn:" + getSubjectDN().toNormalizedString())); - + SearchResult results = getConnection().search(searchRequest); for (SearchResultEntry result : results.getSearchEntries()) { - String entryDN = result.getAttributeValue("entrydn"); - // make sure the group isn't deleted - if (result.getAttribute("nsaccountlock") == null) - { - groupDNs.add(new DN(entryDN)); - } - + ret.add(createGroupFromEntry(result, GROUP_ATTRS)); } } catch (LDAPException e1) { - logger.debug("getOwnerGroups Exception: " + e1, e1); + logger.debug("getOwnerGroups Exception: " + e1, e1); LdapDAO.checkLdapResult(e1.getResultCode()); } - return groupDNs; + return ret; + } + + private Group createGroupFromEntry(SearchResultEntry result, String[] attributes) + throws LDAPException + { + if (result.getAttribute("nsaccountlock") != null) + { + throw new RuntimeException("BUG: found group with nsaccountlock set: " + result.getAttributeValue("entrydn").toString()); + } + + String entryDN = result.getAttributeValue("entrydn"); + String groupName = result.getAttributeValue("cn"); + if (attributes == PUB_GROUP_ATTRS) + return new Group(groupName); + + DN ownerDN = result.getAttributeValueAsDN("owner"); + if (ownerDN == null) + throw new AccessControlException(groupName); + try + { + User owner = userPersist.getX500User(ownerDN); + Group g = new Group(groupName, owner); + if (result.hasAttribute("description")) + g.description = result.getAttributeValue("description"); + if (result.hasAttribute("modifytimestamp")) + g.lastModified = result.getAttributeValueAsDate("modifytimestamp"); + return g; + } + catch(UserNotFoundException ex) + { + throw new RuntimeException("Invalid state: owner does not exist: " + ownerDN + " group: " + entryDN); + } } - - protected Collection<DN> getMemberGroups(final User<T> user, - final DN userDN, + + protected Collection<DN> getMemberGroups(final User<T> user, + final DN userDN, final String groupID, final boolean isAdmin) throws TransientException, AccessControlException, @@ -922,114 +922,17 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } else { - Collection<DN> memberGroupDNs = + Collection<DN> memberGroupDNs = userPersist.getUserGroups(user.getUserID(), isAdmin); groupDNs.addAll(memberGroupDNs); } return groupDNs; } - - /** - * Returns a group based on its LDAP DN. The returned group does not contain - * members or admins - * - * @param groupDN - * @return - * @throws com.unboundid.ldap.sdk.LDAPException - * @throws ca.nrc.cadc.ac.GroupNotFoundException - if group does not exist, - * it's deleted or caller has no access to it. - */ - protected Group getGroup(final DN groupDN) - throws LDAPException, GroupNotFoundException, UserNotFoundException - { - Filter filter = Filter.createEqualityFilter("entrydn", - groupDN.toNormalizedString()); - - SearchRequest searchRequest = new SearchRequest( - config.getGroupsDN(), SearchScope.SUB, filter, - "cn", "description", "owner", "nsaccountlock"); - - searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + - getSubjectDN().toNormalizedString())); - - SearchResultEntry searchResult = - getConnection().searchForEntry(searchRequest); - if (searchResult == null) - { - String msg = "Group not found " + groupDN; - logger.debug(msg); - throw new GroupNotFoundException(groupDN.toNormalizedString()); - } - - if (searchResult.getAttribute("nsaccountlock") != null) - { - // deleted group - String msg = "Group not found " + groupDN; - logger.debug(msg); - throw new GroupNotFoundException(groupDN.toNormalizedString()); - } - - Group group = new Group(searchResult.getAttributeValue("cn"), - userPersist.getX500User( - new DN(searchResult.getAttributeValue( - "owner")))); - group.description = searchResult.getAttributeValue("description"); - return group; - } - - /** - * Returns a group ID corresponding to a DN. Although the groupID can be - * deduced from the group DN, this method checks if the group exists and - * it's active and throws an exception if any of those conditions are not - * met. - * - * @param groupDN - * @return - * @throws com.unboundid.ldap.sdk.LDAPException - * @throws ca.nrc.cadc.ac.GroupNotFoundException - Group not found or not - * active - */ - protected String getGroupID(final DN groupDN) - throws LDAPException, GroupNotFoundException - { - Filter filter = Filter.createEqualityFilter("entrydn", - groupDN.toNormalizedString()); - - SearchRequest searchRequest = new SearchRequest( - config.getGroupsDN(), SearchScope.SUB, filter, - "cn", "nsaccountlock"); - - searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + - getSubjectDN().toNormalizedString())); - - SearchResultEntry searchResult = - getConnection().searchForEntry(searchRequest); - - if (searchResult == null) - { - String msg = "Group not found " + groupDN; - logger.debug(msg); - throw new GroupNotFoundException(groupDN.toNormalizedString()); - } - - if (searchResult.getAttribute("nsaccountlock") != null) - { - // deleted group - String msg = "Group not found " + groupDN; - logger.debug(msg); - throw new GroupNotFoundException(groupDN.toNormalizedString()); - } - - return searchResult.getAttributeValue("cn"); - } - /** - * + * * @param groupID - * @return + * @return */ protected DN getGroupDN(final String groupID) throws TransientException { @@ -1039,16 +942,16 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } catch (LDAPException e) { - logger.debug("getGroupDN Exception: " + e, e); + logger.debug("getGroupDN Exception: " + e, e); LdapDAO.checkLdapResult(e.getResultCode()); } throw new IllegalArgumentException(groupID + " not a valid group ID"); } - + /** - * + * * @param groupID - * @return + * @return */ protected DN getAdminGroupDN(final String groupID) throws TransientException { @@ -1058,24 +961,24 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } catch (LDAPException e) { - logger.debug("getAdminGroupDN Exception: " + e, e); + logger.debug("getAdminGroupDN Exception: " + e, e); LdapDAO.checkLdapResult(e.getResultCode()); } throw new IllegalArgumentException(groupID + " not a valid group ID"); } - + /** - * + * * @param owner * @return - * @throws UserNotFoundException + * @throws UserNotFoundException */ protected boolean isCreatorOwner(final User<? extends Principal> owner) throws UserNotFoundException { try { - User<X500Principal> subjectUser = + User<X500Principal> subjectUser = userPersist.getX500User(getSubjectDN()); if (subjectUser.equals(owner)) { @@ -1089,18 +992,20 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throw new RuntimeException(e); } } - - private boolean checkGroupExists(String groupID) - throws TransientException + + private boolean checkGroupExists(String groupID) + throws LDAPException, TransientException { - for (String groupName : getGroupNames()) + try { - if (groupName.equalsIgnoreCase(groupID)) - { - return true; - } + Group g = getGroup(getGroupDN(groupID), groupID, PUB_GROUP_ATTRS); + return true; + } + catch(GroupNotFoundException ex) + { + return false; } - return false; + finally { } } } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java index e66dc2e5960f289be4050c3d87429b0a3f93c38d..9f66002c0eb4cd86c8e328440456b3f54408a585 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java @@ -79,6 +79,7 @@ import ca.nrc.cadc.ac.GroupAlreadyExistsException; import ca.nrc.cadc.ac.GroupNotFoundException; import ca.nrc.cadc.ac.Role; import ca.nrc.cadc.ac.UserNotFoundException; +import ca.nrc.cadc.ac.server.GroupDetailSelector; import ca.nrc.cadc.ac.server.GroupPersistence; import ca.nrc.cadc.net.TransientException; @@ -88,12 +89,19 @@ public class LdapGroupPersistence<T extends Principal> private static final Logger log = Logger.getLogger(LdapGroupPersistence.class); private final LdapConfig config; + + private GroupDetailSelector detailSelector; public LdapGroupPersistence() { config = LdapConfig.getLdapConfig(); } + protected void setDetailSelector(GroupDetailSelector gds) + { + this.detailSelector = gds; + } + public Collection<String> getGroupNames() throws TransientException, AccessControlException { @@ -233,6 +241,7 @@ public class LdapGroupPersistence<T extends Principal> { userDAO = new LdapUserDAO<T>(config); groupDAO = new LdapGroupDAO<T>(config, userDAO); + groupDAO.searchDetailSelector = detailSelector; Collection<Group> ret = groupDAO.getGroups(userID, role, groupID); return ret; } 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 219728d8a32b0ae1eb1e05296c04bb87ae825d2c..5108b0828c2c1618262d3aa22e63eedc69057829 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 @@ -150,8 +150,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO private String[] userAttribs = new String[] { LDAP_FIRST_NAME, LDAP_LAST_NAME, LDAP_ADDRESS, LDAP_CITY, - LDAP_COUNTRY, - LDAP_EMAIL, LDAP_INSTITUTE + LDAP_COUNTRY, LDAP_EMAIL, LDAP_INSTITUTE }; private String[] memberAttribs = new String[] { @@ -286,7 +285,16 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO DN userDN; try { - userDN = getUserRequestsDN(userRequest.getUser().getUserID().getName()); + T userID = userRequest.getUser().getUserID(); + try + { + getUser(userID, config.getUsersDN(), false); + throw new UserAlreadyExistsException(userID.getName() + " found in " + + config.getUsersDN()); + } + catch (UserNotFoundException ignore) {} + + userDN = getUserRequestsDN(userID.getName()); addUser(userRequest, userDN); // AD: Search results sometimes come incomplete if @@ -294,7 +302,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO getConnection().reconnect(); try { - return getUser(userRequest.getUser().getUserID(), config.getUserRequestsDN()); + return getUser(userID, config.getUserRequestsDN()); } catch (UserNotFoundException e) { @@ -444,7 +452,6 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO return getUser(userID, config.getUserRequestsDN()); } - /** * Get the user specified by userID. * @@ -456,6 +463,24 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO * @throws AccessControlException If the operation is not permitted. */ private User<T> getUser(final T userID, final String usersDN) + throws UserNotFoundException, TransientException, + AccessControlException + { + return getUser(userID, usersDN, true); + } + + /** + * Get the user specified by userID. + * + * @param userID The userID. + * @param usersDN The LDAP tree to search. + * @param proxy If true proxy the request as the calling user. + * @return User instance. + * @throws UserNotFoundException when the user is not found. + * @throws TransientException If an temporary, unexpected problem occurred. + * @throws AccessControlException If the operation is not permitted. + */ + private User<T> getUser(final T userID, final String usersDN, boolean proxy) throws UserNotFoundException, TransientException, AccessControlException { @@ -466,8 +491,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO "Unsupported principal type " + userID.getClass()); } - searchField = "(&(objectclass=inetorgperson)(objectclass=cadcaccount)(" + - searchField + "=" + userID.getName() + "))"; + searchField = "(" + searchField + "=" + userID.getName() + ")"; logger.debug(searchField); SearchResultEntry searchResult = null; @@ -476,7 +500,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO SearchRequest searchRequest = new SearchRequest(usersDN, SearchScope.SUB, searchField, userAttribs); - if (isSecure(usersDN)) + if (proxy && isSecure(usersDN)) { searchRequest.addControl( new ProxiedAuthorizationV2RequestControl( @@ -539,11 +563,10 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO * @return A map of string keys to string values. * @throws TransientException If an temporary, unexpected problem occurred. */ - public Map<String, PersonalDetails> getUsers() + public Collection<User<Principal>> getUsers() throws TransientException { - final Map<String, PersonalDetails> users = - new HashMap<String, PersonalDetails>(); + final Collection<User<Principal>> users = new ArrayList<User<Principal>>(); try { @@ -566,16 +589,15 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO { if (!next.hasAttribute(LDAP_NSACCOUNTLOCK)) { - final String trimmedFirstName = + final String firstName = next.getAttributeValue(LDAP_FIRST_NAME).trim(); - final String trimmedLastName = + final String lastName = next.getAttributeValue(LDAP_LAST_NAME).trim(); - final String trimmedUID = - next.getAttributeValue(LDAP_UID).trim(); - - users.put(trimmedUID, - new PersonalDetails(trimmedFirstName, - trimmedLastName)); + final String uid = next.getAttributeValue(LDAP_UID).trim(); + User<Principal> user = new User<Principal>(new HttpPrincipal(uid)); + PersonalDetails pd = new PersonalDetails(firstName, lastName); + user.details.add(pd); + users.add(user); } } } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java index 6a042cc3785193c354f28626a785e803c8190f88..fd8b41ec1abb427b629fc026f53eda34b767937a 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java @@ -75,6 +75,7 @@ import com.unboundid.ldap.sdk.DN; import java.security.AccessControlException; import java.security.Principal; import java.util.Collection; +import java.util.List; import java.util.Map; import org.apache.log4j.Logger; @@ -96,8 +97,8 @@ public class LdapUserPersistence<T extends Principal> logger.error("test/config/LdapConfig.properties file required.", e); } } - - public Map<String, PersonalDetails> getUsers() + + public Collection<User<Principal>> getUsers() throws TransientException, AccessControlException { LdapUserDAO<T> userDAO = null; @@ -125,7 +126,6 @@ public class LdapUserPersistence<T extends Principal> * @throws TransientException If an temporary, unexpected problem occurred. * @throws AccessControlException If the operation is not permitted. */ - @Override public User<T> addUser(UserRequest<T> user) throws TransientException, AccessControlException, UserAlreadyExistsException @@ -183,7 +183,6 @@ public class LdapUserPersistence<T extends Principal> * @throws TransientException If an temporary, unexpected problem occurred. * @throws AccessControlException If the operation is not permitted. */ - @Override public User<T> getPendingUser(final T userID) throws UserNotFoundException, TransientException, AccessControlException diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/SyncOutput.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/SyncOutput.java index 9ebd42f75c00d63f1ccc05a6cee320bb8e3b6d7f..1f67b9bf613c852c294515d673c4247b75c42dad 100644 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/SyncOutput.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/SyncOutput.java @@ -97,16 +97,18 @@ public class SyncOutput public void setCode(int code) { + log.debug("setting code"); if (writer != null) - return; + throw new IllegalStateException("attempted to set code after writer has been opened"); response.setStatus(code); + log.debug("set code " + code); } public void setHeader(String key, Object value) { if (writer != null) - return; + throw new IllegalStateException("attempted to set header after writer has been opened"); if (value == null) response.setHeader(key, null); diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ACSearchRunner.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ACSearchRunner.java index f576ec6ba47de6cf4cff2ee3e8fb394d142e3a32..80661d4573cc2e4efaac40fcbdc5e609de09053d 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ACSearchRunner.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ACSearchRunner.java @@ -335,6 +335,23 @@ public class ACSearchRunner implements JobRunner // } } */ + catch(IllegalArgumentException ex) + { + logInfo.setSuccess(true); + logInfo.setMessage(ex.getMessage()); + log.debug("FAIL", ex); + + syncOut.setResponseCode(400); + syncOut.setHeader("Content-Type", "text/plain"); + try + { + syncOut.getOutputStream().write(ex.getMessage().getBytes()); + } + catch (IOException e) + { + log.warn("Could not write response to output stream", e); + } + } catch (AccessControlException t) { logInfo.setSuccess(true); diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupAction.java index 2805eda54b4e7bebfe5f0fc719c84085651f6dfd..9a93cfecf3ead83a4ac28fe764ce58c7823470fc 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupAction.java @@ -68,19 +68,6 @@ */ package ca.nrc.cadc.ac.server.web.groups; -import java.io.IOException; -import java.security.AccessControlException; -import java.security.Principal; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.List; - -import javax.security.auth.Subject; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.log4j.Logger; - import ca.nrc.cadc.ac.GroupAlreadyExistsException; import ca.nrc.cadc.ac.GroupNotFoundException; import ca.nrc.cadc.ac.MemberAlreadyExistsException; @@ -91,6 +78,15 @@ import ca.nrc.cadc.ac.server.PluginFactory; import ca.nrc.cadc.ac.server.UserPersistence; import ca.nrc.cadc.ac.server.web.SyncOutput; import ca.nrc.cadc.net.TransientException; +import org.apache.log4j.Logger; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.security.AccessControlException; +import java.security.Principal; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.List; public abstract class AbstractGroupAction implements PrivilegedExceptionAction<Object> { @@ -208,18 +204,18 @@ public abstract class AbstractGroupAction implements PrivilegedExceptionAction<O private void sendError(int responseCode, String message) { syncOut.setHeader("Content-Type", "text/plain"); + syncOut.setCode(responseCode); if (message != null) { try { - syncOut.getWriter() .write(message); + syncOut.getWriter().write(message); } catch (IOException e) { log.warn("Could not write error message to output stream"); } } - syncOut.setCode(responseCode); } <T extends Principal> GroupPersistence<T> getGroupPersistence() diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/CreateGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/CreateGroupAction.java index a4341c40aa82975b1aebf363794f30dc1a00d95c..5afabdb07782eeda62a83d49f68efb1ab6595ff6 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/CreateGroupAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/CreateGroupAction.java @@ -99,7 +99,8 @@ public class CreateGroupAction extends AbstractGroupAction groupWriter.write(newGroup, syncOut.getWriter()); List<String> addedMembers = null; - if ((newGroup.getUserMembers().size() > 0) || (newGroup.getGroupMembers().size() > 0)) + if ((newGroup.getUserMembers().size() > 0) || + (newGroup.getGroupMembers().size() > 0)) { addedMembers = new ArrayList<String>(); for (Group gr : newGroup.getGroupMembers()) diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupServlet.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupServlet.java index 096b5827a25ad73b5efcb91427879d0c3cd28943..fb9a2dbfff49f2851412f52663898931a3d5b695 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupServlet.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupServlet.java @@ -80,7 +80,6 @@ import org.apache.log4j.Logger; import ca.nrc.cadc.ac.server.web.SyncOutput; import ca.nrc.cadc.auth.AuthenticationUtil; -import ca.nrc.cadc.util.StringUtil; /** * Servlet for handling all requests to /groups diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionFactory.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionFactory.java index 5cf7a054c70568d75fdddfa964a2a6334eb1c401..0009a53094c5be10b6ec77a68af06482f3959d2f 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionFactory.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionFactory.java @@ -201,7 +201,6 @@ public abstract class GroupsActionFactory } sb.append(request.getContextPath()); sb.append(request.getServletPath()); - sb.append("/"); sb.append(path); action = new ModifyGroupAction(groupName, sb.toString(), request.getInputStream()); diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ModifyGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ModifyGroupAction.java index f4094251a8e57c4f7a351ff40b12d357157df1ab..d42ca9b6525944989f454d2c16a17d1c6b1ca6dc 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ModifyGroupAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ModifyGroupAction.java @@ -133,7 +133,7 @@ public class ModifyGroupAction extends AbstractGroupAction } logGroupInfo(group.getID(), deletedMembers, addedMembers); - syncOut.setHeader("Location", "/" + group.getID()); + syncOut.setHeader("Location", request); syncOut.setCode(303); } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/AbstractUserAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/AbstractUserAction.java index 07a83782a467cf199f1106d3a21df5481fa67561..af79d45ffa72d6f8adcbe4db50a154d006aca303 100644 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/AbstractUserAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/AbstractUserAction.java @@ -69,7 +69,9 @@ package ca.nrc.cadc.ac.server.web.users; import ca.nrc.cadc.ac.PersonalDetails; +import ca.nrc.cadc.ac.ReaderException; import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.UserAlreadyExistsException; import ca.nrc.cadc.ac.UserNotFoundException; import ca.nrc.cadc.ac.UserRequest; import ca.nrc.cadc.ac.json.JsonUserListWriter; @@ -94,7 +96,9 @@ import java.io.Writer; import java.security.AccessControlException; import java.security.Principal; import java.security.PrivilegedExceptionAction; +import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; @@ -156,6 +160,13 @@ public abstract class AbstractUserAction implements PrivilegedExceptionAction<Ob this.logInfo.setMessage(message); sendError(400, message); } + catch (ReaderException e) + { + log.debug(e.getMessage(), e); + String message = e.getMessage(); + this.logInfo.setMessage(message); + sendError(400, message); + } catch (UserNotFoundException e) { log.debug(e.getMessage(), e); @@ -163,6 +174,13 @@ public abstract class AbstractUserAction implements PrivilegedExceptionAction<Ob this.logInfo.setMessage(message); sendError(404, message); } + catch (UserAlreadyExistsException e) + { + log.debug(e.getMessage(), e); + String message = "User not found: " + e.getMessage(); + this.logInfo.setMessage(message); + sendError(409, message); + } catch (UnsupportedOperationException e) { log.debug(e.getMessage(), e); @@ -196,19 +214,19 @@ public abstract class AbstractUserAction implements PrivilegedExceptionAction<Ob private void sendError(int responseCode, String message) { + syncOut.setCode(responseCode); syncOut.setHeader("Content-Type", "text/plain"); if (message != null) { try { - syncOut.getWriter() .write(message); + syncOut.getWriter().write(message); } catch (IOException e) { log.warn("Could not write error message to output stream"); } } - syncOut.setCode(responseCode); } @SuppressWarnings("unchecked") @@ -324,7 +342,7 @@ public abstract class AbstractUserAction implements PrivilegedExceptionAction<Ob * * @param users The Map of user IDs to names. */ - protected final void writeUsers(final Map<String, PersonalDetails> users) + protected final <T extends Principal> void writeUsers(final Collection<User<T>> users) throws IOException { syncOut.setHeader("Content-Type", acceptedContentType); @@ -342,28 +360,4 @@ public abstract class AbstractUserAction implements PrivilegedExceptionAction<Ob } } - void redirectGet(User<?> user) throws Exception - { - final Set<Principal> httpPrincipals = user.getIdentities(); - - String id = null; - String idType = null; - Iterator<Principal> i = httpPrincipals.iterator(); - Principal next = null; - while (idType == null && i.hasNext()) - { - next = i.next(); - idType = AuthenticationUtil.getPrincipalType(next); - id = next.getName(); - } - - if (idType == null) - { - throw new IllegalStateException("No identities found."); - } - - final String redirectURL = "/" + id + "?idType=" + idType; - syncOut.setHeader("Location", redirectURL); - syncOut.setCode(303); - } } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/LoginServlet.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/LoginServlet.java index f1daa9db4f5c8870590ca10c0698b5cbe55ce33c..eae23b582ad0798dded000843f53ea673a109eff 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/LoginServlet.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/LoginServlet.java @@ -71,16 +71,23 @@ package ca.nrc.cadc.ac.server.web.users; import java.io.IOException; import java.security.AccessControlException; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; +import ca.nrc.cadc.ac.UserNotFoundException; +import ca.nrc.cadc.ac.server.GroupPersistence; +import ca.nrc.cadc.ac.server.UserPersistence; +import ca.nrc.cadc.ac.server.ldap.LdapGroupPersistence; import ca.nrc.cadc.ac.server.ldap.LdapUserPersistence; import ca.nrc.cadc.auth.HttpPrincipal; import ca.nrc.cadc.auth.SSOCookieManager; import ca.nrc.cadc.log.ServletLogInfo; +import ca.nrc.cadc.net.TransientException; import ca.nrc.cadc.util.StringUtil; @SuppressWarnings("serial") @@ -88,6 +95,30 @@ public class LoginServlet extends HttpServlet { private static final Logger log = Logger.getLogger(LoginServlet.class); private static final String CONTENT_TYPE = "text/plain"; + public static final String PROXY_USER_DELIM = " as "; + String proxyGroup; // only users in this group can impersonate other users + String nonImpersonGroup; // users in this group cannot be impersonated + + + @Override + public void init(final ServletConfig config) throws ServletException + { + super.init(config); + + try + { + this.proxyGroup = config.getInitParameter( + LoginServlet.class.getName() + ".proxyGroup"); + log.info("proxyGroup: " + proxyGroup); + this.nonImpersonGroup = config.getInitParameter( + LoginServlet.class.getName() + ".nonImpersonGroup"); + log.info("nonImpersonGroup: " + nonImpersonGroup); + } + catch(Exception ex) + { + log.error("failed to init: " + ex); + } + } /** * Attempt to login for userid/password. */ @@ -101,14 +132,28 @@ public class LoginServlet extends HttpServlet { log.info(logInfo.start()); String userID = request.getParameter("username"); + String proxyUser = null; + if (userID.contains(PROXY_USER_DELIM)) + { + String[] fields = userID.split(PROXY_USER_DELIM); + proxyUser = fields[0]; + userID = fields[1]; + checkCanImpersonate(userID, proxyUser); + } String password = request.getParameter("password"); + UserPersistence up = new LdapUserPersistence(); if (StringUtil.hasText(userID)) { if (StringUtil.hasText(password)) { - if (new LdapUserPersistence().doLogin(userID, password)) - { - String token = new SSOCookieManager().generate(new HttpPrincipal(userID)); + if ((StringUtil.hasText(proxyUser) && + up.doLogin(proxyUser, password)) || + (!StringUtil.hasText(proxyUser) && + up.doLogin(userID, password))) + { + String token = + new SSOCookieManager().generate( + new HttpPrincipal(userID, proxyUser)); response.setContentType(CONTENT_TYPE); response.setContentLength(token.length()); response.getWriter().write(token); @@ -157,4 +202,29 @@ public class LoginServlet extends HttpServlet log.info(logInfo.end()); } } + + /** + * Checks if user can impersonate another user + */ + private void checkCanImpersonate(final String userID, final String proxyUser) + throws AccessControlException, UserNotFoundException, + TransientException + { + GroupPersistence<HttpPrincipal> gp = new LdapGroupPersistence<HttpPrincipal>(); + + if (!gp.isMember(new HttpPrincipal(proxyUser), proxyGroup)) + { + throw new AccessControlException(proxyUser + " as " + userID + + " failed: not in proxyGroup"); + } + + if (gp.isMember(new HttpPrincipal(userID), nonImpersonGroup)) + { + if (gp.isMember(new HttpPrincipal(proxyUser), proxyGroup)) + { + throw new AccessControlException(proxyUser + " as " + userID + + " failed: non impersonable"); + } + } + } } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/ModifyUserAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/ModifyUserAction.java index 4e7ebe372ba2a3c0feb149274bf8bbfcbb38b58f..51777edcdfc1e20d793a15bc9bf74e8c510a963c 100644 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/ModifyUserAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/ModifyUserAction.java @@ -84,13 +84,15 @@ import java.util.Set; public class ModifyUserAction extends AbstractUserAction { private final InputStream inputStream; + private final String request; - ModifyUserAction(final InputStream inputStream) + ModifyUserAction(final InputStream inputStream, final String request) { super(); this.inputStream = inputStream; + this.request = request; } @@ -100,7 +102,8 @@ public class ModifyUserAction extends AbstractUserAction final User<Principal> modifiedUser = modifyUser(user); logUserInfo(modifiedUser.getUserID().getName()); - redirectGet(modifiedUser); + syncOut.setHeader("Location", request); + syncOut.setCode(303); } /** diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/PasswordServlet.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/PasswordServlet.java index 5d07ea7147d9581fe140c98956454362652f750e..20fca463e31263da2908a76d88fff44da3cf668e 100644 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/PasswordServlet.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/PasswordServlet.java @@ -70,13 +70,19 @@ package ca.nrc.cadc.ac.server.web.users; import java.io.IOException; import java.security.AccessControlException; +import java.security.Principal; +import java.security.PrivilegedExceptionAction; import java.util.Set; +import java.util.TreeSet; import javax.security.auth.Subject; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import ca.nrc.cadc.ac.UserNotFoundException; +import ca.nrc.cadc.ac.server.ldap.LdapUserDAO; +import ca.nrc.cadc.net.TransientException; import org.apache.log4j.Logger; import ca.nrc.cadc.ac.User; @@ -85,6 +91,7 @@ import ca.nrc.cadc.auth.AuthenticationUtil; import ca.nrc.cadc.auth.HttpPrincipal; import ca.nrc.cadc.log.ServletLogInfo; import ca.nrc.cadc.util.StringUtil; +import org.omg.CORBA.UserException; /** @@ -117,37 +124,53 @@ public class PasswordServlet extends HttpServlet try { final Subject subject = AuthenticationUtil.getSubject(request); - if ((subject == null) - || (subject.getPrincipals(HttpPrincipal.class).isEmpty())) + if ((subject == null) || (subject.getPrincipals().isEmpty())) { logInfo.setMessage("Unauthorized subject"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } else { - logInfo.setSubject(subject); - final Set<HttpPrincipal> webPrincipals = - subject.getPrincipals(HttpPrincipal.class); - final User<HttpPrincipal> user = - new User<HttpPrincipal>(webPrincipals.iterator().next()); - String oldPassword = request.getParameter("old_password"); - String newPassword = request.getParameter("new_password"); - if (StringUtil.hasText(oldPassword)) + Subject.doAs(subject, new PrivilegedExceptionAction<Object>() { - if (StringUtil.hasText(newPassword)) + public Object run() throws Exception { - (new LdapUserPersistence<HttpPrincipal>()) - .setPassword(user, oldPassword, newPassword); - } - else - { - throw new IllegalArgumentException("Missing new password"); + LdapUserPersistence<Principal> dao = new LdapUserPersistence<Principal>(); + User<Principal> user; + try + { + user = dao.getUser(subject.getPrincipals().iterator().next()); + } + catch (UserNotFoundException e) + { + throw new AccessControlException("User not found"); + } + + Subject logSubject = new Subject(false, user.getIdentities(), + new TreeSet(), new TreeSet()); + + logInfo.setSubject(logSubject); + + String oldPassword = request.getParameter("old_password"); + String newPassword = request.getParameter("new_password"); + if (StringUtil.hasText(oldPassword)) + { + if (StringUtil.hasText(newPassword)) + { + dao.setPassword(user, oldPassword, newPassword); + } + else + { + throw new IllegalArgumentException("Missing new password"); + } + } + else + { + throw new IllegalArgumentException("Missing old password"); + } + return null; } - } - else - { - throw new IllegalArgumentException("Missing old password"); - } + }); } } catch (IllegalArgumentException e) diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserActionFactory.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserActionFactory.java index 6c7b0c33338c075c71e3688f65d068f0bb6c2773..457dd4b1b686ef42092abd6378945affbf82ed05 100644 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserActionFactory.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserActionFactory.java @@ -77,6 +77,7 @@ import ca.nrc.cadc.auth.NumericPrincipal; import ca.nrc.cadc.auth.OpenIdPrincipal; import java.io.IOException; +import java.net.URL; import java.security.Principal; import javax.security.auth.x500.X500Principal; import javax.servlet.http.HttpServletRequest; @@ -169,7 +170,23 @@ public abstract class UserActionFactory if (segments.length == 1) { - action = new ModifyUserAction(request.getInputStream()); + final URL requestURL = new URL(request.getRequestURL().toString()); + final StringBuilder sb = new StringBuilder(); + sb.append(requestURL.getProtocol()); + sb.append("://"); + sb.append(requestURL.getHost()); + if (requestURL.getPort() > 0) + { + sb.append(":"); + sb.append(requestURL.getPort()); + } + sb.append(request.getContextPath()); + sb.append(request.getServletPath()); + sb.append(path); + sb.append("?"); + sb.append(request.getQueryString()); + + action = new ModifyUserAction(request.getInputStream(), sb.toString()); } if (action != null) @@ -242,7 +259,7 @@ public abstract class UserActionFactory { return new User<X500Principal>(new X500Principal(userName)); } - else if (idType.equalsIgnoreCase(IdentityType.UID.getValue())) + else if (idType.equalsIgnoreCase(IdentityType.CADC.getValue())) { return new User<NumericPrincipal>(new NumericPrincipal( Long.parseLong(userName))); diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserServlet.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserServlet.java index 69a5c20a786b9cf364713c9555fd13a2b195cb60..6fbb19c354a93371a55156836e135a8394784f0f 100644 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserServlet.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserServlet.java @@ -192,7 +192,7 @@ public class UserServlet extends HttpServlet public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { - doAction(UserActionFactory.httpGetFactory(), request, response); + doAction(UserActionFactory.httpPostFactory(), request, response); } @Override diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/auth/AuthenticatorImpl.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/auth/AuthenticatorImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..024549e6ea3053593c4734ac608638b875154193 --- /dev/null +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/auth/AuthenticatorImpl.java @@ -0,0 +1,156 @@ +/* + ************************************************************************ + ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* + ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** + * + * (c) 2014. (c) 2014. + * Government of Canada Gouvernement du Canada + * National Research Council Conseil national de recherches + * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 + * All rights reserved Tous droits réservés + * + * NRC disclaims any warranties, Le CNRC dénie toute garantie + * expressed, implied, or énoncée, implicite ou légale, + * statutory, of any kind with de quelque nature que ce + * respect to the software, soit, concernant le logiciel, + * including without limitation y compris sans restriction + * any warranty of merchantability toute garantie de valeur + * or fitness for a particular marchande ou de pertinence + * purpose. NRC shall not be pour un usage particulier. + * liable in any event for any Le CNRC ne pourra en aucun cas + * damages, whether direct or être tenu responsable de tout + * indirect, special or general, dommage, direct ou indirect, + * consequential or incidental, particulier ou général, + * arising from the use of the accessoire ou fortuit, résultant + * software. Neither the name de l'utilisation du logiciel. Ni + * of the National Research le nom du Conseil National de + * Council of Canada nor the Recherches du Canada ni les noms + * names of its contributors may de ses participants ne peuvent + * be used to endorse or promote être utilisés pour approuver ou + * products derived from this promouvoir les produits dérivés + * software without specific prior de ce logiciel sans autorisation + * written permission. préalable et particulière + * par écrit. + * + * This file is part of the Ce fichier fait partie du projet + * OpenCADC project. OpenCADC. + * + * OpenCADC is free software: OpenCADC est un logiciel libre ; + * you can redistribute it and/or vous pouvez le redistribuer ou le + * modify it under the terms of modifier suivant les termes de + * the GNU Affero General Public la “GNU Affero General Public + * License as published by the License” telle que publiée + * Free Software Foundation, par la Free Software Foundation + * either version 3 of the : soit la version 3 de cette + * License, or (at your option) licence, soit (à votre gré) + * any later version. toute version ultérieure. + * + * OpenCADC is distributed in the OpenCADC est distribué + * hope that it will be useful, dans l’espoir qu’il vous + * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE + * without even the implied GARANTIE : sans même la garantie + * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ + * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF + * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence + * General Public License for Générale Publique GNU Affero + * more details. pour plus de détails. + * + * You should have received Vous devriez avoir reçu une + * a copy of the GNU Affero copie de la Licence Générale + * General Public License along Publique GNU Affero avec + * with OpenCADC. If not, see OpenCADC ; si ce n’est + * <http://www.gnu.org/licenses/>. pas le cas, consultez : + * <http://www.gnu.org/licenses/>. + * + * $Revision: 4 $ + * + ************************************************************************ + */ + +package ca.nrc.cadc.auth; + +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.UserNotFoundException; +import ca.nrc.cadc.ac.server.ldap.LdapUserPersistence; +import ca.nrc.cadc.profiler.Profiler; +import org.apache.log4j.Logger; + +import javax.security.auth.Subject; +import java.security.Principal; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +/** + * Implementation of default Authenticator for AuthenticationUtil in cadcUtil. + * This class augments the subject with additional identities using the + * access control library. + * + * @author pdowler + */ +public class AuthenticatorImpl implements Authenticator +{ + private static final Logger log = Logger.getLogger(AuthenticatorImpl.class); + + public AuthenticatorImpl() { } + + /** + * @param subject + * @return the possibly modified subject + */ + public Subject getSubject(Subject subject) + { + AuthMethod am = AuthenticationUtil.getAuthMethod(subject); + if (am == null || AuthMethod.ANON.equals(am)) + return subject; + + if (subject != null && subject.getPrincipals().size() > 0) + { + Profiler prof = new Profiler(AuthenticatorImpl.class); + this.augmentSubject(subject); + prof.checkpoint("userDAO.augmentSubject()"); + + // if the caller had an invalid or forged CADC_SSO cookie, we could get + // in here and then not match any known identity: drop to anon + if ( subject.getPrincipals(HttpPrincipal.class).isEmpty() ) // no matching cadc account + { + log.debug("HttpPrincipal not found - dropping to anon: " + subject); + subject = AuthenticationUtil.getAnonSubject(); + } + } + + return subject; + } + + protected void augmentSubject(final Subject subject) + { + try + { + PrivilegedExceptionAction<Object> action = + new PrivilegedExceptionAction<Object>() + { + public Object run() throws Exception + { + try + { + LdapUserPersistence<Principal> dao = new LdapUserPersistence<Principal>(); + User<Principal> user = dao.getUser(subject.getPrincipals().iterator().next()); + subject.getPrincipals().addAll(user.getIdentities()); + } + catch (UserNotFoundException e) + { + // ignore, could be an anonymous user + } + return null; + } + }; + + Subject.doAs(subject, action); + } + catch (PrivilegedActionException e) + { + String msg = "Error augmenting subject " + subject; + throw new RuntimeException(msg, e); + } + } + +} 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 b5f7b7fc706285aec7404148022f0a4ffae7f2a1..24b055459faa0ebe77d4fb669f3431118b147869 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 @@ -494,11 +494,11 @@ public class LdapGroupDAOTest extends AbstractLdapDAOTest for (Group group : groups) { log.debug("admin group: " + group.getID()); - if (group.getID().equals(testGroup1ID)) + if (group.getID().equalsIgnoreCase(testGroup1ID)) { found1 = true; } - if (group.getID().equals(testGroup2ID)) + if (group.getID().equalsIgnoreCase(testGroup2ID)) { found2 = true; } @@ -861,18 +861,10 @@ public class LdapGroupDAOTest extends AbstractLdapDAOTest getGroupDAO().getGroups(unknownPrincipal, Role.OWNER, groupID); fail("searchGroups with unknown user should throw " + - "UserNotFoundException"); + "AccessControlException"); } catch (AccessControlException ignore) {} - try - { - getGroupDAO().getGroups(daoTestPrincipal1, Role.OWNER, - "foo"); - fail("searchGroups with unknown user should throw " + - "GroupNotFoundException"); - } - catch (GroupNotFoundException ignore) {} return null; } }); diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserListActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserListActionTest.java index 8e3a6db4ccc8259169fbbb50232317d7e0fa9e86..0624272f497c63f2e8d502f43d482d43dc67c614 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserListActionTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserListActionTest.java @@ -70,6 +70,7 @@ package ca.nrc.cadc.ac.server.web.users; import ca.nrc.cadc.ac.PersonalDetails; +import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.json.JsonUserListWriter; import ca.nrc.cadc.ac.server.UserPersistence; import ca.nrc.cadc.ac.server.web.SyncOutput; @@ -85,7 +86,10 @@ import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; +import java.security.Principal; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.easymock.EasyMock.*; @@ -115,13 +119,14 @@ public class GetUserListActionTest createMock(SyncOutput.class); final UserPersistence<HttpPrincipal> mockUserPersistence = createMock(UserPersistence.class); - final Map<String, PersonalDetails> userEntries = - new HashMap<String, PersonalDetails>(); + List<User<Principal>> expectedUsers = new ArrayList<User<Principal>>(); for (int i = 1; i <= 5; i++) { - userEntries.put("USER_" + i, - new PersonalDetails("USER", Integer.toString(i))); + User<Principal> user = new User<Principal>(new HttpPrincipal("USER_" + i)); + PersonalDetails pd = new PersonalDetails("USER", Integer.toString(i)); + user.details.add(pd); + expectedUsers.add(user); } final GetUserListAction testSubject = new GetUserListAction() @@ -138,8 +143,7 @@ public class GetUserListActionTest final Writer actualWriter = new StringWriter(); final PrintWriter actualPrintWriter = new PrintWriter(actualWriter); - expect(mockUserPersistence.getUsers()).andReturn( - userEntries).once(); + expect(mockUserPersistence.getUsers()).andReturn(expectedUsers).once(); expect(mockSyncOut.getWriter()).andReturn(actualPrintWriter).once(); mockSyncOut.setHeader("Content-Type", "application/json"); expectLastCall().once(); @@ -153,7 +157,7 @@ public class GetUserListActionTest final Writer expectedWriter = new StringWriter(); final PrintWriter expectedPrintWriter = new PrintWriter(expectedWriter); JsonUserListWriter userListWriter = new JsonUserListWriter(); - userListWriter.write(userEntries, expectedPrintWriter); + userListWriter.write(expectedUsers, expectedPrintWriter); JSONAssert.assertEquals(expectedWriter.toString(), actualWriter.toString(), false); verify(mockSyncOut, mockUserPersistence); @@ -167,13 +171,14 @@ public class GetUserListActionTest createMock(SyncOutput.class); final UserPersistence<HttpPrincipal> mockUserPersistence = createMock(UserPersistence.class); - final Map<String, PersonalDetails> userEntries = - new HashMap<String, PersonalDetails>(); + List<User<Principal>> expectedUsers = new ArrayList<User<Principal>>(); for (int i = 1; i <= 5; i++) { - userEntries.put("USER_" + i, - new PersonalDetails("USER", Integer.toString(i))); + User<Principal> user = new User<Principal>(new HttpPrincipal("USER_" + i)); + PersonalDetails pd = new PersonalDetails("USER", Integer.toString(i)); + user.details.add(pd); + expectedUsers.add(user); } final GetUserListAction testSubject = new GetUserListAction() @@ -188,8 +193,7 @@ public class GetUserListActionTest final Writer actualWriter = new StringWriter(); final PrintWriter actualPrintWriter = new PrintWriter(actualWriter); - expect(mockUserPersistence.getUsers()).andReturn( - userEntries).once(); + expect(mockUserPersistence.getUsers()).andReturn(expectedUsers).once(); expect(mockSyncOut.getWriter()).andReturn(actualPrintWriter).once(); mockSyncOut.setHeader("Content-Type", "text/xml"); expectLastCall().once(); @@ -203,7 +207,7 @@ public class GetUserListActionTest final Writer expectedWriter = new StringWriter(); final PrintWriter expectedPrintWriter = new PrintWriter(expectedWriter); UserListWriter userListWriter = new UserListWriter(); - userListWriter.write(userEntries, expectedPrintWriter); + userListWriter.write(expectedUsers, expectedPrintWriter); assertEquals("Wrong XML", expectedWriter.toString(), actualWriter.toString()); verify(mockSyncOut, mockUserPersistence); diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/ModifyUserActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/ModifyUserActionTest.java index ff4538b29c58b0c6a33548b1907f78077309a866..861403fac3f2c3d8e3808c0210fe439935e53ebc 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/ModifyUserActionTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/ModifyUserActionTest.java @@ -108,6 +108,7 @@ public class ModifyUserActionTest final byte[] input = sb.toString().getBytes(); final InputStream inputStream = new ByteArrayInputStream(input); + final String request = "/CADCtest?idType=http"; // Should match the JSON above, without the e-mail modification. Principal principal = new HttpPrincipal("CADCtest"); @@ -119,8 +120,6 @@ public class ModifyUserActionTest personalDetail.email = "CADC.Test@nrc-cnrc.gc.ca"; userObject.details.add(personalDetail); - final HttpServletRequest mockRequest = - createMock(HttpServletRequest.class); final SyncOutput mockSyncOut = createMock(SyncOutput.class); @@ -130,11 +129,9 @@ public class ModifyUserActionTest expect(mockUserPersistence.modifyUser(userObject)).andReturn( userObject).once(); -// -// expect(mockRequest.getRemoteAddr()).andReturn(requestURL). -// once(); - mockSyncOut.setHeader("Location", "/CADCtest?idType=http"); + + mockSyncOut.setHeader("Location", request); expectLastCall().once(); mockSyncOut.setCode(303); @@ -143,9 +140,9 @@ public class ModifyUserActionTest mockSyncOut.setHeader("Content-Type", "application/json"); expectLastCall().once(); - replay(mockRequest, mockSyncOut, mockUserPersistence); + replay(mockSyncOut, mockUserPersistence); - final ModifyUserAction testSubject = new ModifyUserAction(inputStream) + final ModifyUserAction testSubject = new ModifyUserAction(inputStream, request) { @Override @SuppressWarnings("unchecked") @@ -161,6 +158,6 @@ public class ModifyUserActionTest testSubject.setLogInfo(logInfo); testSubject.doAction(); - verify(mockRequest, mockSyncOut, mockUserPersistence); + verify(mockSyncOut, mockUserPersistence); } } diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserActionFactoryTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserActionFactoryTest.java index 93aa2a541fdc96f9b2ba2f2057ab37becf347c8c..54db0b92985648da2304dadf29e42e63fcbaaa62 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserActionFactoryTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserActionFactoryTest.java @@ -175,11 +175,12 @@ public class UserActionFactoryTest HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class); EasyMock.expect(request.getPathInfo()).andReturn("userName"); - //EasyMock.expect(request.getRequestURL()).andReturn(sb); - //EasyMock.expect(request.getContextPath()).andReturn(""); - //EasyMock.expect(request.getServletPath()).andReturn(""); + EasyMock.expect(request.getRequestURL()).andReturn(sb); + EasyMock.expect(request.getContextPath()).andReturn(""); + EasyMock.expect(request.getServletPath()).andReturn(""); + EasyMock.expect(request.getQueryString()).andReturn(""); EasyMock.expect(request.getInputStream()).andReturn(null); - //EasyMock.expect(request.getParameter("idType")).andReturn("sessionID"); +// EasyMock.expect(request.getParameter("idType")).andReturn("sessionID"); EasyMock.replay(request); AbstractUserAction action = UserActionFactory.httpPostFactory().createAction(request); EasyMock.verify(request); diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java index 6a91450bc9acc1e27da28ae2f227edfa2fe8fd16..8dac1009e59bf92e492678dfb3837e7088aa0658 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java @@ -155,6 +155,11 @@ public class Group return owner; } + public void setOwner(User<? extends Principal> owner) + { + this.owner = owner; + } + /** * * @return a set of properties associated with a group diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/User.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/User.java index 97a63b6bb2827991999266e3e547cdf4bd31f42d..70ec423820e5bac0c3878dda8cb7a7469279e151 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/User.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/User.java @@ -152,6 +152,18 @@ public class User<T extends Principal> return getClass().getSimpleName() + "[" + userID.getName() + "]"; } + public <S extends UserDetails>S getUserDetail(final Class<S> userDetailsClass) + { + for (final UserDetails ud : details) + { + if (ud.getClass() == userDetailsClass) + { + return (S) ud; + } + } + return null; + } + public <S extends UserDetails> Set<S> getDetails( final Class<S> userDetailsClass) { diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java index bad17d1f5f80cee819082265ca7f344940ac14b1..3f0a4f954a61d5ec56ca98e4773e3ab842f2c401 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java @@ -68,28 +68,6 @@ */ package ca.nrc.cadc.ac.client; -import ca.nrc.cadc.ac.Group; -import ca.nrc.cadc.ac.GroupAlreadyExistsException; -import ca.nrc.cadc.ac.GroupNotFoundException; -import ca.nrc.cadc.ac.Role; -import ca.nrc.cadc.ac.User; -import ca.nrc.cadc.ac.UserNotFoundException; -import ca.nrc.cadc.ac.xml.GroupListReader; -import ca.nrc.cadc.ac.xml.GroupReader; -import ca.nrc.cadc.ac.xml.GroupWriter; -import ca.nrc.cadc.auth.AuthenticationUtil; -import ca.nrc.cadc.auth.HttpPrincipal; -import ca.nrc.cadc.auth.SSLUtil; -import ca.nrc.cadc.net.HttpDownload; -import ca.nrc.cadc.net.HttpPost; -import ca.nrc.cadc.net.HttpUpload; -import ca.nrc.cadc.net.InputStreamWrapper; -import ca.nrc.cadc.net.NetUtil; -import org.apache.log4j.Logger; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; -import javax.security.auth.Subject; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -106,17 +84,37 @@ import java.security.AccessController; import java.security.Principal; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; +import javax.security.auth.Subject; + +import ca.nrc.cadc.ac.*; +import ca.nrc.cadc.auth.HttpPrincipal; +import org.apache.log4j.Logger; + +import ca.nrc.cadc.ac.xml.GroupListReader; +import ca.nrc.cadc.ac.xml.GroupReader; +import ca.nrc.cadc.ac.xml.GroupWriter; +import ca.nrc.cadc.auth.AuthenticationUtil; +import ca.nrc.cadc.auth.SSLUtil; +import ca.nrc.cadc.net.HttpDownload; +import ca.nrc.cadc.net.HttpPost; +import ca.nrc.cadc.net.HttpUpload; +import ca.nrc.cadc.net.InputStreamWrapper; +import ca.nrc.cadc.net.NetUtil; +import ca.nrc.cadc.net.event.TransferEvent; +import ca.nrc.cadc.net.event.TransferListener; + /** * Client class for performing group searching and group actions * with the access control web service. */ -public class GMSClient +public class GMSClient implements TransferListener { private static final Logger log = Logger.getLogger(GMSClient.class); @@ -130,10 +128,10 @@ public class GMSClient * Constructor. * * @param baseURL The URL of the supporting access control web service - * obtained from the registry. + * obtained from the registry. */ - public GMSClient(final String baseURL) - throws IllegalArgumentException + public GMSClient(String baseURL) + throws IllegalArgumentException { if (baseURL == null) { @@ -159,6 +157,18 @@ public class GMSClient } } + public void transferEvent(TransferEvent te) + { + if ( TransferEvent.RETRYING == te.getState() ) + log.debug("retry after request failed, reason: " + te.getError()); + } + + public String getEventHeader() + { + return null; // no custom eventID header + } + + /** * Get a list of groups. * @@ -169,8 +179,6 @@ public class GMSClient throw new UnsupportedOperationException("Not yet implemented"); } - - /** * Obtain all of the users as userID - name in JSON format. * @@ -181,9 +189,8 @@ public class GMSClient { final List<User<HttpPrincipal>> webUsers = new ArrayList<User<HttpPrincipal>>(); - final HttpDownload httpDownload = - createDisplayUsersHTTPDownload(webUsers); + createDisplayUsersHTTPDownload(webUsers); httpDownload.setRequestProperty("Accept", "application/json"); httpDownload.run(); @@ -194,12 +201,10 @@ public class GMSClient { final String errMessage = error.getMessage(); final int responseCode = httpDownload.getResponseCode(); - - log.debug("getDisplayUsers response " + responseCode + ": " + - errMessage); - - if ((responseCode == 401) || (responseCode == 403) || - (responseCode == -1)) + log.debug("getDisplayUsers response " + responseCode + ": " + + errMessage); + if ((responseCode == 401) || (responseCode == 403) + || (responseCode == -1)) { throw new AccessControlException(errMessage); } @@ -207,9 +212,11 @@ public class GMSClient { throw new IllegalArgumentException(errMessage); } - - throw new IOException("HttpResponse (" + responseCode + ") - " - + errMessage); + else + { + throw new IOException("HttpResponse (" + responseCode + ") - " + + errMessage); + } } log.debug("Content-Length: " + httpDownload.getContentLength()); @@ -218,6 +225,14 @@ public class GMSClient return webUsers; } + + /** + * Create a new HTTPDownload instance. Testers can override as needed. + * + * @param webUsers The User objects. + * @return HttpDownload instance. Never null. + * @throws IOException Any writing/reading errors. + */ HttpDownload createDisplayUsersHTTPDownload( final List<User<HttpPrincipal>> webUsers) throws IOException { @@ -233,13 +248,13 @@ public class GMSClient * @return The newly created group will all the information. * @throws GroupAlreadyExistsException If a group with the same name already * exists. - * @throws AccessControlException If unauthorized to perform this operation. + * @throws AccessControlException If unauthorized to perform this operation. * @throws UserNotFoundException * @throws IOException */ public Group createGroup(Group group) - throws GroupAlreadyExistsException, AccessControlException, - UserNotFoundException, IOException + throws GroupAlreadyExistsException, AccessControlException, + UserNotFoundException, IOException { URL createGroupURL = new URL(this.baseURL + "/groups"); log.debug("createGroupURL request to " + createGroupURL.toString()); @@ -310,7 +325,7 @@ public class GMSClient * @throws java.io.IOException */ public Group getGroup(String groupName) - throws GroupNotFoundException, AccessControlException, IOException + throws GroupNotFoundException, AccessControlException, IOException { URL getGroupURL = new URL(this.baseURL + "/groups/" + groupName); log.debug("getGroup request to " + getGroupURL.toString()); @@ -323,8 +338,7 @@ public class GMSClient Throwable error = transfer.getThrowable(); if (error != null) { - log.debug("getGroup throwable (" + transfer - .getResponseCode() + ")", error); + log.debug("getGroup throwable (" + transfer.getResponseCode() + ")", error); // transfer returns a -1 code for anonymous access. if ((transfer.getResponseCode() == -1) || (transfer.getResponseCode() == 401) || @@ -365,7 +379,7 @@ public class GMSClient * @throws java.io.IOException */ public List<String> getGroupNames() - throws AccessControlException, IOException + throws AccessControlException, IOException { final URL getGroupNamesURL = new URL(this.baseURL + "/groups"); log.debug("getGroupNames request to " + getGroupNamesURL.toString()); @@ -373,28 +387,26 @@ public class GMSClient final List<String> groupNames = new ArrayList<String>(); final HttpDownload httpDownload = new HttpDownload(getGroupNamesURL, new InputStreamWrapper() + { + @Override + public void read(final InputStream inputStream) throws IOException + { + try { - @Override - public void read(final InputStream inputStream) throws - IOException - { - try - { - InputStreamReader inReader = new InputStreamReader(inputStream); - BufferedReader reader = new BufferedReader(inReader); - String line; - while ((line = reader.readLine()) != null) - { - groupNames.add(line); - } - } - catch (Exception bug) - { - log.error("Unexpected exception", bug); - throw new RuntimeException(bug); - } + InputStreamReader inReader = new InputStreamReader(inputStream); + BufferedReader reader = new BufferedReader(inReader); + String line; + while ((line = reader.readLine()) != null) { + groupNames.add(line); } - }); + } + catch (Exception bug) + { + log.error("Unexpected exception", bug); + throw new RuntimeException(bug); + } + } + }); httpDownload.setSSLSocketFactory(getSSLSocketFactory()); httpDownload.run(); @@ -410,7 +422,7 @@ public class GMSClient errMessage); if ((responseCode == 401) || (responseCode == 403) || - (responseCode == -1)) + (responseCode == -1)) { throw new AccessControlException(errMessage); } @@ -433,15 +445,14 @@ public class GMSClient * @param group The update group object. * @return The group after update. * @throws IllegalArgumentException If cyclical membership is detected. - * @throws GroupNotFoundException If the group was not found. - * @throws UserNotFoundException If a member was not found. - * @throws AccessControlException If unauthorized to perform this operation. + * @throws GroupNotFoundException If the group was not found. + * @throws UserNotFoundException If a member was not found. + * @throws AccessControlException If unauthorized to perform this operation. * @throws java.io.IOException */ public Group updateGroup(Group group) - throws IllegalArgumentException, GroupNotFoundException, - UserNotFoundException, - AccessControlException, IOException + throws IllegalArgumentException, GroupNotFoundException, UserNotFoundException, + AccessControlException, IOException { URL updateGroupURL = new URL(this.baseURL + "/groups/" + group.getID()); log.debug("updateGroup request to " + updateGroupURL.toString()); @@ -457,8 +468,10 @@ public class GMSClient HttpPost transfer = new HttpPost(updateGroupURL, groupXML.toString(), "application/xml", true); transfer.setSSLSocketFactory(getSSLSocketFactory()); + transfer.setTransferListener(this); transfer.run(); + Throwable error = transfer.getThrowable(); if (error != null) { @@ -475,15 +488,10 @@ public class GMSClient } if (transfer.getResponseCode() == 404) { - if (error.getMessage() != null && error.getMessage() - .toLowerCase().contains("user")) - { + if (error.getMessage() != null && error.getMessage().toLowerCase().contains("user")) throw new UserNotFoundException(error.getMessage()); - } else - { throw new GroupNotFoundException(error.getMessage()); - } } throw new IOException(error); } @@ -511,7 +519,7 @@ public class GMSClient * @throws java.io.IOException */ public void deleteGroup(String groupName) - throws GroupNotFoundException, AccessControlException, IOException + throws GroupNotFoundException, AccessControlException, IOException { URL deleteGroupURL = new URL(this.baseURL + "/groups/" + groupName); log.debug("deleteGroup request to " + deleteGroupURL.toString()); @@ -536,7 +544,7 @@ public class GMSClient { responseCode = conn.getResponseCode(); } - catch (Exception e) + catch(Exception e) { throw new AccessControlException(e.getMessage()); } @@ -548,7 +556,7 @@ public class GMSClient errMessage); if ((responseCode == 401) || (responseCode == 403) || - (responseCode == -1)) + (responseCode == -1)) { throw new AccessControlException(errMessage); } @@ -570,13 +578,13 @@ public class GMSClient * @param targetGroupName The group in which to add the group member. * @param groupMemberName The group member to add. * @throws IllegalArgumentException If cyclical membership is detected. - * @throws GroupNotFoundException If the group was not found. - * @throws AccessControlException If unauthorized to perform this operation. + * @throws GroupNotFoundException If the group was not found. + * @throws AccessControlException If unauthorized to perform this operation. * @throws java.io.IOException */ public void addGroupMember(String targetGroupName, String groupMemberName) - throws IllegalArgumentException, GroupNotFoundException, - AccessControlException, IOException + throws IllegalArgumentException, GroupNotFoundException, + AccessControlException, IOException { URL addGroupMemberURL = new URL(this.baseURL + "/groups/" + targetGroupName + "/groupMembers/" + @@ -620,15 +628,14 @@ public class GMSClient * Add a user as a member of a group. * * @param targetGroupName The group in which to add the group member. - * @param userID The user to add. + * @param userID The user to add. * @throws GroupNotFoundException If the group was not found. - * @throws UserNotFoundException If the member was not found. + * @throws UserNotFoundException If the member was not found. * @throws java.io.IOException * @throws AccessControlException If unauthorized to perform this operation. */ public void addUserMember(String targetGroupName, Principal userID) - throws GroupNotFoundException, UserNotFoundException, - AccessControlException, IOException + throws GroupNotFoundException, UserNotFoundException, AccessControlException, IOException { String userIDType = AuthenticationUtil.getPrincipalType(userID); String encodedUserID = URLEncoder.encode(userID.getName(), "UTF-8"); @@ -665,15 +672,10 @@ public class GMSClient } if (responseCode == 404) { - if (errMessage != null && errMessage.toLowerCase() - .contains("user")) - { + if (errMessage != null && errMessage.toLowerCase().contains("user")) throw new UserNotFoundException(errMessage); - } else - { throw new GroupNotFoundException(errMessage); - } } throw new IOException(errMessage); } @@ -690,7 +692,7 @@ public class GMSClient */ public void removeGroupMember(String targetGroupName, String groupMemberName) - throws GroupNotFoundException, AccessControlException, IOException + throws GroupNotFoundException, AccessControlException, IOException { URL removeGroupMemberURL = new URL(this.baseURL + "/groups/" + targetGroupName + "/groupMembers/" + @@ -712,15 +714,13 @@ public class GMSClient .setSSLSocketFactory(getSSLSocketFactory()); } - // Try to handle anonymous access and throw AccessControlException + // Try to handle anonymous access and throw AccessControlException int responseCode = -1; try { responseCode = conn.getResponseCode(); } - catch (Exception ignore) - { - } + catch (Exception ignore) {} if (responseCode != 200) { @@ -750,15 +750,14 @@ public class GMSClient * Remove a user as a member of a group. * * @param targetGroupName The group from which to remove the group member. - * @param userID The user to remove. + * @param userID The user to remove. * @throws GroupNotFoundException If the group was not found. - * @throws UserNotFoundException If the member was not found. + * @throws UserNotFoundException If the member was not found. * @throws java.io.IOException * @throws AccessControlException If unauthorized to perform this operation. */ public void removeUserMember(String targetGroupName, Principal userID) - throws GroupNotFoundException, UserNotFoundException, - AccessControlException, IOException + throws GroupNotFoundException, UserNotFoundException, AccessControlException, IOException { String userIDType = AuthenticationUtil.getPrincipalType(userID); String encodedUserID = URLEncoder.encode(userID.toString(), "UTF-8"); @@ -784,15 +783,13 @@ public class GMSClient .setSSLSocketFactory(getSSLSocketFactory()); } - // Try to handle anonymous access and throw AccessControlException + // Try to handle anonymous access and throw AccessControlException int responseCode = -1; try { responseCode = conn.getResponseCode(); } - catch (Exception ignore) - { - } + catch (Exception ignore) {} if (responseCode != 200) { @@ -812,15 +809,10 @@ public class GMSClient } if (responseCode == 404) { - if (errMessage != null && errMessage.toLowerCase() - .contains("user")) - { + if (errMessage != null && errMessage.toLowerCase().contains("user")) throw new UserNotFoundException(errMessage); - } else - { throw new GroupNotFoundException(errMessage); - } } throw new IOException(errMessage); } @@ -830,22 +822,22 @@ public class GMSClient * Get all the memberships of the user of a certain role. * * @param userID Identifies the user. - * @param role The role to look up. + * @param role The role to look up. * @return A list of groups for which the user has the role. - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public List<Group> getMemberships(Principal userID, Role role) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { if (userID == null || role == null) { throw new IllegalArgumentException("userID and role are required."); } - List<Group> cachedGroups = getCachedGroups(userID, role); + List<Group> cachedGroups = getCachedGroups(userID, role, true); if (cachedGroups != null) { return cachedGroups; @@ -914,19 +906,19 @@ public class GMSClient * Return the group, specified by paramter groupName, if the user, * identified by userID, is a member of that group. Return null * otherwise. - * <p/> + * * This call is identical to getMemberShip(userID, groupName, Role.MEMBER) * - * @param userID Identifies the user. + * @param userID Identifies the user. * @param groupName Identifies the group. * @return The group or null of the user is not a member. - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public Group getMembership(Principal userID, String groupName) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { return getMembership(userID, groupName, Role.MEMBER); } @@ -936,35 +928,27 @@ public class GMSClient * identified by userID, is a member (of type role) of that group. * Return null otherwise. * - * @param userID Identifies the user. + * @param userID Identifies the user. * @param groupName Identifies the group. - * @param role The membership role to search. + * @param role The membership role to search. * @return The group or null of the user is not a member. - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public Group getMembership(Principal userID, String groupName, Role role) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { if (userID == null || groupName == null || role == null) { throw new IllegalArgumentException("userID and role are required."); } - List<Group> cachedGroups = getCachedGroups(userID, role); - if (cachedGroups != null) + Group cachedGroup = getCachedGroup(userID, groupName, role); + if (cachedGroup != null) { - int index = cachedGroups.indexOf(new Group(groupName)); - if (index != -1) - { - return cachedGroups.get(index); - } - else - { - return null; - } + return cachedGroup; } String idType = AuthenticationUtil.getPrincipalType(userID); @@ -1024,9 +1008,9 @@ public class GMSClient } if (groups.size() == 1) { - // don't cache these results as it is not a complete - // list of memberships--it only applies to one group. - return groups.get(0); + Group ret = groups.get(0); + addCachedGroup(userID, ret, role); + return ret; } throw new IllegalStateException( "Duplicate membership for " + id + " in group " + groupName); @@ -1040,19 +1024,19 @@ public class GMSClient /** * Check if userID is a member of groupName. - * <p/> + * * This is equivalent to isMember(userID, groupName, Role.MEMBER) * - * @param userID Identifies the user. + * @param userID Identifies the user. * @param groupName Identifies the group. * @return True if the user is a member of the group - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public boolean isMember(Principal userID, String groupName) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { return isMember(userID, groupName, Role.MEMBER); } @@ -1060,17 +1044,17 @@ public class GMSClient /** * Check if userID is a member (of type role) of groupName. * - * @param userID Identifies the user. + * @param userID Identifies the user. * @param groupName Identifies the group. - * @param role The type of membership. + * @param role The type of membership. * @return True if the user is a member of the group - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public boolean isMember(Principal userID, String groupName, Role role) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { Group group = getMembership(userID, groupName, role); return group != null; @@ -1082,16 +1066,13 @@ public class GMSClient public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) { if (mySocketFactory != null) - { throw new IllegalStateException("Illegal use of GMSClient: " - + "cannot set SSLSocketFactory after using one created from Subject"); - } + + "cannot set SSLSocketFactory after using one created from Subject"); this.sslSocketFactory = sslSocketFactory; clearCache(); } private int subjectHashCode = 0; - private SSLSocketFactory getSSLSocketFactory() { AccessControlContext ac = AccessController.getContext(); @@ -1114,12 +1095,9 @@ public class GMSClient { int c = s.hashCode(); if (c != subjectHashCode) - { throw new IllegalStateException("Illegal use of " - + this.getClass() - .getSimpleName() - + ": subject change not supported for internal SSLSocketFactory"); - } + + this.getClass().getSimpleName() + + ": subject change not supported for internal SSLSocketFactory"); } return this.mySocketFactory; } @@ -1128,15 +1106,13 @@ public class GMSClient { AccessControlContext acContext = AccessController.getContext(); Subject subject = Subject.getSubject(acContext); - if (subject != null) { - log.debug("Clearing cache"); - subject.getPrivateCredentials().clear(); + subject.getPrivateCredentials().remove(new GroupMemberships()); } } - protected List<Group> getCachedGroups(Principal userID, Role role) + protected GroupMemberships getGroupCache(Principal userID) { AccessControlContext acContext = AccessController.getContext(); Subject subject = Subject.getSubject(acContext); @@ -1144,46 +1120,81 @@ public class GMSClient // only consult cache if the userID is of the calling subject if (userIsSubject(userID, subject)) { - Set groupCredentialSet = subject - .getPrivateCredentials(GroupMemberships.class); - if ((groupCredentialSet != null) && - (groupCredentialSet.size() == 1)) + Set<GroupMemberships> gset = subject.getPrivateCredentials(GroupMemberships.class); + if (gset == null || gset.isEmpty()) { - Iterator i = groupCredentialSet.iterator(); - GroupMemberships groupMemberships = ((GroupMemberships) i - .next()); - return groupMemberships.memberships.get(role); + GroupMemberships mems = new GroupMemberships(); + subject.getPrivateCredentials().add(mems); + return mems; } + GroupMemberships mems = gset.iterator().next(); + return mems; + } + return null; // no cache + } + + protected Group getCachedGroup(Principal userID, String groupID, Role role) + { + List<Group> groups = getCachedGroups(userID, role, false); + if (groups == null) + return null; // no cache + for (Group g : groups) + { + if (g.getID().equals(groupID)) + return g; } return null; } + protected List<Group> getCachedGroups(Principal userID, Role role, boolean complete) + { + GroupMemberships mems = getGroupCache(userID); + if (mems == null) + return null; // no cache - protected void setCachedGroups(Principal userID, List<Group> groups, Role role) + Boolean cacheState = mems.complete.get(role); + if (!complete || Boolean.TRUE.equals(cacheState)) + return mems.memberships.get(role); + + // caller wanted complete and we don't have that + return null; + } + + protected void addCachedGroup(Principal userID, Group group, Role role) { - AccessControlContext acContext = AccessController.getContext(); - Subject subject = Subject.getSubject(acContext); + GroupMemberships mems = getGroupCache(userID); + if (mems == null) + return; // no cache - // only save to cache if the userID is of the calling subject - if (userIsSubject(userID, subject)) + List<Group> groups = mems.memberships.get(role); + if (groups == null) { - log.debug("Caching groups for " + userID + ", role " + role); + groups = new ArrayList<Group>(); + mems.complete.put(role, Boolean.FALSE); + mems.memberships.put(role, groups); + } + if (!groups.contains(group)) + groups.add(group); + } - final GroupMemberships groupCredentials; - Set groupCredentialSet = subject - .getPrivateCredentials(GroupMemberships.class); - if ((groupCredentialSet != null) && - (groupCredentialSet.size() == 1)) - { - Iterator i = groupCredentialSet.iterator(); - groupCredentials = ((GroupMemberships) i.next()); - } - else - { - groupCredentials = new GroupMemberships(); - subject.getPrivateCredentials().add(groupCredentials); - } + protected void setCachedGroups(Principal userID, List<Group> groups, Role role) + { + GroupMemberships mems = getGroupCache(userID); + if (mems == null) + return; // no cache - groupCredentials.memberships.put(role, groups); + log.debug("Caching groups for " + userID + ", role " + role); + List<Group> cur = mems.memberships.get(role); + if (cur == null) + { + cur = new ArrayList<Group>(); + mems.complete.put(role, Boolean.FALSE); + mems.memberships.put(role, cur); + } + for (Group group : groups) + { + if (!cur.contains(group)) + cur.add(group); + mems.complete.put(role, Boolean.TRUE); } } @@ -1196,7 +1207,7 @@ public class GMSClient for (Principal subjectPrincipal : subject.getPrincipals()) { - if (subjectPrincipal.equals(userID)) + if (AuthenticationUtil.equals(subjectPrincipal, userID)) { return true; } @@ -1205,17 +1216,31 @@ public class GMSClient } /** - * Class used to hold list of groups in which - * a user is a member. + * Class used to hold list of groups in which a user is known to be a member. */ - protected class GroupMemberships + protected class GroupMemberships implements Comparable { Map<Role, List<Group>> memberships = new HashMap<Role, List<Group>>(); + Map<Role, Boolean> complete = new HashMap<Role, Boolean>(); protected GroupMemberships() { } + // only allow one in a set - makes clearCache simple too + public boolean equals(Object rhs) + { + if (rhs != null && rhs instanceof GroupMemberships) + return true; + return false; + } + + public int compareTo(Object t) + { + if (this.equals(t)) + return 0; + return -1; // wonder if this is sketchy + } } } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClientMain.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClientMain.java new file mode 100644 index 0000000000000000000000000000000000000000..e4a1fa6d6561729b0e67bfcffe8b46d894c9d4a2 --- /dev/null +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClientMain.java @@ -0,0 +1,276 @@ +/* + ************************************************************************ + ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* + ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** + * + * (c) 2014. (c) 2014. + * Government of Canada Gouvernement du Canada + * National Research Council Conseil national de recherches + * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 + * All rights reserved Tous droits réservés + * + * NRC disclaims any warranties, Le CNRC dénie toute garantie + * expressed, implied, or énoncée, implicite ou légale, + * statutory, of any kind with de quelque nature que ce + * respect to the software, soit, concernant le logiciel, + * including without limitation y compris sans restriction + * any warranty of merchantability toute garantie de valeur + * or fitness for a particular marchande ou de pertinence + * purpose. NRC shall not be pour un usage particulier. + * liable in any event for any Le CNRC ne pourra en aucun cas + * damages, whether direct or être tenu responsable de tout + * indirect, special or general, dommage, direct ou indirect, + * consequential or incidental, particulier ou général, + * arising from the use of the accessoire ou fortuit, résultant + * software. Neither the name de l'utilisation du logiciel. Ni + * of the National Research le nom du Conseil National de + * Council of Canada nor the Recherches du Canada ni les noms + * names of its contributors may de ses participants ne peuvent + * be used to endorse or promote être utilisés pour approuver ou + * products derived from this promouvoir les produits dérivés + * software without specific prior de ce logiciel sans autorisation + * written permission. préalable et particulière + * par écrit. + * + * This file is part of the Ce fichier fait partie du projet + * OpenCADC project. OpenCADC. + * + * OpenCADC is free software: OpenCADC est un logiciel libre ; + * you can redistribute it and/or vous pouvez le redistribuer ou le + * modify it under the terms of modifier suivant les termes de + * the GNU Affero General Public la “GNU Affero General Public + * License as published by the License” telle que publiée + * Free Software Foundation, par la Free Software Foundation + * either version 3 of the : soit la version 3 de cette + * License, or (at your option) licence, soit (à votre gré) + * any later version. toute version ultérieure. + * + * OpenCADC is distributed in the OpenCADC est distribué + * hope that it will be useful, dans l’espoir qu’il vous + * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE + * without even the implied GARANTIE : sans même la garantie + * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ + * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF + * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence + * General Public License for Générale Publique GNU Affero + * more details. pour plus de détails. + * + * You should have received Vous devriez avoir reçu une + * a copy of the GNU Affero copie de la Licence Générale + * General Public License along Publique GNU Affero avec + * with OpenCADC. If not, see OpenCADC ; si ce n’est + * <http://www.gnu.org/licenses/>. pas le cas, consultez : + * <http://www.gnu.org/licenses/>. + * + * $Revision: 4 $ + * + ************************************************************************ + */ + +package ca.nrc.cadc.ac.client; + +import ca.nrc.cadc.ac.Group; +import ca.nrc.cadc.ac.User; +import java.net.URI; +import java.net.URL; +import java.security.PrivilegedAction; + +import javax.security.auth.Subject; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + +import ca.nrc.cadc.auth.CertCmdArgUtil; +import ca.nrc.cadc.auth.HttpPrincipal; +import ca.nrc.cadc.reg.client.RegistryClient; +import ca.nrc.cadc.util.ArgumentMap; +import ca.nrc.cadc.util.Log4jInit; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.util.Set; +import javax.security.auth.x500.X500Principal; + +/** + * Prototype main class for the GMSClient. Currently + * only used for testing. Should not be used for production + * work. + */ +public class GMSClientMain implements PrivilegedAction<Object> +{ + + private static Logger log = Logger.getLogger(GMSClientMain.class); + + public static final String ARG_ADD_MEMBER = "add-member"; + public static final String ARG_CREATE_GROUP = "create"; + public static final String ARG_GET_GROUP = "get"; + public static final String ARG_DELETE_GROUP = "delete"; + + public static final String ARG_USERID = "userid"; + public static final String ARG_GROUP = "group"; + + public static final String ARG_HELP = "help"; + public static final String ARG_VERBOSE = "verbose"; + public static final String ARG_DEBUG = "debug"; + public static final String ARG_H = "h"; + public static final String ARG_V = "v"; + public static final String ARG_D = "d"; + + private GMSClient client; + private ArgumentMap argMap; + + private GMSClientMain() + { + RegistryClient regClient = new RegistryClient(); + URL acURL = null; + try + { + acURL = regClient.getServiceURL(new URI("ivo://cadc.nrc.ca/canfargms"), "https"); + } + catch (Exception e) + { + log.error("FAIL", e); + } + log.info("GMS service URL: " + acURL); + client = new GMSClient(acURL.toString()); + } + + public static void main(String[] args) + { + ArgumentMap argMap = new ArgumentMap(args); + + if (argMap.isSet(ARG_HELP) || argMap.isSet(ARG_H)) + { + usage(); + System.exit(0); + } + + // Set debug mode + if (argMap.isSet(ARG_DEBUG) || argMap.isSet(ARG_D)) + { + Log4jInit.setLevel("ca.nrc.cadc.ac.client", Level.DEBUG); + Log4jInit.setLevel("ca.nrc.cadc.net", Level.DEBUG); + } + else if (argMap.isSet(ARG_VERBOSE) || argMap.isSet(ARG_V)) + { + Log4jInit.setLevel("ca.nrc.cadc.ac.client", Level.INFO); + } + else + Log4jInit.setLevel("ca", Level.WARN); + + GMSClientMain main = new GMSClientMain(); + main.argMap = argMap; + + Subject subject = CertCmdArgUtil.initSubject(argMap, true); + + Object response = null; + + if (subject != null) + response = Subject.doAs(subject, main); + else + response = main.run(); + + log.debug("Response: " + response); + } + + private String getCommand() + { + if (argMap.isSet(ARG_ADD_MEMBER)) + return ARG_ADD_MEMBER; + + if (argMap.isSet(ARG_CREATE_GROUP)) + return ARG_CREATE_GROUP; + + if (argMap.isSet(ARG_GET_GROUP)) + return ARG_GET_GROUP; + + if (argMap.isSet(ARG_DELETE_GROUP)) + return ARG_DELETE_GROUP; + + throw new IllegalArgumentException("No valid commands"); + } + + private static void usage() + { + System.out.println("--add-member --group=<g> --userid=<u>"); + System.out.println("--create --group=<g>"); + System.out.println("--get --group=<g>"); + System.out.println("--delete --group=<g>"); + } + + @Override + public Object run() + { + try + { + String command = getCommand(); + + if (command.equals(ARG_ADD_MEMBER)) + { + String group = argMap.getValue(ARG_GROUP); + String userID = argMap.getValue(ARG_USERID); + + if (group == null) + throw new IllegalArgumentException("No group specified"); + + if (userID == null) + throw new IllegalArgumentException("No userid specified"); + + client.addUserMember(group, new HttpPrincipal(userID)); + } + else if (command.equals(ARG_CREATE_GROUP)) + { + String group = argMap.getValue(ARG_GROUP); + if (group == null) + throw new IllegalArgumentException("No group specified"); + + AccessControlContext accessControlContext = AccessController.getContext(); + Subject subject = Subject.getSubject(accessControlContext); + Set<X500Principal> principals = subject.getPrincipals(X500Principal.class); + X500Principal p = principals.iterator().next(); + + Group g = new Group(group, new User(p)); + g.getUserMembers().add(g.getOwner()); + client.createGroup(g); + } + else if (command.equals(ARG_GET_GROUP)) + { + String group = argMap.getValue(ARG_GROUP); + if (group == null) + throw new IllegalArgumentException("No group specified"); + + Group g = client.getGroup(group); + System.out.println("found: " + g.getID()); + System.out.println("\t" + g.description); + System.out.println("owner: " + g.getOwner()); + + for (User u : g.getUserAdmins()) + System.out.println("admin: " + u); + + for (Group ga : g.getGroupAdmins()) + System.out.println("admin: " + ga); + + for (User u : g.getUserMembers()) + System.out.println("member: " + u); + + for (Group gm : g.getGroupMembers()) + System.out.println("member: " + gm); + + } + else if (command.equals(ARG_DELETE_GROUP)) + { + String group = argMap.getValue(ARG_GROUP); + if (group == null) + throw new IllegalArgumentException("No group specified"); + + client.deleteGroup(group); + } + + return null; + } + catch (Throwable t) + { + log.error("ERROR", t); + return t; + } + } +} diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/UserClient.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/UserClient.java index c108f030c4fb867b52b98649648e60c99f5d2243..a22a7519f360b2598a9728110b0163e6dfab2f3c 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/UserClient.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/UserClient.java @@ -264,7 +264,7 @@ public class UserClient } else if (principal instanceof NumericPrincipal) { - idTypeStr = IdentityType.UID.getValue(); + idTypeStr = IdentityType.CADC.getValue(); } else if (principal instanceof CookiePrincipal) { diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupReader.java index a240184be0794193897d8924ce1002747b5ecb4c..e5997162863ce13b0f560bb579d4b7e492d8b851 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupReader.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupReader.java @@ -76,37 +76,15 @@ import org.jdom2.Document; import org.json.JSONException; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.io.Reader; import java.util.Scanner; +/** + * Class to read an JSON representation of a list of Groups + * into a list of Group objects. + */ public class JsonGroupReader extends GroupReader { - /** - * Construct a Group from a InputStream. - * - * @param in InputStream. - * @return Group Group. - * @throws ReaderException - * @throws IOException - */ - @Override - public Group read(InputStream in) - throws ReaderException, IOException - { - if (in == null) - { - throw new IOException("stream closed"); - } - InputStreamReader reader; - - Scanner s = new Scanner(in).useDelimiter("\\A"); - String json = s.hasNext() ? s.next() : ""; - - return read(json); - } - /** * Construct a Group from a Reader. * @@ -127,27 +105,6 @@ public class JsonGroupReader extends GroupReader Scanner s = new Scanner(reader).useDelimiter("\\A"); String json = s.hasNext() ? s.next() : ""; - return read(json); - } - - /** - * Construct a Group from an JSON String source. - * - * @param json String of JSON. - * @return Group Group. - * @throws ReaderException - * @throws IOException - */ - @Override - public Group read(String json) - throws ReaderException, IOException - { - if (json == null) - { - throw new IllegalArgumentException("JSON must not be null"); - } - - // Create a JSONObject from the JSON try { JsonInputter jsonInputter = new JsonInputter(); @@ -160,12 +117,12 @@ public class JsonGroupReader extends GroupReader jsonInputter.getListElementMap().put("userAdmins", "user"); Document document = jsonInputter.input(json); - return GroupReader.parseGroup(document.getRootElement()); + return getGroup(document.getRootElement()); } catch (JSONException e) { String error = "Unable to parse JSON to Group because " + - e.getMessage(); + e.getMessage(); throw new ReaderException(error, e); } } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupWriter.java index 2db229327ce52306744846b95f0a4ffb19f310ce..864e14df31befef998f29f0cfaafaca5ecc4216b 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupWriter.java @@ -71,57 +71,18 @@ package ca.nrc.cadc.ac.json; import ca.nrc.cadc.ac.Group; import ca.nrc.cadc.ac.WriterException; import ca.nrc.cadc.ac.xml.GroupWriter; -import ca.nrc.cadc.util.StringBuilderWriter; import ca.nrc.cadc.xml.JsonOutputter; import org.jdom2.Document; import org.jdom2.Element; -import java.io.BufferedWriter; import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; import java.io.Writer; +/** + * Class to write a JSON representation of a Group object. + */ public class JsonGroupWriter extends GroupWriter { - /** - * Write a Group to a StringBuilder. - * @param group - * @param builder - * @throws IOException - * @throws WriterException - */ - public void write(Group group, StringBuilder builder) - throws IOException, WriterException - { - write(group, new StringBuilderWriter(builder)); - } - - /** - * Write a Group to an OutputStream. - * - * @param group Group to write. - * @param out OutputStream to write to. - * @throws IOException if the writer fails to write. - * @throws WriterException - */ - @Override - public void write(Group group, OutputStream out) - throws IOException, WriterException - { - OutputStreamWriter outWriter; - try - { - outWriter = new OutputStreamWriter(out, "UTF-8"); - } - catch (UnsupportedEncodingException e) - { - throw new RuntimeException("UTF-8 encoding not supported", e); - } - write(group, new BufferedWriter(outWriter)); - } - /** * Write a Group to a Writer. * @@ -139,7 +100,7 @@ public class JsonGroupWriter extends GroupWriter throw new WriterException("null group"); } - Element children = GroupWriter.getGroupElement(group); + Element children = getElement(group); Element groupElement = new Element("group"); groupElement.addContent(children); Document document = new Document(); diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListReader.java new file mode 100644 index 0000000000000000000000000000000000000000..1e63adedabde2c2c9a704f5b05dc5c2c1f791a2f --- /dev/null +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListReader.java @@ -0,0 +1,127 @@ +/* + ************************************************************************ + ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* + ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** + * + * (c) 2015. (c) 2015. + * Government of Canada Gouvernement du Canada + * National Research Council Conseil national de recherches + * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 + * All rights reserved Tous droits réservés + * + * NRC disclaims any warranties, Le CNRC dénie toute garantie + * expressed, implied, or énoncée, implicite ou légale, + * statutory, of any kind with de quelque nature que ce + * respect to the software, soit, concernant le logiciel, + * including without limitation y compris sans restriction + * any warranty of merchantability toute garantie de valeur + * or fitness for a particular marchande ou de pertinence + * purpose. NRC shall not be pour un usage particulier. + * liable in any event for any Le CNRC ne pourra en aucun cas + * damages, whether direct or être tenu responsable de tout + * indirect, special or general, dommage, direct ou indirect, + * consequential or incidental, particulier ou général, + * arising from the use of the accessoire ou fortuit, résultant + * software. Neither the name de l'utilisation du logiciel. Ni + * of the National Research le nom du Conseil National de + * Council of Canada nor the Recherches du Canada ni les noms + * names of its contributors may de ses participants ne peuvent + * be used to endorse or promote être utilisés pour approuver ou + * products derived from this promouvoir les produits dérivés + * software without specific prior de ce logiciel sans autorisation + * written permission. préalable et particulière + * par écrit. + * + * This file is part of the Ce fichier fait partie du projet + * OpenCADC project. OpenCADC. + * + * OpenCADC is free software: OpenCADC est un logiciel libre ; + * you can redistribute it and/or vous pouvez le redistribuer ou le + * modify it under the terms of modifier suivant les termes de + * the GNU Affero General Public la “GNU Affero General Public + * License as published by the License” telle que publiée + * Free Software Foundation, par la Free Software Foundation + * either version 3 of the : soit la version 3 de cette + * License, or (at your option) licence, soit (à votre gré) + * any later version. toute version ultérieure. + * + * OpenCADC is distributed in the OpenCADC est distribué + * hope that it will be useful, dans l’espoir qu’il vous + * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE + * without even the implied GARANTIE : sans même la garantie + * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ + * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF + * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence + * General Public License for Générale Publique GNU Affero + * more details. pour plus de détails. + * + * You should have received Vous devriez avoir reçu une + * a copy of the GNU Affero copie de la Licence Générale + * General Public License along Publique GNU Affero avec + * with OpenCADC. If not, see OpenCADC ; si ce n’est + * <http://www.gnu.org/licenses/>. pas le cas, consultez : + * <http://www.gnu.org/licenses/>. + * + * + ************************************************************************ + */ + +package ca.nrc.cadc.ac.json; + +import ca.nrc.cadc.ac.ReaderException; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.xml.UserListReader; +import ca.nrc.cadc.xml.JsonInputter; +import org.jdom2.Document; +import org.json.JSONException; + +import java.io.Reader; +import java.net.URISyntaxException; +import java.security.Principal; +import java.util.List; +import java.util.Scanner; + +/** + * Class to read an JSON representation of a Collection of Users + * into a list of User objects. + */ +public class JsonUserListReader extends UserListReader +{ + /** + * Construct a list of Users from a Reader. + * + * @param reader Reader. + * @return users List of Users. + * @throws ReaderException + * @throws URISyntaxException + */ + @Override + public List<User<Principal>> read(Reader reader) + throws URISyntaxException, ReaderException + { + if (reader == null) + { + throw new IllegalArgumentException("reader must not be null"); + } + + Scanner s = new Scanner(reader).useDelimiter("\\A"); + String json = s.hasNext() ? s.next() : ""; + + try + { + JsonInputter jsonInputter = new JsonInputter(); + jsonInputter.getListElementMap().put("identities", "identity"); + jsonInputter.getListElementMap().put("details", "userDetails"); + + Document document = jsonInputter.input(json); + return getUserList(document.getRootElement()); + } + catch (JSONException e) + { + String error = "Unable to parse JSON to list of Users because " + + e.getMessage(); + throw new ReaderException(error, e); + } + } + +} diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListWriter.java index 5bf9317a44b6c30c61b50f569471f652dc875daa..9a8acad66057c4c6c16e8ab52c5baf29de49f175 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListWriter.java @@ -68,58 +68,54 @@ package ca.nrc.cadc.ac.json; -import ca.nrc.cadc.ac.PersonalDetails; -import org.json.JSONException; -import org.json.JSONWriter; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.WriterException; +import ca.nrc.cadc.ac.xml.UserListWriter; +import ca.nrc.cadc.xml.JsonOutputter; +import org.jdom2.Document; +import org.jdom2.Element; import java.io.IOException; import java.io.Writer; -import java.util.Map; - +import java.security.Principal; +import java.util.Collection; /** * Class to write out, as JSON, a list of user entries. */ -public class JsonUserListWriter +public class JsonUserListWriter extends UserListWriter { - public static void write(final Map<String, PersonalDetails> users, - final Writer writer) throws IOException + /** + * Write a Collection of Users to a Writer. + * + * @param users Users to write. + * @param writer Writer to write to. + * @throws IOException if the writer fails to write. + * @throws WriterException + */ + @Override + public <T extends Principal> void write(Collection<User<T>> users, Writer writer) + throws IOException, WriterException { - final JSONWriter jsonWriter = new JSONWriter(writer); - - try + if (users == null) { - jsonWriter.array(); - - for (final Map.Entry<String, PersonalDetails> entry - : users.entrySet()) - { - jsonWriter.object(); - - jsonWriter.key("id").value(entry.getKey()); - jsonWriter.key("firstName").value(entry.getValue(). - getFirstName()); - jsonWriter.key("lastName").value(entry.getValue(). - getLastName()); - - jsonWriter.endObject(); - writer.write("\n"); - } + throw new WriterException("null users"); } - catch (JSONException e) - { - throw new IOException(e); - } - finally + + Element usersElement = new Element("users"); + for (User<? extends Principal> user : users) { - try - { - jsonWriter.endArray(); - } - catch (JSONException e) - { - // Do nothing. - } + Element userElement = new Element(("user")); + userElement.addContent(getElement(user)); } + Document document = new Document(); + document.setRootElement(usersElement); + + JsonOutputter jsonOutputter = new JsonOutputter(); + jsonOutputter.getListElementNames().add("identities"); + jsonOutputter.getListElementNames().add("details"); + + jsonOutputter.output(document, writer); } + } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserReader.java index 2c4a8e376ec338c5fa2157d08767ca79f8999d5d..4ef9ee9afa59ff7ea34631b2f569fcc5c718644f 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserReader.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserReader.java @@ -76,36 +76,15 @@ import org.jdom2.Document; import org.json.JSONException; import java.io.IOException; -import java.io.InputStream; import java.io.Reader; import java.security.Principal; import java.util.Scanner; +/** + * Class to read a JSON representation of a User to a User object. + */ public class JsonUserReader extends UserReader { - /** - * Construct a User from a InputStream. - * - * @param in InputStream. - * @return User User. - * @throws ReaderException - * @throws IOException - */ - @Override - public User<Principal> read(InputStream in) - throws IOException - { - if (in == null) - { - throw new IOException("stream closed"); - } - - Scanner s = new Scanner(in).useDelimiter("\\A"); - String json = s.hasNext() ? s.next() : ""; - - return read(json); - } - /** * Construct a User from a Reader. * @@ -126,27 +105,6 @@ public class JsonUserReader extends UserReader Scanner s = new Scanner(reader).useDelimiter("\\A"); String json = s.hasNext() ? s.next() : ""; - return read(json); - } - - /** - * Construct a User from an JSON String source. - * - * @param json String of JSON. - * @return User User. - * @throws ReaderException - * @throws IOException - */ - @Override - public User<Principal> read(String json) - throws IOException - { - if (json == null || json.isEmpty()) - { - throw new IllegalArgumentException("JSON must not be null or empty"); - } - - // Create a JSONObject from the JSON try { JsonInputter jsonInputter = new JsonInputter(); @@ -154,7 +112,7 @@ public class JsonUserReader extends UserReader jsonInputter.getListElementMap().put("details", "userDetails"); Document document = jsonInputter.input(json); - return UserReader.parseUser(document.getRootElement()); + return getUser(document.getRootElement()); } catch (JSONException e) { diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestReader.java index b8d61e8cea9567a2caac1beb562766a5e05b1bfa..eb03dbb01e2655358798886e44be0d896b867d9e 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestReader.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestReader.java @@ -76,36 +76,15 @@ import org.jdom2.Document; import org.json.JSONException; import java.io.IOException; -import java.io.InputStream; import java.io.Reader; import java.security.Principal; import java.util.Scanner; +/** + * Class to read a JSON representation of a UserRequest to a UserRequest object. + */ public class JsonUserRequestReader extends UserRequestReader { - /** - * Construct a User from a InputStream. - * - * @param in InputStream. - * @return User User. - * @throws ReaderException - * @throws IOException - */ - @Override - public UserRequest<Principal> read(InputStream in) - throws IOException - { - if (in == null) - { - throw new IOException("stream closed"); - } - - Scanner s = new Scanner(in).useDelimiter("\\A"); - String json = s.hasNext() ? s.next() : ""; - - return read(json); - } - /** * Construct a User from a Reader. * @@ -126,41 +105,20 @@ public class JsonUserRequestReader extends UserRequestReader Scanner s = new Scanner(reader).useDelimiter("\\A"); String json = s.hasNext() ? s.next() : ""; - return read(json); - } - - /** - * Construct a UserRequest from an JSON String source. - * - * @param json String of the JSON. - * @return UserRequest UserRequest. - * @throws IOException - */ - @Override - public UserRequest<Principal> read(String json) - throws IOException - { - if (json == null) + try { - throw new IllegalArgumentException("JSON must not be null"); + JsonInputter jsonInputter = new JsonInputter(); + jsonInputter.getListElementMap().put("identities", "identity"); + jsonInputter.getListElementMap().put("details", "userDetails"); + + Document document = jsonInputter.input(json); + return getUserRequest(document.getRootElement()); } - else + catch (JSONException e) { - try - { - JsonInputter jsonInputter = new JsonInputter(); - jsonInputter.getListElementMap().put("identities", "identity"); - jsonInputter.getListElementMap().put("details", "userDetails"); - - Document document = jsonInputter.input(json); - return UserRequestReader.parseUserRequest(document.getRootElement()); - } - catch (JSONException e) - { - String error = "Unable to parse JSON to User because " + - e.getMessage(); - throw new ReaderException(error, e); - } + String error = "Unable to parse JSON to User because " + + e.getMessage(); + throw new ReaderException(error, e); } } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestWriter.java index 7ebb07a652a9b972252d528e869ac86938d33c12..6cf6e6f742b009f319f25371126cbe777de768da 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestWriter.java @@ -1,9 +1,77 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2011. (c) 2011. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* <http://www.gnu.org/licenses/>. pas le cas, consultez : +* <http://www.gnu.org/licenses/>. +* +* $Revision: 5 $ +* +************************************************************************ +*/ + package ca.nrc.cadc.ac.json; import ca.nrc.cadc.ac.UserRequest; import ca.nrc.cadc.ac.WriterException; import ca.nrc.cadc.ac.xml.UserRequestWriter; -import ca.nrc.cadc.util.StringBuilderWriter; import ca.nrc.cadc.xml.JsonOutputter; import org.jdom2.Document; import org.jdom2.Element; @@ -17,20 +85,6 @@ import java.security.Principal; */ public class JsonUserRequestWriter extends UserRequestWriter { - /** - * Write a UserRequest to a StringBuilder. - * - * @param userRequest UserRequest to write. - * @param builder StringBuilder to write to. - * @throws java.io.IOException if the writer fails to write. - * @throws WriterException - */ - public void write(UserRequest<? extends Principal> userRequest, StringBuilder builder) - throws IOException, WriterException - { - write(userRequest, new StringBuilderWriter(builder)); - } - /** * Write a UserRequest to a Writer. * @@ -39,7 +93,8 @@ public class JsonUserRequestWriter extends UserRequestWriter * @throws IOException if the writer fails to write. * @throws WriterException */ - public static void write(UserRequest<? extends Principal> userRequest, Writer writer) + @Override + public <T extends Principal> void write(UserRequest<T> userRequest, Writer writer) throws IOException, WriterException { if (userRequest == null) @@ -47,7 +102,7 @@ public class JsonUserRequestWriter extends UserRequestWriter throw new WriterException("null UserRequest"); } - Element children = UserRequestWriter.getUserRequestElement(userRequest); + Element children = getElement(userRequest); Element userRequestElement = new Element("userRequest"); userRequestElement.addContent(children); Document document = new Document(); diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserWriter.java index 444a670a4453e0bd2e6b7f481fb9247e73df0525..3b9004ff24ae028ccd7b6027e2ce18b77394c902 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserWriter.java @@ -69,68 +69,21 @@ package ca.nrc.cadc.ac.json; import ca.nrc.cadc.ac.User; -import ca.nrc.cadc.ac.UserDetails; import ca.nrc.cadc.ac.WriterException; -import ca.nrc.cadc.ac.xml.GroupWriter; import ca.nrc.cadc.ac.xml.UserWriter; -import ca.nrc.cadc.util.StringBuilderWriter; import ca.nrc.cadc.xml.JsonOutputter; import org.jdom2.Document; import org.jdom2.Element; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import java.io.BufferedWriter; import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; import java.io.Writer; import java.security.Principal; -import java.util.Set; +/** + * Class to write a JSON representation of a User object. + */ public class JsonUserWriter extends UserWriter { - /** - * Write a User as a JSON string to a StringBuilder. - * - * @param user User to write. - * @param builder StringBuilder to write to. - * @throws IOException if the writer fails to write. - * @throws WriterException - */ - @Override - public void write(User<? extends Principal> user, StringBuilder builder) - throws IOException, WriterException - { - write(user, new StringBuilderWriter(builder)); - } - - /** - * Write a User as a JSON string to an OutputStream. - * - * @param user User to write. - * @param out OutputStream to write to. - * @throws IOException if the writer fails to write. - * @throws WriterException - */ - @Override - public void write(User<? extends Principal> user, OutputStream out) - throws IOException, WriterException - { - OutputStreamWriter outWriter; - try - { - outWriter = new OutputStreamWriter(out, "UTF-8"); - } - catch (UnsupportedEncodingException e) - { - throw new RuntimeException("UTF-8 encoding not supported", e); - } - write(user, new BufferedWriter(outWriter)); - } - /** * Write a User as a JSON string to a Writer. * @@ -140,7 +93,7 @@ public class JsonUserWriter extends UserWriter * @throws WriterException */ @Override - public void write(User<? extends Principal> user, Writer writer) + public<T extends Principal> void write(User<T> user, Writer writer) throws IOException, WriterException { if (user == null) @@ -148,7 +101,7 @@ public class JsonUserWriter extends UserWriter throw new WriterException("null User"); } - Element children = UserWriter.getUserElement(user); + Element children = getElement(user); Element userElement = new Element("user"); userElement.addContent(children); Document document = new Document(); diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/AbstractReaderWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/AbstractReaderWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..7401185b0487eafa718057a3fa74732f74f0e6bc --- /dev/null +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/AbstractReaderWriter.java @@ -0,0 +1,1017 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2011. (c) 2011. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* <http://www.gnu.org/licenses/>. pas le cas, consultez : +* <http://www.gnu.org/licenses/>. +* +* $Revision: 5 $ +* +************************************************************************ +*/ + +package ca.nrc.cadc.ac.xml; + +import ca.nrc.cadc.ac.AC; +import ca.nrc.cadc.ac.Group; +import ca.nrc.cadc.ac.GroupProperty; +import ca.nrc.cadc.ac.IdentityType; +import ca.nrc.cadc.ac.PersonalDetails; +import ca.nrc.cadc.ac.PosixDetails; +import ca.nrc.cadc.ac.ReaderException; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.UserDetails; +import ca.nrc.cadc.ac.UserRequest; +import ca.nrc.cadc.ac.WriterException; +import ca.nrc.cadc.auth.HttpPrincipal; +import ca.nrc.cadc.auth.NumericPrincipal; +import ca.nrc.cadc.auth.OpenIdPrincipal; +import ca.nrc.cadc.date.DateUtil; +import org.jdom2.Attribute; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; + +import javax.security.auth.x500.X500Principal; +import java.io.IOException; +import java.io.Writer; +import java.security.Principal; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.List; +import java.util.Set; + +/** + * AbstractReaderWriter TODO describe class + */ +public abstract class AbstractReaderWriter +{ + /** + * Write to root Element to a writer. + * + * @param root Root Element to write. + * @param writer Writer to write to. + * @throws IOException if the writer fails to write. + */ + protected void write(Element root, Writer writer) + throws IOException + { + XMLOutputter outputter = new XMLOutputter(); + outputter.setFormat(Format.getPrettyFormat()); + outputter.output(new Document(root), writer); + } + + /** + * Get a User object from a JDOM element. + * + * @param element The User JDOM element. + * @return A User object. + * @throws ReaderException + */ + protected final User<Principal> getUser(Element element) + throws ReaderException + { + // userID element of the User element + Element userIDElement = element.getChild("userID"); + if (userIDElement == null) + { + String error = "userID element not found in user element"; + throw new ReaderException(error); + } + + // identity element of the userID element + Element userIDIdentityElement = userIDElement.getChild("identity"); + if (userIDIdentityElement == null) + { + String error = "identity element not found in userID element"; + throw new ReaderException(error); + } + + Principal userID = getPrincipal(userIDIdentityElement); + User<Principal> user = new User<Principal>(userID); + + // identities + Element identitiesElement = element.getChild("identities"); + if (identitiesElement != null) + { + List<Element> identityElements = identitiesElement.getChildren("identity"); + for (Element identityElement : identityElements) + { + user.getIdentities().add(getPrincipal(identityElement)); + } + + } + + // details + Element detailsElement = element.getChild("details"); + if (detailsElement != null) + { + List<Element> userDetailsElements = detailsElement.getChildren("userDetails"); + for (Element userDetailsElement : userDetailsElements) + { + user.details.add(getUserDetails(userDetailsElement)); + } + } + + return user; + } + + /** + * Get a UserRequest object from a JDOM element. + * + * @param element The UserRequest JDOM element. + * @return A UserRequest object. + * @throws ReaderException + */ + protected final UserRequest<Principal> getUserRequest(Element element) + throws ReaderException + { + // user element of the UserRequest element + Element userElement = element.getChild("user"); + if (userElement == null) + { + String error = "user element not found in userRequest element"; + throw new ReaderException(error); + } + User<Principal> user = getUser(userElement); + + // password element of the userRequest element + Element passwordElement = element.getChild("password"); + if (passwordElement == null) + { + String error = "password element not found in userRequest element"; + throw new ReaderException(error); + } + String password = passwordElement.getText(); + + return new UserRequest<Principal>(user, password.toCharArray()); + } + + /** + * Get a Principal object from a JDOM element. + * + * @param element The Principal JDOM element. + * @return A Principal object. + * @throws ReaderException + */ + protected final Principal getPrincipal(Element element) + throws ReaderException + { + if (element == null) + { + String error = "null identity element"; + throw new ReaderException(error); + } + + if (!element.getName().equals("identity")) + { + String error = "expected identity element name, found " + + element.getName(); + throw new ReaderException(error); + } + + String type = element.getAttributeValue("type"); + if (type == null) + { + String error = "type attribute not found in identity element" + + element.getName(); + throw new ReaderException(error); + } + + String identity = element.getText(); + Principal principal; + if (type.equals(IdentityType.OPENID.getValue())) + { + principal = new OpenIdPrincipal(identity); + } + else if (type.equals(IdentityType.CADC.getValue())) + { + Integer cadcID; + try + { + cadcID = Integer.valueOf(identity); + } + catch (NumberFormatException e) + { + String error = "Non-integer cadcID: " + identity; + throw new ReaderException(error); + } + principal = new NumericPrincipal(cadcID); + } + else if (type.equals(IdentityType.USERNAME.getValue())) + { + principal = new HttpPrincipal(identity); + } + else if (type.equals(IdentityType.X500.getValue())) + { + principal = new X500Principal(identity); + } + else + { + String error = "Unknown type attribute: " + type; + throw new ReaderException(error); + } + + return principal; + } + + /** + * Get a UserDetails object from a JDOM element. + * + * @param element The UserDetails JDOM element. + * @return A UserDetails object. + * @throws ReaderException + */ + protected final UserDetails getUserDetails(Element element) + throws ReaderException + { + if (element == null) + { + throw new ReaderException("null UserDetails"); + } + + if (!element.getName().equals(UserDetails.NAME)) + { + String error = "expected element name userDetails, found " + + element.getName(); + throw new ReaderException(error); + } + + String type = element.getAttributeValue(UserDetails.TYPE_ATTRIBUTE); + if (type == null) + { + String error = "userDetails missing required attribute type"; + throw new ReaderException(error); + } + + if (type.equals(PosixDetails.NAME)) + { + return getPosixDetails(element); + } + if (type.equals(PersonalDetails.NAME)) + { + return getPersonalDetails(element); + } + + String error = "Unknown UserDetails attribute type " + type; + throw new ReaderException(error); + } + + /** + * Get a PosixDetails object from a JDOM element. + * + * @param element The PosixDetails JDOM element. + * @return A PosixDetails object. + * @throws ReaderException + */ + protected final PosixDetails getPosixDetails(Element element) + throws ReaderException + { + // uid + Element uidElement = element.getChild(PosixDetails.UID); + if (uidElement == null) + { + String error = "posixDetails missing required element uid"; + throw new ReaderException(error); + } + long uid; + try + { + uid = Long.valueOf(uidElement.getText()); + } + catch (NumberFormatException e) + { + String error = "Cannot parse posixDetails uid to a long"; + throw new ReaderException(error); + } + + // gid + Element gidElement = element.getChild(PosixDetails.GID); + if (gidElement == null) + { + String error = "posixDetails missing required element gid"; + throw new ReaderException(error); + } + long gid; + try + { + gid = Long.valueOf(gidElement.getText()); + } + catch (NumberFormatException e) + { + String error = "Cannot parse posixDetails gid to a long"; + throw new ReaderException(error); + } + + // homeDirectory + Element homeDirElement = element.getChild(PosixDetails.HOME_DIRECTORY); + if (homeDirElement == null) + { + String error = "posixDetails missing required element homeDirectory"; + throw new ReaderException(error); + } + String homeDirectory = homeDirElement.getText(); + + return new PosixDetails(uid, gid, homeDirectory); + } + + /** + * Get a PersonalDetails object from a JDOM element. + * + * @param element The PersonalDetails JDOM element. + * @return A PersonalDetails object. + * @throws ReaderException + */ + protected final PersonalDetails getPersonalDetails(Element element) + throws ReaderException + { + // firstName + Element firstNameElement = element.getChild(PersonalDetails.FIRSTNAME); + if (firstNameElement == null) + { + String error = "personalDetails missing required element firstName"; + throw new ReaderException(error); + } + String firstName = firstNameElement.getText(); + + // lastName + Element lastNameElement = element.getChild(PersonalDetails.LASTNAME); + if (lastNameElement == null) + { + String error = "personalDetails missing required element lastName"; + throw new ReaderException(error); + } + String lastName = lastNameElement.getText(); + + PersonalDetails details = new PersonalDetails(firstName, lastName); + + // email + Element emailElement = element.getChild(PersonalDetails.EMAIL); + if (emailElement != null) + { + details.email = emailElement.getText(); + } + + // address + Element addressElement = element.getChild(PersonalDetails.ADDRESS); + if (addressElement != null) + { + details.address = addressElement.getText(); + } + + // institute + Element instituteElement = element.getChild(PersonalDetails.INSTITUTE); + if (instituteElement != null) + { + details.institute = instituteElement.getText(); + } + + // city + Element cityElement = element.getChild(PersonalDetails.CITY); + if (cityElement != null) + { + details.city = cityElement.getText(); + } + + // country + Element countryElement = element.getChild(PersonalDetails.COUNTRY); + if (countryElement != null) + { + details.country = countryElement.getText(); + } + + return details; + } + + /** + * Get a UserRequest object from a JDOM element. + * + * @param element The UserRequest JDOM element. + * @return A UserRequest object. + * @throws ReaderException + */ + protected final Group getGroup(Element element) + throws ReaderException + { + String uri = element.getAttributeValue("uri"); + if (uri == null) + { + String error = "group missing required uri attribute"; + throw new ReaderException(error); + } + + // Group groupID + int index = uri.indexOf(AC.GROUP_URI); + if (index == -1) + { + String error = "group uri attribute malformed: " + uri; + throw new ReaderException(error); + } + String groupID = uri.substring(AC.GROUP_URI.length()); + + // Group owner + User<? extends Principal> user = null; + Element ownerElement = element.getChild("owner"); + if (ownerElement != null) + { + // Owner user + Element userElement = ownerElement.getChild("user"); + if (userElement == null) + { + String error = "owner missing required user element"; + throw new ReaderException(error); + } + user = getUser(userElement); + } + + Group group = new Group(groupID, user); + + // description + Element descriptionElement = element.getChild("description"); + if (descriptionElement != null) + { + group.description = descriptionElement.getText(); + } + + // lastModified + Element lastModifiedElement = element.getChild("lastModified"); + if (lastModifiedElement != null) + { + try + { + DateFormat df = DateUtil.getDateFormat(DateUtil.IVOA_DATE_FORMAT, DateUtil.UTC); + group.lastModified = df.parse(lastModifiedElement.getText()); + } + catch (ParseException e) + { + String error = "Unable to parse group lastModified because " + e.getMessage(); + + throw new ReaderException(error); + } + } + + // properties + Element propertiesElement = element.getChild("properties"); + if (propertiesElement != null) + { + List<Element> propertyElements = propertiesElement.getChildren("property"); + for (Element propertyElement : propertyElements) + { + group.getProperties().add(getGroupProperty(propertyElement)); + } + } + + // groupMembers + Element groupMembersElement = element.getChild("groupMembers"); + if (groupMembersElement != null) + { + List<Element> groupElements = groupMembersElement.getChildren("group"); + for (Element groupMember : groupElements) + { + group.getGroupMembers().add(getGroup(groupMember)); + } + } + + // userMembers + Element userMembersElement = element.getChild("userMembers"); + if (userMembersElement != null) + { + List<Element> userElements = userMembersElement.getChildren("user"); + for (Element userMember : userElements) + { + group.getUserMembers().add(getUser(userMember)); + } + } + + // groupAdmins + Element groupAdminsElement = element.getChild("groupAdmins"); + if (groupAdminsElement != null) + { + List<Element> groupElements = groupAdminsElement.getChildren("group"); + for (Element groupMember : groupElements) + { + group.getGroupAdmins().add(getGroup(groupMember)); + } + } + + // userAdmins + Element userAdminsElement = element.getChild("userAdmins"); + if (userAdminsElement != null) + { + List<Element> userElements = userAdminsElement.getChildren("user"); + for (Element userMember : userElements) + { + group.getUserAdmins().add(getUser(userMember)); + } + } + + return group; + } + + /** + * Get a GroupProperty object from a JDOM element. + * + * @param element The GroupProperty JDOM element. + * @return A GroupProperty object. + * @throws ReaderException + */ + protected final GroupProperty getGroupProperty(Element element) + throws ReaderException + { + if (element == null) + { + String error = "null property element"; + throw new ReaderException(error); + } + + if (!element.getName().equals(GroupProperty.NAME)) + { + String error = "expected property element name, found " + + element.getName(); + throw new ReaderException(error); + } + + String key = element.getAttributeValue(GroupProperty.KEY_ATTRIBUTE); + if (key == null) + { + String error = "required key attribute not found"; + throw new ReaderException(error); + } + + String type = element.getAttributeValue(GroupProperty.TYPE_ATTRIBUTE); + if (type == null) + { + String error = "required type attribute not found"; + throw new ReaderException(error); + } + Object value; + if (type.equals(GroupProperty.STRING_TYPE)) + { + value = String.valueOf(element.getText()); + } + else + { + if (type.equals(GroupProperty.INTEGER_TYPE)) + { + value = Integer.valueOf(element.getText()); + } + else + { + String error = "Unsupported GroupProperty type: " + type; + throw new ReaderException(error); + } + } + Boolean readOnly = Boolean.valueOf(element.getAttributeValue(GroupProperty.READONLY_ATTRIBUTE)); + + return new GroupProperty(key, value, readOnly); + } + + /** + * Get a JDOM element from a User object. + * + * @param user The User. + * @return A JDOM User representation. + * @throws WriterException + */ + protected final Element getElement(User<? extends Principal> user) + throws WriterException + { + // Create the user Element. + Element userElement = new Element("user"); + + // userID element + Element userIDElement = new Element("userID"); + userIDElement.addContent(getElement(user.getUserID())); + userElement.addContent(userIDElement); + + // identities + Set<Principal> identities = user.getIdentities(); + if (!identities.isEmpty()) + { + Element identitiesElement = new Element("identities"); + for (Principal identity : identities) + { + identitiesElement.addContent(getElement(identity)); + } + userElement.addContent(identitiesElement); + } + + // details + if (!user.details.isEmpty()) + { + Element detailsElement = new Element("details"); + Set<UserDetails> userDetails = user.details; + for (UserDetails userDetail : userDetails) + { + detailsElement.addContent(getElement(userDetail)); + } + userElement.addContent(detailsElement); + } + + return userElement; + } + + /** + * Get a JDOM element from a UserRequest object. + * + * @param userRequest The UserRequest. + * @return A JDOM UserRequest representation. + * @throws WriterException + */ + protected final Element getElement(UserRequest<? extends Principal> userRequest) + throws WriterException + { + // Create the userRequest Element. + Element userRequestElement = new Element("userRequest"); + + // user element + Element userElement = getElement(userRequest.getUser()); + userRequestElement.addContent(userElement); + + // password element + Element passwordElement = new Element("password"); + passwordElement.setText(String.valueOf(userRequest.getPassword())); + userRequestElement.addContent(passwordElement); + + return userRequestElement; + } + + /** + * Get a JDOM element from a Principal object. + * + * @param identity The Principal. + * @return A JDOM UserDetails representation. + * @throws WriterException + */ + protected final Element getElement(Principal identity) + throws WriterException + { + if (identity == null) + { + String error = "null identity"; + throw new WriterException(error); + } + + Element identityElement = new Element("identity"); + if ((identity instanceof HttpPrincipal)) + { + identityElement.setAttribute("type", IdentityType.USERNAME.getValue()); + } + else if ((identity instanceof NumericPrincipal)) + { + identityElement.setAttribute("type", IdentityType.CADC.getValue()); + } + else if ((identity instanceof OpenIdPrincipal)) + { + identityElement.setAttribute("type", IdentityType.OPENID.getValue()); + } + else if ((identity instanceof X500Principal)) + { + identityElement.setAttribute("type", IdentityType.X500.getValue()); + } + else + { + String error = "Unsupported Principal type " + + identity.getClass().getSimpleName(); + throw new IllegalArgumentException(error); + } + identityElement.setText(identity.getName()); + + return identityElement; + } + + /** + * Get a JDOM element from a UserDetails object. + * + * @param details The UserDetails. + * @return A JDOM UserDetails representation. + * @throws WriterException + */ + protected final Element getElement(UserDetails details) + throws WriterException + { + if (details == null) + { + throw new WriterException("null UserDetails"); + } + + if ((details instanceof PosixDetails)) + { + return getElement((PosixDetails) details); + } + if ((details instanceof PersonalDetails)) + { + return getElement((PersonalDetails) details); + } + + String error = "Unknown UserDetails implementation: " + + details.getClass().getName(); + throw new WriterException(error); + } + + /** + * Get a JDOM element from a PosixDetails object. + * + * @param details The PosixDetails. + * @return A JDOM PosixDetails representation. + */ + protected final Element getElement(PosixDetails details) + { + Element detailsElement = new Element(UserDetails.NAME); + detailsElement.setAttribute(UserDetails.TYPE_ATTRIBUTE, + PosixDetails.NAME); + + Element uidElement = new Element(PosixDetails.UID); + uidElement.setText(String.valueOf(details.getUid())); + detailsElement.addContent(uidElement); + + Element gidElement = new Element(PosixDetails.GID); + gidElement.setText(String.valueOf(details.getGid())); + detailsElement.addContent(gidElement); + + Element homeDirElement = new Element(PosixDetails.HOME_DIRECTORY); + homeDirElement.setText(details.getHomeDirectory()); + detailsElement.addContent(homeDirElement); + + return detailsElement; + } + + /** + * Get a JDOM element from a PersonalDetails object. + * + * @param details The PersonalDetails. + * @return JDOM PersonalDetails representation. + */ + protected final Element getElement(PersonalDetails details) + { + Element detailsElement = new Element(UserDetails.NAME); + detailsElement.setAttribute(UserDetails.TYPE_ATTRIBUTE, + PersonalDetails.NAME); + + Element firstNameElement = new Element(PersonalDetails.FIRSTNAME); + firstNameElement.setText(details.getFirstName()); + detailsElement.addContent(firstNameElement); + + Element lastNameElement = new Element(PersonalDetails.LASTNAME); + lastNameElement.setText(details.getLastName()); + detailsElement.addContent(lastNameElement); + + if (details.email != null) + { + Element emailElement = new Element(PersonalDetails.EMAIL); + emailElement.setText(details.email); + detailsElement.addContent(emailElement); + } + + if (details.address != null) + { + Element addressElement = new Element(PersonalDetails.ADDRESS); + addressElement.setText(details.address); + detailsElement.addContent(addressElement); + } + + if (details.institute != null) + { + Element instituteElement = new Element(PersonalDetails.INSTITUTE); + instituteElement.setText(details.institute); + detailsElement.addContent(instituteElement); + } + + if (details.city != null) + { + Element cityElement = new Element(PersonalDetails.CITY); + cityElement.setText(details.city); + detailsElement.addContent(cityElement); + } + + if (details.country != null) + { + Element countryElement = new Element(PersonalDetails.COUNTRY); + countryElement.setText(details.country); + detailsElement.addContent(countryElement); + } + + return detailsElement; + } + + /** + * Get a JDOM element from a Group object. + * + * @param group The UserRequest. + * @return A JDOM Group representation. + * @throws WriterException + */ + protected final Element getElement(Group group) + throws WriterException + { + return getElement(group, true); + } + + /** + * Get a JDOM element from a Group object. + * + * @param group The UserRequest. + * @param deepCopy Return all Group elements. + * @return A JDOM Group representation. + * @throws WriterException + */ + protected final Element getElement(Group group, boolean deepCopy) + throws WriterException + { + // Create the root group element. + Element groupElement = new Element("group"); + String groupURI = AC.GROUP_URI + group.getID(); + groupElement.setAttribute(new Attribute("uri", groupURI)); + + // Group owner + if (group.getOwner() != null) + { + Element ownerElement = new Element("owner"); + Element userElement = getElement(group.getOwner()); + ownerElement.addContent(userElement); + groupElement.addContent(ownerElement); + } + + if (deepCopy) + { + // Group description + if (group.description != null) + { + Element descriptionElement = new Element("description"); + descriptionElement.setText(group.description); + groupElement.addContent(descriptionElement); + } + + // lastModified + if (group.lastModified != null) + { + Element lastModifiedElement = new Element("lastModified"); + DateFormat df = DateUtil.getDateFormat(DateUtil.IVOA_DATE_FORMAT, DateUtil.UTC); + lastModifiedElement.setText(df.format(group.lastModified)); + groupElement.addContent(lastModifiedElement); + } + + // Group properties + if (!group.getProperties().isEmpty()) + { + Element propertiesElement = new Element("properties"); + for (GroupProperty property : group.getProperties()) + { + propertiesElement.addContent(getElement(property)); + } + groupElement.addContent(propertiesElement); + } + + // Group groupMembers. + if ((group.getGroupMembers() != null) && (!group.getGroupMembers().isEmpty())) + { + Element groupMembersElement = new Element("groupMembers"); + for (Group groupMember : group.getGroupMembers()) + { + groupMembersElement.addContent(getElement(groupMember, false)); + } + groupElement.addContent(groupMembersElement); + } + + // Group userMembers + if ((group.getUserMembers() != null) && (!group.getUserMembers().isEmpty())) + { + Element userMembersElement = new Element("userMembers"); + for (User<? extends Principal> userMember : group.getUserMembers()) + { + userMembersElement.addContent(getElement(userMember)); + } + groupElement.addContent(userMembersElement); + } + + // Group groupAdmins. + if ((group.getGroupAdmins() != null) && (!group.getGroupAdmins().isEmpty())) + { + Element groupAdminsElement = new Element("groupAdmins"); + for (Group groupMember : group.getGroupAdmins()) + { + groupAdminsElement.addContent(getElement(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(getElement(userMember)); + } + groupElement.addContent(userAdminsElement); + } + } + + return groupElement; + } + + /** + * Get a JDOM element from a GroupProperty object. + * + * @param property The GroupProperty. + * @return A JDOM GroupProperty representation. + * @throws WriterException + */ + protected final Element getElement(GroupProperty property) + throws WriterException + { + if (property == null) + { + throw new WriterException("null GroupProperty"); + } + + Element propertyElement = new Element(GroupProperty.NAME); + propertyElement.setAttribute(GroupProperty.KEY_ATTRIBUTE, + property.getKey()); + if (property.isReadOnly()) + { + propertyElement.setAttribute(GroupProperty.READONLY_ATTRIBUTE, + "true"); + } + + Object value = property.getValue(); + if ((value instanceof String)) + { + propertyElement.setAttribute(GroupProperty.TYPE_ATTRIBUTE, + GroupProperty.STRING_TYPE); + } + else if ((value instanceof Integer)) + { + propertyElement.setAttribute(GroupProperty.TYPE_ATTRIBUTE, + GroupProperty.INTEGER_TYPE); + } + else + { + String error = "Unsupported value type: " + + value.getClass().getSimpleName(); + throw new IllegalArgumentException(error); + } + propertyElement.setText(String.valueOf(property.getValue())); + + return propertyElement; + } + +} diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListReader.java index cd962918e443f8d3db33cd7eead08d475285543d..da9f16c848b0c53e57051d62d4a241012b1392db 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListReader.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListReader.java @@ -71,6 +71,10 @@ package ca.nrc.cadc.ac.xml; import ca.nrc.cadc.ac.Group; import ca.nrc.cadc.ac.ReaderException; import ca.nrc.cadc.xml.XmlUtil; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; + import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -80,15 +84,12 @@ import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.JDOMException; /** - * Class to read an XML representation of a list of Groups - * into a List of Group objects. + * Class to read an XML representation of a List of Groups + * into a Collection of Group objects. */ -public class GroupListReader +public class GroupListReader extends AbstractReaderWriter { /** * Construct a list of Group's from an XML String source. @@ -110,7 +111,7 @@ public class GroupListReader } /** - * Construct a list of Group's from a InputStream. + * Construct a List of Group's from a InputStream. * * @param in InputStream. * @return Groups List of Group. @@ -175,20 +176,29 @@ public class GroupListReader throw new ReaderException(error); } - return parseGroups(root); + return getGroupList(root); } - protected static List<Group> parseGroups(Element groupsElement) - throws URISyntaxException, ReaderException - { + /** + * Get a List of Groups from a JDOM element. + * + * @param element The Group's JDOM element. + * @return A List of Group objects. + * @throws URISyntaxException + * @throws ReaderException + */ + protected final List<Group> getGroupList(Element element) + throws URISyntaxException, ReaderException + {; List<Group> groups = new ArrayList<Group>(); - List<Element> groupElements = groupsElement.getChildren("group"); + List<Element> groupElements = element.getChildren("group"); for (Element groupElement : groupElements) { - groups.add(GroupReader.parseGroup(groupElement)); + groups.add(getGroup(groupElement)); } return groups; } + } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListWriter.java index b3707fb3d7ce6bb3e2f3c16a6e9f59c01606b786..a42cff8bef88bf2965dd59888ea8cd81e8838abb 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListWriter.java @@ -1,8 +1,79 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2011. (c) 2011. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* <http://www.gnu.org/licenses/>. pas le cas, consultez : +* <http://www.gnu.org/licenses/>. +* +* $Revision: 5 $ +* +************************************************************************ +*/ + package ca.nrc.cadc.ac.xml; import ca.nrc.cadc.ac.Group; import ca.nrc.cadc.ac.WriterException; import ca.nrc.cadc.util.StringBuilderWriter; +import org.jdom2.Element; + import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; @@ -10,19 +81,15 @@ import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.Collection; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.output.Format; -import org.jdom2.output.XMLOutputter; /** * Class to write a XML representation from a Collection of Groups objects. */ -public class GroupListWriter +public class GroupListWriter extends AbstractReaderWriter { /** - * Write a List of Group's to a StringBuilder. - * @param groups List of Group's to write. + * Write a Collection of Group's to a StringBuilder. + * @param groups Collection of Group's to write. * @param builder * @throws java.io.IOException * @throws WriterException @@ -34,9 +101,9 @@ public class GroupListWriter } /** - * Write a List of Group's to an OutputStream. + * Write a Collection of Group's to an OutputStream. * - * @param groups List of Group's to write. + * @param groups Collection of Group's to write. * @param out OutputStream to write to. * @throws IOException if the writer fails to write. * @throws WriterException @@ -57,9 +124,9 @@ public class GroupListWriter } /** - * Write a List of Group's to a Writer. + * Write a Collection of Group's to a Writer. * - * @param groups List of Group's to write. + * @param groups Collection of Group's to write. * @param writer Writer to write to. * @throws IOException if the writer fails to write. * @throws WriterException @@ -72,41 +139,27 @@ public class GroupListWriter throw new WriterException("null groups"); } - write(getGroupsElement(groups), writer); + write(getElement(groups), writer); } /** - * - * @param groups List of Group's to write. - * @return Element of list of Group's. + * Get a JDOM element from a Collection of Group objects. + * + * @param groups Collection of Group's to write. + * @return A JDOM Group list representation. * @throws WriterException */ - public static Element getGroupsElement(Collection<Group> groups) + protected final Element getElement(Collection<Group> groups) throws WriterException { Element groupsElement = new Element("groups"); for (Group group : groups) { - groupsElement.addContent(GroupWriter.getGroupElement(group)); + groupsElement.addContent(getElement(group)); } return groupsElement; } - /** - * Write to root Element to a writer. - * - * @param root Root Element to write. - * @param writer Writer to write to. - * @throws IOException if the writer fails to write. - */ - private static void write(Element root, Writer writer) - throws IOException - { - XMLOutputter outputter = new XMLOutputter(); - outputter.setFormat(Format.getPrettyFormat()); - outputter.output(new Document(root), writer); - } - } \ No newline at end of file diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupReader.java index 0f0d1dca1233d54a01c9cbb0da2ba9685fffc18c..b9c64d1bcd3a75ed9855739db1df0f999fe0fe7d 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupReader.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupReader.java @@ -68,11 +68,8 @@ */ package ca.nrc.cadc.ac.xml; -import ca.nrc.cadc.ac.AC; import ca.nrc.cadc.ac.Group; import ca.nrc.cadc.ac.ReaderException; -import ca.nrc.cadc.ac.User; -import ca.nrc.cadc.date.DateUtil; import ca.nrc.cadc.xml.XmlUtil; import org.jdom2.Document; import org.jdom2.Element; @@ -85,15 +82,11 @@ import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; -import java.security.Principal; -import java.text.DateFormat; -import java.text.ParseException; -import java.util.List; /** * Class to read a XML representation of a Group to a Group object. */ -public class GroupReader +public class GroupReader extends AbstractReaderWriter { /** @@ -181,128 +174,7 @@ public class GroupReader throw new ReaderException(error); } - return parseGroup(root); + return getGroup(root); } - public static Group parseGroup(Element groupElement) - throws ReaderException - { - String uri = groupElement.getAttributeValue("uri"); - if (uri == null) - { - String error = "group missing required uri attribute"; - throw new ReaderException(error); - } - - // Group groupID - int index = uri.indexOf(AC.GROUP_URI); - if (index == -1) - { - String error = "group uri attribute malformed: " + uri; - throw new ReaderException(error); - } - String groupID = uri.substring(AC.GROUP_URI.length()); - - // Group owner - User<? extends Principal> user = null; - Element ownerElement = groupElement.getChild("owner"); - if (ownerElement != null) - { - // Owner user - Element userElement = ownerElement.getChild("user"); - if (userElement == null) - { - String error = "owner missing required user element"; - throw new ReaderException(error); - } - user = UserReader.parseUser(userElement); - } - - Group group = new Group(groupID, user); - - // description - Element descriptionElement = groupElement.getChild("description"); - if (descriptionElement != null) - { - group.description = descriptionElement.getText(); - } - - // lastModified - Element lastModifiedElement = groupElement.getChild("lastModified"); - if (lastModifiedElement != null) - { - try - { - DateFormat df = DateUtil.getDateFormat(DateUtil.IVOA_DATE_FORMAT, DateUtil.UTC); - group.lastModified = df.parse(lastModifiedElement.getText()); - } - catch (ParseException e) - { - String error = "Unable to parse group lastModified because " + e.getMessage(); - - throw new ReaderException(error); - } - - } - - // properties - Element propertiesElement = groupElement.getChild("properties"); - if (propertiesElement != null) - { - List<Element> propertyElements = propertiesElement.getChildren("property"); - for (Element propertyElement : propertyElements) - { - group.getProperties().add(ca.nrc.cadc.ac.xml.GroupPropertyReader.read(propertyElement)); - } - - } - - // groupMembers - Element groupMembersElement = groupElement.getChild("groupMembers"); - if (groupMembersElement != null) - { - List<Element> groupElements = groupMembersElement.getChildren("group"); - for (Element groupMember : groupElements) - { - group.getGroupMembers().add(parseGroup(groupMember)); - } - - } - - // userMembers - Element userMembersElement = groupElement.getChild("userMembers"); - if (userMembersElement != null) - { - List<Element> userElements = userMembersElement.getChildren("user"); - for (Element userMember : userElements) - { - group.getUserMembers().add(UserReader.parseUser(userMember)); - } - } - - // groupAdmins - Element groupAdminsElement = groupElement.getChild("groupAdmins"); - if (groupAdminsElement != null) - { - List<Element> groupElements = groupAdminsElement.getChildren("group"); - for (Element groupMember : groupElements) - { - group.getGroupAdmins().add(parseGroup(groupMember)); - } - - } - - // userAdmins - Element userAdminsElement = groupElement.getChild("userAdmins"); - if (userAdminsElement != null) - { - List<Element> userElements = userAdminsElement.getChildren("user"); - for (Element userMember : userElements) - { - group.getUserAdmins().add(UserReader.parseUser(userMember)); - } - } - - return group; - } } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupWriter.java index 380ba79a8d7ec2fa8e203f8e7f6d8bafbb51ff0e..67687c5374fee4350aedaf80eb67d277e869de1e 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupWriter.java @@ -68,18 +68,9 @@ */ package ca.nrc.cadc.ac.xml; -import ca.nrc.cadc.ac.AC; import ca.nrc.cadc.ac.Group; -import ca.nrc.cadc.ac.GroupProperty; -import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.WriterException; -import ca.nrc.cadc.date.DateUtil; import ca.nrc.cadc.util.StringBuilderWriter; -import org.jdom2.Attribute; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.output.Format; -import org.jdom2.output.XMLOutputter; import java.io.BufferedWriter; import java.io.IOException; @@ -87,13 +78,11 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; -import java.security.Principal; -import java.text.DateFormat; /** * Class to write a XML representation of a Group object. */ -public class GroupWriter +public class GroupWriter extends AbstractReaderWriter { /** * Write a Group to a StringBuilder. @@ -147,129 +136,7 @@ public class GroupWriter throw new WriterException("null group"); } - write(getGroupElement(group), writer); + write(getElement(group), writer); } - /** - * - * @param group - * @return - * @throws WriterException - */ - public static Element getGroupElement(Group group) - throws WriterException - { - return getGroupElement(group, true); - } - - public static Element getGroupElement(Group group, boolean deepCopy) - throws WriterException - { - // Create the root group element. - Element groupElement = new Element("group"); - String groupURI = AC.GROUP_URI + group.getID(); - groupElement.setAttribute(new Attribute("uri", groupURI)); - - // Group owner - if (group.getOwner() != null) - { - Element ownerElement = new Element("owner"); - Element userElement = UserWriter.getUserElement(group.getOwner()); - ownerElement.addContent(userElement); - groupElement.addContent(ownerElement); - } - - if (deepCopy) - { - // Group description - if (group.description != null) - { - Element descriptionElement = new Element("description"); - descriptionElement.setText(group.description); - groupElement.addContent(descriptionElement); - } - - // lastModified - if (group.lastModified != null) - { - Element lastModifiedElement = new Element("lastModified"); - DateFormat df = DateUtil.getDateFormat(DateUtil.IVOA_DATE_FORMAT, DateUtil.UTC); - lastModifiedElement.setText(df.format(group.lastModified)); - groupElement.addContent(lastModifiedElement); - } - - // Group properties - if (!group.getProperties().isEmpty()) - { - Element propertiesElement = new Element("properties"); - for (GroupProperty property : group.getProperties()) - { - propertiesElement.addContent(ca.nrc.cadc.ac.xml.GroupPropertyWriter.write(property)); - } - groupElement.addContent(propertiesElement); - } - - // Group groupMembers. - if ((group.getGroupMembers() != null) && (!group.getGroupMembers().isEmpty())) - { - Element groupMembersElement = new Element("groupMembers"); - for (Group groupMember : group.getGroupMembers()) - { - groupMembersElement.addContent(getGroupElement(groupMember, false)); - } - groupElement.addContent(groupMembersElement); - } - - // Group userMembers - if ((group.getUserMembers() != null) && (!group.getUserMembers().isEmpty())) - { - Element userMembersElement = new Element("userMembers"); - for (User<? extends Principal> userMember : group.getUserMembers()) - { - userMembersElement.addContent(UserWriter.getUserElement(userMember)); - } - 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; - } - - /** - * Write to root Element to a writer. - * - * @param root Root Element to write. - * @param writer Writer to write to. - * @throws IOException if the writer fails to write. - */ - private static void write(Element root, Writer writer) - throws IOException - { - XMLOutputter outputter = new XMLOutputter(); - outputter.setFormat(Format.getPrettyFormat()); - outputter.output(new Document(root), writer); - } - } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListReader.java new file mode 100644 index 0000000000000000000000000000000000000000..ae454fcb8ebe8aeb4c66537d711cb4b73a00276a --- /dev/null +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListReader.java @@ -0,0 +1,204 @@ +/* + ************************************************************************ + ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* + ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** + * + * (c) 2015. (c) 2015. + * Government of Canada Gouvernement du Canada + * National Research Council Conseil national de recherches + * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 + * All rights reserved Tous droits réservés + * + * NRC disclaims any warranties, Le CNRC dénie toute garantie + * expressed, implied, or énoncée, implicite ou légale, + * statutory, of any kind with de quelque nature que ce + * respect to the software, soit, concernant le logiciel, + * including without limitation y compris sans restriction + * any warranty of merchantability toute garantie de valeur + * or fitness for a particular marchande ou de pertinence + * purpose. NRC shall not be pour un usage particulier. + * liable in any event for any Le CNRC ne pourra en aucun cas + * damages, whether direct or être tenu responsable de tout + * indirect, special or general, dommage, direct ou indirect, + * consequential or incidental, particulier ou général, + * arising from the use of the accessoire ou fortuit, résultant + * software. Neither the name de l'utilisation du logiciel. Ni + * of the National Research le nom du Conseil National de + * Council of Canada nor the Recherches du Canada ni les noms + * names of its contributors may de ses participants ne peuvent + * be used to endorse or promote être utilisés pour approuver ou + * products derived from this promouvoir les produits dérivés + * software without specific prior de ce logiciel sans autorisation + * written permission. préalable et particulière + * par écrit. + * + * This file is part of the Ce fichier fait partie du projet + * OpenCADC project. OpenCADC. + * + * OpenCADC is free software: OpenCADC est un logiciel libre ; + * you can redistribute it and/or vous pouvez le redistribuer ou le + * modify it under the terms of modifier suivant les termes de + * the GNU Affero General Public la “GNU Affero General Public + * License as published by the License” telle que publiée + * Free Software Foundation, par la Free Software Foundation + * either version 3 of the : soit la version 3 de cette + * License, or (at your option) licence, soit (à votre gré) + * any later version. toute version ultérieure. + * + * OpenCADC is distributed in the OpenCADC est distribué + * hope that it will be useful, dans l’espoir qu’il vous + * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE + * without even the implied GARANTIE : sans même la garantie + * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ + * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF + * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence + * General Public License for Générale Publique GNU Affero + * more details. pour plus de détails. + * + * You should have received Vous devriez avoir reçu une + * a copy of the GNU Affero copie de la Licence Générale + * General Public License along Publique GNU Affero avec + * with OpenCADC. If not, see OpenCADC ; si ce n’est + * <http://www.gnu.org/licenses/>. pas le cas, consultez : + * <http://www.gnu.org/licenses/>. + * + * + ************************************************************************ + */ + +package ca.nrc.cadc.ac.xml; + +import ca.nrc.cadc.ac.ReaderException; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.xml.XmlUtil; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +/** + * Class to read an XML representation of a List of Users + * into a List of User objects. + */ +public class UserListReader extends AbstractReaderWriter +{ + /** + * Construct a List of Users from an XML String source. + * + * @param xml String of the XML. + * @return List of users. + * @throws ReaderException + * @throws java.io.IOException + * @throws java.net.URISyntaxException + */ + public List<User<Principal>> read(String xml) + throws ReaderException, IOException, URISyntaxException + { + if (xml == null) + { + throw new IllegalArgumentException("XML must not be null"); + } + return read(new StringReader(xml)); + } + + /** + * Construct a List of Users from a InputStream. + * + * @param in InputStream. + * @return List of Users. + * @throws ReaderException + * @throws java.io.IOException + * @throws java.net.URISyntaxException + */ + public List<User<Principal>> read(InputStream in) + throws ReaderException, IOException, URISyntaxException + { + if (in == null) + { + throw new IOException("stream closed"); + } + InputStreamReader reader; + try + { + reader = new InputStreamReader(in, "UTF-8"); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException("UTF-8 encoding not supported"); + } + return read(reader); + } + + /** + * Construct a List of Users from a Reader. + * + * @param reader Reader. + * @return List of Users. + * @throws ReaderException + * @throws java.io.IOException + * @throws java.net.URISyntaxException + */ + public List<User<Principal>> read(Reader reader) + throws ReaderException, IOException, URISyntaxException + { + if (reader == null) + { + throw new IllegalArgumentException("reader must not be null"); + } + + Document document; + try + { + document = XmlUtil.buildDocument(reader); + } + catch (JDOMException jde) + { + String error = "XML failed validation: " + jde.getMessage(); + throw new ReaderException(error, jde); + } + + Element root = document.getRootElement(); + + String userElemName = root.getName(); + + if (!userElemName.equalsIgnoreCase("users")) + { + String error = "Expected users element, found " + userElemName; + throw new ReaderException(error); + } + + return getUserList(root); + } + + /** + * Get a List of Users from a JDOM element. + * + * @param element The Users JDOM element. + * @return A List of User objects. + * @throws URISyntaxException + * @throws ReaderException + */ + protected final List<User<Principal>> getUserList(Element element) + throws URISyntaxException, ReaderException + { + List<User<Principal>> users = new ArrayList<User<Principal>>(); + + List<Element> userElements = element.getChildren("user"); + for (Element userElement : userElements) + { + users.add(getUser(userElement)); + } + + return users; + } +} diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListWriter.java index f08d4de4c4ba371da692b858c25e51198cb6abe0..1674324dffa067f6fc06bd9bc3629c7c7bf27d15 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListWriter.java @@ -68,54 +68,100 @@ package ca.nrc.cadc.ac.xml; -import ca.nrc.cadc.ac.PersonalDetails; -import org.jdom2.Document; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.WriterException; +import ca.nrc.cadc.util.StringBuilderWriter; import org.jdom2.Element; -import org.jdom2.output.Format; -import org.jdom2.output.XMLOutputter; +import java.io.BufferedWriter; import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; import java.io.Writer; -import java.util.Map; +import java.security.Principal; +import java.util.Collection; /** - * Class to write a XML representation of a Collection of User's. + * Class to write a XML representation of a List of User's. */ -public class UserListWriter +public class UserListWriter extends AbstractReaderWriter { /** - * Write the Map of User entries as XML. + * Write a Collection of User's to a StringBuilder. * - * @param users The Map of User IDs to Names. - * @param writer The Writer to output to. - * @throws IOException Any writing errors. + * @param users Collection of User's to write. + * @param builder The StringBuilder. + * @throws java.io.IOException + * @throws WriterException */ - public void write(final Map<String, PersonalDetails> users, - final Writer writer) throws IOException + public <T extends Principal> void write(Collection<User<T>> users, StringBuilder builder) + throws IOException, WriterException { - // Create the root users Element. - final Element usersElement = new Element("users"); + write(users, new StringBuilderWriter(builder)); + } + + /** + * Write a Collection of User's to an OutputStream. + * + * @param users Collection of User's to write. + * @param out OutputStream to write to. + * @throws IOException if the writer fails to write. + * @throws WriterException + */ + public <T extends Principal> void write(Collection<User<T>> users, OutputStream out) + throws IOException, WriterException + { + OutputStreamWriter outWriter; + try + { + outWriter = new OutputStreamWriter(out, "UTF-8"); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException("UTF-8 encoding not supported", e); + } + write(users, new BufferedWriter(outWriter)); + } - for (final Map.Entry<String, PersonalDetails> entry : users.entrySet()) + /** + * Write a Collection of Users to a Writer. + * + * @param users Users to write. + * @param writer Writer to write to. + * @throws IOException if the writer fails to write. + * @throws WriterException + */ + public <T extends Principal> void write(Collection<User<T>> users, Writer writer) + throws IOException, WriterException + { + if (users == null) { - final Element userEntryElement = new Element("user"); - final Element firstNameElement = new Element("firstName"); - final Element lastNameElement = new Element("lastName"); + throw new WriterException("null users"); + } - userEntryElement.setAttribute("id", entry.getKey()); + write(getElement(users), writer); + } - firstNameElement.setText(entry.getValue().getFirstName()); - userEntryElement.addContent(firstNameElement); - lastNameElement.setText(entry.getValue().getLastName()); - userEntryElement.addContent(lastNameElement); + /** + * Get a JDOM element from a Collection of User objects. + * + * @param users Collection of User's to write. + * @return A JDOM Group list representation. + * @throws WriterException + */ + protected final <T extends Principal> Element getElement(Collection<User<T>> users) + throws WriterException + { + Element usersElement = new Element("users"); - usersElement.addContent(userEntryElement); + for (User<T> user : users) + { + usersElement.addContent(getElement(user)); } - final XMLOutputter output = new XMLOutputter(); - - output.setFormat(Format.getPrettyFormat()); - output.output(new Document(usersElement), writer); + return usersElement; } + } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java index 0accd9da28358bd855b9b73c0d10ce8265044872..c3cbae20aaa3f64e875560f521b1776366f98aca 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java @@ -83,16 +83,15 @@ import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.security.Principal; -import java.util.List; /** * Class to read a XML representation of a User to a User object. */ -public class UserReader +public class UserReader extends AbstractReaderWriter { /** * Construct a User from an XML String source. - * + * * @param xml String of the XML. * @return User User. * @throws ReaderException @@ -111,7 +110,7 @@ public class UserReader /** * Construct a User from a InputStream. - * + * * @param in InputStream. * @return User User. * @throws java.io.IOException @@ -137,7 +136,7 @@ public class UserReader /** * Construct a User from a Reader. - * + * * @param reader Reader. * @return User User. * @throws ReaderException @@ -166,58 +165,7 @@ public class UserReader // Root element and namespace of the Document Element root = document.getRootElement(); - return parseUser(root); - } - - public static User<Principal> parseUser(Element userElement) - throws ReaderException - { - // userID element of the User element - Element userIDElement = userElement.getChild("userID"); - if (userIDElement == null) - { - String error = "userID element not found in user element"; - throw new ReaderException(error); - } - - // identity element of the userID element - Element userIDIdentityElement = userIDElement.getChild("identity"); - if (userIDIdentityElement == null) - { - String error = "identity element not found in userID element"; - throw new ReaderException(error); - } - - IdentityReader identityReader = new IdentityReader(); - Principal userID = identityReader.read(userIDIdentityElement); - - User<Principal> user = new User<Principal>(userID); - - // identities - Element identitiesElement = userElement.getChild("identities"); - if (identitiesElement != null) - { - List<Element> identityElements = identitiesElement.getChildren("identity"); - for (Element identityElement : identityElements) - { - user.getIdentities().add(identityReader.read(identityElement)); - } - - } - - // details - Element detailsElement = userElement.getChild("details"); - if (detailsElement != null) - { - UserDetailsReader userDetailsReader = new UserDetailsReader(); - List<Element> userDetailsElements = detailsElement.getChildren("userDetails"); - for (Element userDetailsElement : userDetailsElements) - { - user.details.add(userDetailsReader.read(userDetailsElement)); - } - } - - return user; + return getUser(root); } } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java index e44eba1e4dc37fe8f9fa81b0f7b9629bf271e65e..dffef3e5e7f65d9fc3217f18f19f3a641189a095 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java @@ -69,9 +69,12 @@ package ca.nrc.cadc.ac.xml; import ca.nrc.cadc.ac.ReaderException; -import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.UserRequest; import ca.nrc.cadc.xml.XmlUtil; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; + import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -80,14 +83,10 @@ import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.security.Principal; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.JDOMException; - /** * Class to read a XML representation of a UserRequest to a UserRequest object. */ -public class UserRequestReader +public class UserRequestReader extends AbstractReaderWriter { /** * Construct a UserRequest from an XML String source. @@ -164,31 +163,7 @@ public class UserRequestReader // Root element and namespace of the Document Element root = document.getRootElement(); - return parseUserRequest(root); + return getUserRequest(root); } - protected static UserRequest<Principal> parseUserRequest( - Element userRequestElement) - throws ReaderException - { - // user element of the UserRequest element - Element userElement = userRequestElement.getChild("user"); - if (userElement == null) - { - String error = "user element not found in userRequest element"; - throw new ReaderException(error); - } - User<Principal> user = ca.nrc.cadc.ac.xml.UserReader.parseUser(userElement); - - // password element of the userRequest element - Element passwordElement = userRequestElement.getChild("password"); - if (passwordElement == null) - { - String error = "password element not found in userRequest element"; - throw new ReaderException(error); - } - String password = passwordElement.getText(); - - return new UserRequest<Principal>(user, password.toCharArray()); - } } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestWriter.java index fe0ffee96a6fbbd1a9e9a70f77d575dfbdf0a629..e87840d129da63713cd78b9b5107413dec04404e 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestWriter.java @@ -72,10 +72,6 @@ package ca.nrc.cadc.ac.xml; import ca.nrc.cadc.ac.UserRequest; import ca.nrc.cadc.ac.WriterException; import ca.nrc.cadc.util.StringBuilderWriter; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.output.Format; -import org.jdom2.output.XMLOutputter; import java.io.IOException; import java.io.Writer; @@ -84,7 +80,7 @@ import java.security.Principal; /** * Class to write a XML representation of a UserRequest object. */ -public class UserRequestWriter +public class UserRequestWriter extends AbstractReaderWriter { /** * Write a UserRequest to a StringBuilder. @@ -94,7 +90,7 @@ public class UserRequestWriter * @throws java.io.IOException if the writer fails to write. * @throws WriterException */ - public void write(UserRequest<? extends Principal> userRequest, StringBuilder builder) + public <T extends Principal> void write(UserRequest<T> userRequest, StringBuilder builder) throws IOException, WriterException { write(userRequest, new StringBuilderWriter(builder)); @@ -108,7 +104,7 @@ public class UserRequestWriter * @throws IOException if the writer fails to write. * @throws WriterException */ - public static void write(UserRequest<? extends Principal> userRequest, Writer writer) + public <T extends Principal> void write(UserRequest<T> userRequest, Writer writer) throws IOException, WriterException { if (userRequest == null) @@ -116,46 +112,7 @@ public class UserRequestWriter throw new WriterException("null UserRequest"); } - write(getUserRequestElement(userRequest), writer); + write(getElement(userRequest), writer); } - /** - * Build the UserRequest element. - * - * @param userRequest UserRequest. - * @return member Element. - * @throws WriterException - */ - public static Element getUserRequestElement(UserRequest<? extends Principal> userRequest) - throws WriterException - { - // Create the userRequest Element. - Element userRequestElement = new Element("userRequest"); - - // user element - Element userElement = UserWriter.getUserElement(userRequest.getUser()); - userRequestElement.addContent(userElement); - - // password element - Element passwordElement = new Element("password"); - passwordElement.setText(String.valueOf(userRequest.getPassword())); - userRequestElement.addContent(passwordElement); - - return userRequestElement; - } - - /** - * Write to root Element to a writer. - * - * @param root Root Element to write. - * @param writer Writer to write to. - * @throws IOException if the writer fails to write. - */ - private static void write(Element root, Writer writer) - throws IOException - { - XMLOutputter outputter = new XMLOutputter(); - outputter.setFormat(Format.getPrettyFormat()); - outputter.output(new Document(root), writer); - } } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserWriter.java index 86327ad8f27e7e77bfaa4b17291fe4d8830e2fbf..7df99cbb6e0d284eb1c8f5df5fde0995f5398fc2 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserWriter.java @@ -69,13 +69,8 @@ package ca.nrc.cadc.ac.xml; import ca.nrc.cadc.ac.User; -import ca.nrc.cadc.ac.UserDetails; import ca.nrc.cadc.ac.WriterException; import ca.nrc.cadc.util.StringBuilderWriter; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.output.Format; -import org.jdom2.output.XMLOutputter; import java.io.BufferedWriter; import java.io.IOException; @@ -84,12 +79,11 @@ import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.security.Principal; -import java.util.Set; /** * Class to write a XML representation of a User object. */ -public class UserWriter +public class UserWriter extends AbstractReaderWriter { /** * Write a User to a StringBuilder. @@ -99,7 +93,7 @@ public class UserWriter * @throws java.io.IOException if the writer fails to write. * @throws WriterException */ - public void write(User<? extends Principal> user, StringBuilder builder) + public <T extends Principal> void write(User<T> user, StringBuilder builder) throws IOException, WriterException { write(user, new StringBuilderWriter(builder)); @@ -113,7 +107,7 @@ public class UserWriter * @throws IOException if the writer fails to write. * @throws WriterException */ - public void write(User<? extends Principal> user, OutputStream out) + public <T extends Principal> void write(User<T> user, OutputStream out) throws IOException, WriterException { OutputStreamWriter outWriter; @@ -136,7 +130,7 @@ public class UserWriter * @throws IOException if the writer fails to write. * @throws WriterException */ - public void write(User<? extends Principal> user, Writer writer) + public <T extends Principal> void write(User<T> user, Writer writer) throws IOException, WriterException { if (user == null) @@ -144,69 +138,7 @@ public class UserWriter throw new WriterException("null User"); } - write(getUserElement(user), writer); - } - - /** - * Build the member Element of a User. - * - * @param user User. - * @return member Element. - * @throws WriterException - */ - public static Element getUserElement(User<? extends Principal> user) - throws WriterException - { - // Create the user Element. - Element userElement = new Element("user"); - - // userID element - IdentityWriter identityWriter = new IdentityWriter(); - Element userIDElement = new Element("userID"); - userIDElement.addContent(identityWriter.write(user.getUserID())); - userElement.addContent(userIDElement); - - // identities - Set<Principal> identities = user.getIdentities(); - if (!identities.isEmpty()) - { - Element identitiesElement = new Element("identities"); - for (Principal identity : identities) - { - identitiesElement.addContent(identityWriter.write(identity)); - } - userElement.addContent(identitiesElement); - } - - // details - if (!user.details.isEmpty()) - { - UserDetailsWriter userDetailsWriter = new UserDetailsWriter(); - Element detailsElement = new Element("details"); - Set<UserDetails> userDetails = user.details; - for (UserDetails userDetail : userDetails) - { - detailsElement.addContent(userDetailsWriter.write(userDetail)); - } - userElement.addContent(detailsElement); - } - - return userElement; - } - - /** - * Write to root Element to a writer. - * - * @param root Root Element to write. - * @param writer Writer to write to. - * @throws IOException if the writer fails to write. - */ - private static void write(Element root, Writer writer) - throws IOException - { - XMLOutputter outputter = new XMLOutputter(); - outputter.setFormat(Format.getPrettyFormat()); - outputter.output(new Document(root), writer); + write(getElement(user), writer); } } diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/GMSClientTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/GMSClientTest.java index 2da9b1e8948109ca172b198bf0e96965b54b6a7e..d3eade86bb389c2368c7c6c16082f2f0a197b18c 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/GMSClientTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/GMSClientTest.java @@ -92,20 +92,20 @@ import ca.nrc.cadc.util.Log4jInit; import org.junit.Assert; import org.junit.Test; + import static org.easymock.EasyMock.*; public class GMSClientTest { - + private static final Logger log = Logger.getLogger(GMSClientTest.class); - + public GMSClientTest() { Log4jInit.setLevel("ca.nrc.cadc.ac", Level.DEBUG); } - @Test public void testGetDisplayUsers() throws Exception { @@ -127,18 +127,16 @@ public class GMSClientTest expectLastCall().once(); expect(mockHTTPDownload.getThrowable()).andReturn(null).once(); - expect(mockHTTPDownload.getContentLength()).andReturn(88l).once(); expect(mockHTTPDownload.getContentType()).andReturn( "application/json").once(); replay(mockHTTPDownload); - testSubject.getDisplayUsers(); - verify(mockHTTPDownload); } + @Test public void testUserIsSubject() { @@ -148,7 +146,7 @@ public class GMSClientTest HttpPrincipal userID = new HttpPrincipal("test"); HttpPrincipal userID2 = new HttpPrincipal("test2"); subject.getPrincipals().add(userID); - + RegistryClient regClient = new RegistryClient(); URL baseURL = regClient.getServiceURL(new URI(AC.GMS_SERVICE_URI), "https"); @@ -159,10 +157,10 @@ public class GMSClientTest Assert.assertFalse(client.userIsSubject(null, subject)); Assert.assertFalse(client.userIsSubject(userID2, subject)); Assert.assertTrue(client.userIsSubject(userID, subject)); - + HttpPrincipal userID3 = new HttpPrincipal("test3"); subject.getPrincipals().add(userID3); - + Assert.assertTrue(client.userIsSubject(userID, subject)); Assert.assertFalse(client.userIsSubject(userID2, subject)); Assert.assertTrue(client.userIsSubject(userID3, subject)); @@ -173,7 +171,7 @@ public class GMSClientTest Assert.fail("Unexpected exception: " + t.getMessage()); } } - + @Test public void testGroupCaching() { @@ -182,86 +180,108 @@ public class GMSClientTest Subject subject = new Subject(); final HttpPrincipal test1UserID = new HttpPrincipal("test"); subject.getPrincipals().add(test1UserID); - + RegistryClient regClient = new RegistryClient(); URL baseURL = regClient.getServiceURL(new URI(AC.GMS_SERVICE_URI), - "https"); + "https"); final GMSClient client = new GMSClient(baseURL.toString()); Subject.doAs(subject, new PrivilegedExceptionAction<Object>() + { + @Override + public Object run() throws Exception { - @Override - public Object run() throws Exception - { - - List<Group> initial = client.getCachedGroups(test1UserID, Role.MEMBER); - Assert.assertNull("Cache should be null", initial); - - List<Group> expected = new ArrayList<Group>(); - Group group1 = new Group("1"); - Group group2 = new Group("2"); - expected.add(group1); - expected.add(group2); - - client.setCachedGroups(test1UserID, expected, Role.MEMBER); - - List<Group> actual = client.getCachedGroups(test1UserID, Role.MEMBER); - Assert.assertEquals("Wrong cached groups", expected, actual); - - // check against another role - actual = client.getCachedGroups(test1UserID, Role.OWNER); - Assert.assertNull("Cache should be null", actual); - - // check against another userid - final HttpPrincipal anotherUserID = new HttpPrincipal("anotheruser"); - actual = client.getCachedGroups(anotherUserID, Role.MEMBER); - Assert.assertNull("Cache should be null", actual); - - return null; - } - }); - + + List<Group> initial = client + .getCachedGroups(test1UserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", initial); + + // add single group as isMember might do + Group group0 = new Group("0"); + client.addCachedGroup(test1UserID, group0, Role.MEMBER); + List<Group> actual = client + .getCachedGroups(test1UserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", actual); + + Group g = client + .getCachedGroup(test1UserID, "0", Role.MEMBER); + Assert.assertNotNull("cached group from incomplete cache", g); + + // add all groups like getMemberships might do + List<Group> expected = new ArrayList<Group>(); + Group group1 = new Group("1"); + Group group2 = new Group("2"); + expected.add(group0); + expected.add(group1); + expected.add(group2); + + client.setCachedGroups(test1UserID, expected, Role.MEMBER); + + actual = client + .getCachedGroups(test1UserID, Role.MEMBER, true); + Assert.assertEquals("Wrong cached groups", expected, actual); + + // check against another role + actual = client + .getCachedGroups(test1UserID, Role.OWNER, true); + Assert.assertNull("Cache should be null", actual); + + // check against another userid + final HttpPrincipal anotherUserID = new HttpPrincipal("anotheruser"); + actual = client + .getCachedGroups(anotherUserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", actual); + + return null; + } + }); + subject = new Subject(); final HttpPrincipal test2UserID = new HttpPrincipal("test2"); subject.getPrincipals().add(test2UserID); - + // do the same but as a different user Subject.doAs(subject, new PrivilegedExceptionAction<Object>() - { - @Override - public Object run() throws Exception - { - - List<Group> initial = client.getCachedGroups(test2UserID, Role.MEMBER); - Assert.assertNull("Cache should be null", initial); - - List<Group> expected = new ArrayList<Group>(); - Group group1 = new Group("1"); - Group group2 = new Group("2"); - expected.add(group1); - expected.add(group2); - - client.setCachedGroups(test2UserID, expected, Role.MEMBER); - - List<Group> actual = client.getCachedGroups(test2UserID, Role.MEMBER); - Assert.assertEquals("Wrong cached groups", expected, actual); - - // check against another role - actual = client.getCachedGroups(test2UserID, Role.OWNER); - Assert.assertNull("Cache should be null", actual); - - // check against another userid - final HttpPrincipal anotherUserID = new HttpPrincipal("anotheruser"); - actual = client.getCachedGroups(anotherUserID, Role.MEMBER); - Assert.assertNull("Cache should be null", actual); - - return null; - } - }); + { + @Override + public Object run() throws Exception + { + + List<Group> initial = client + .getCachedGroups(test2UserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", initial); + + List<Group> expected = new ArrayList<Group>(); + Group group1 = new Group("1"); + Group group2 = new Group("2"); + expected.add(group1); + expected.add(group2); + + client.setCachedGroups(test2UserID, expected, Role.MEMBER); + + List<Group> actual = client + .getCachedGroups(test2UserID, Role.MEMBER, true); + Assert.assertEquals("Wrong cached groups", expected, actual); + + // check against another role + actual = client + .getCachedGroups(test2UserID, Role.OWNER, true); + Assert.assertNull("Cache should be null", actual); + + // check against another userid + final HttpPrincipal anotherUserID = new HttpPrincipal("anotheruser"); + actual = client + .getCachedGroups(anotherUserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", actual); + + return null; + } + }); // do the same without a subject - List<Group> initial = client.getCachedGroups(test1UserID, Role.MEMBER); + List<Group> initial = client + .getCachedGroups(test1UserID, Role.MEMBER, true); Assert.assertNull("Cache should be null", initial); List<Group> newgroups = new ArrayList<Group>(); @@ -272,7 +292,8 @@ public class GMSClientTest client.setCachedGroups(test1UserID, newgroups, Role.MEMBER); - List<Group> actual = client.getCachedGroups(test1UserID, Role.MEMBER); + List<Group> actual = client + .getCachedGroups(test1UserID, Role.MEMBER, true); Assert.assertNull("Cache should still be null", actual); } catch (Throwable t) diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserListReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserListReaderWriterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..44b3922a64b8ba20c6270cece1d1717832e52f8c --- /dev/null +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserListReaderWriterTest.java @@ -0,0 +1,95 @@ +package ca.nrc.cadc.ac.json; + +import ca.nrc.cadc.ac.PersonalDetails; +import ca.nrc.cadc.ac.PosixDetails; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.WriterException; +import ca.nrc.cadc.auth.HttpPrincipal; +import ca.nrc.cadc.auth.NumericPrincipal; +import org.apache.log4j.Logger; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.security.Principal; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +/** + * JsonUserListReaderWriterTest TODO describe class + */ +public class JsonUserListReaderWriterTest +{ + private static Logger log = Logger.getLogger(JsonUserListReaderWriterTest.class); + + @Test + public void testReaderExceptions() + throws Exception + { + try + { + String s = null; + JsonUserListReader reader = new JsonUserListReader(); + List<User<Principal>> u = reader.read(s); + fail("null String should throw IllegalArgumentException"); + } + catch (IllegalArgumentException e) {} + + try + { + InputStream in = null; + JsonUserListReader reader = new JsonUserListReader(); + List<User<Principal>> u = reader.read(in); + fail("null InputStream should throw IOException"); + } + catch (IOException e) {} + + try + { + Reader r = null; + JsonUserListReader reader = new JsonUserListReader(); + List<User<Principal>> u = reader.read(r); + fail("null Reader should throw IllegalArgumentException"); + } + catch (IllegalArgumentException e) {} + } + + @Test + public void testWriterExceptions() + throws Exception + { + try + { + JsonUserWriter writer = new JsonUserWriter(); + writer.write(null, new StringBuilder()); + fail("null User should throw WriterException"); + } + catch (WriterException e) {} + } + + @Test + public void testReadWrite() + throws Exception + { + User<Principal> expected = new User<Principal>(new HttpPrincipal("foo")); + expected.getIdentities().add(new NumericPrincipal(123l)); + expected.details.add(new PersonalDetails("firstname", "lastname")); + expected.details.add(new PosixDetails(123l, 456l, "foo")); + + StringBuilder json = new StringBuilder(); + JsonUserWriter writer = new JsonUserWriter(); + writer.write(expected, json); + assertFalse(json.toString().isEmpty()); + + JsonUserReader reader = new JsonUserReader(); + User<Principal> actual = reader.read(json.toString()); + assertNotNull(actual); + assertEquals(expected, actual); + } + +} diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserReaderWriterTest.java index dccefe894c2a5b2a3addf25e226cee910061ea74..7de85967189e9397195f80ab03f012b5c8c03d4c 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserReaderWriterTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserReaderWriterTest.java @@ -103,7 +103,7 @@ public class JsonUserReaderWriterTest { String s = null; JsonUserReader reader = new JsonUserReader(); - User<? extends Principal> u = reader.read(s); + User<Principal> u = reader.read(s); fail("null String should throw IllegalArgumentException"); } catch (IllegalArgumentException e) {} @@ -112,7 +112,7 @@ public class JsonUserReaderWriterTest { InputStream in = null; JsonUserReader reader = new JsonUserReader(); - User<? extends Principal> u = reader.read(in); + User<Principal> u = reader.read(in); fail("null InputStream should throw IOException"); } catch (IOException e) {} @@ -121,7 +121,7 @@ public class JsonUserReaderWriterTest { Reader r = null; JsonUserReader reader = new JsonUserReader(); - User<? extends Principal> u = reader.read(r); + User<Principal> u = reader.read(r); fail("null Reader should throw IllegalArgumentException"); } catch (IllegalArgumentException e) {} @@ -144,7 +144,7 @@ public class JsonUserReaderWriterTest public void testReadWrite() throws Exception { - User<Principal> expected = new User<Principal>(new HttpPrincipal("foo")); + User<? extends Principal> expected = new User<Principal>(new HttpPrincipal("foo")); expected.getIdentities().add(new NumericPrincipal(123l)); expected.details.add(new PersonalDetails("firstname", "lastname")); expected.details.add(new PosixDetails(123l, 456l, "foo")); diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupsReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupListReaderWriterTest.java similarity index 98% rename from projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupsReaderWriterTest.java rename to projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupListReaderWriterTest.java index 0281eaf87cf1456c0d5781fc187012d3d5c7caa2..a18c13b3c093922e9da73774004296729eb0cf70 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupsReaderWriterTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupListReaderWriterTest.java @@ -88,9 +88,9 @@ import static org.junit.Assert.fail; * * @author jburke */ -public class GroupsReaderWriterTest +public class GroupListReaderWriterTest { - private static Logger log = Logger.getLogger(GroupsReaderWriterTest.class); + private static Logger log = Logger.getLogger(GroupListReaderWriterTest.class); @Test public void testReaderExceptions() diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupPropertyReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupPropertyReaderWriterTest.java index 7075c9e1f649ca01beb98529bfba564f441582b1..0a2598471ee0c64f290d0bee13e48852d1a74f0e 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupPropertyReaderWriterTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupPropertyReaderWriterTest.java @@ -83,7 +83,7 @@ import static org.junit.Assert.*; * * @author jburke */ -public class GroupPropertyReaderWriterTest +public class GroupPropertyReaderWriterTest extends AbstractReaderWriter { private static Logger log = Logger.getLogger(GroupPropertyReaderWriterTest.class); @@ -100,7 +100,7 @@ public class GroupPropertyReaderWriterTest Element element = null; try { - GroupProperty gp = GroupPropertyReader.read(element); + GroupProperty gp = getGroupProperty(element); fail("null element should throw ReaderException"); } catch (ReaderException e) {} @@ -108,7 +108,7 @@ public class GroupPropertyReaderWriterTest element = new Element("foo"); try { - GroupProperty gp = GroupPropertyReader.read(element); + GroupProperty gp = getGroupProperty(element); fail("element not named 'property' should throw ReaderException"); } catch (ReaderException e) {} @@ -116,7 +116,7 @@ public class GroupPropertyReaderWriterTest element = new Element("property"); try { - GroupProperty gp = GroupPropertyReader.read(element); + GroupProperty gp = getGroupProperty(element); fail("element without 'key' attribute should throw ReaderException"); } catch (ReaderException e) {} @@ -124,7 +124,7 @@ public class GroupPropertyReaderWriterTest element.setAttribute("key", "foo"); try { - GroupProperty gp = GroupPropertyReader.read(element); + GroupProperty gp = getGroupProperty(element); fail("element without 'type' attribute should throw ReaderException"); } catch (ReaderException e) {} @@ -132,7 +132,7 @@ public class GroupPropertyReaderWriterTest element.setAttribute("type", "Double"); try { - GroupProperty gp = GroupPropertyReader.read(element); + GroupProperty gp = getGroupProperty(element); fail("Unsupported 'type' should throw ReaderException"); } catch (ReaderException e) {} @@ -144,15 +144,16 @@ public class GroupPropertyReaderWriterTest { try { - Element element = GroupPropertyWriter.write(null); + GroupProperty groupProperty = null; + Element element = getElement(groupProperty); fail("null GroupProperty should throw WriterException"); } catch (WriterException e) {} - GroupProperty gp = new GroupProperty("key", new Double(1.0), true); + GroupProperty groupProperty = new GroupProperty("key", new Double(1.0), true); try { - Element element = GroupPropertyWriter.write(gp); + Element element = getElement(groupProperty); fail("Unsupported GroupProperty type should throw IllegalArgumentException"); } catch (IllegalArgumentException e) {} @@ -164,20 +165,20 @@ public class GroupPropertyReaderWriterTest { // String type GroupProperty expected = new GroupProperty("key", "value", true); - Element element = GroupPropertyWriter.write(expected); + Element element = getElement(expected); assertNotNull(element); - GroupProperty actual = GroupPropertyReader.read(element); + GroupProperty actual = getGroupProperty(element); assertNotNull(actual); assertEquals(expected, actual); // Integer tuype expected = new GroupProperty("key", new Integer(1), false); - element = GroupPropertyWriter.write(expected); + element = getElement(expected); assertNotNull(element); - actual = GroupPropertyReader.read(element); + actual = getGroupProperty(element); assertNotNull(actual); assertEquals(expected, actual); diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/IdentityReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/IdentityReaderWriterTest.java index 58918c8d17fc6d9e056305e9246f4e3ce9e0c121..8bb1d674b19e633333e8e331c9add568e585a3b5 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/IdentityReaderWriterTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/IdentityReaderWriterTest.java @@ -89,7 +89,7 @@ import static org.junit.Assert.fail; * * @author jburke */ -public class IdentityReaderWriterTest +public class IdentityReaderWriterTest extends AbstractReaderWriter { private static Logger log = Logger.getLogger(IdentityReaderWriterTest.class); @@ -100,7 +100,7 @@ public class IdentityReaderWriterTest Element element = null; try { - Principal p = IdentityReader.read(element); + Principal p = getPrincipal(element); fail("null element should throw ReaderException"); } catch (ReaderException e) {} @@ -108,7 +108,7 @@ public class IdentityReaderWriterTest element = new Element("foo"); try { - Principal p = IdentityReader.read(element); + Principal p = getPrincipal(element); fail("element not named 'identity' should throw ReaderException"); } catch (ReaderException e) {} @@ -116,7 +116,7 @@ public class IdentityReaderWriterTest element = new Element("identity"); try { - Principal p = IdentityReader.read(element); + Principal p = getPrincipal(element); fail("element without 'type' attribute should throw ReaderException"); } catch (ReaderException e) {} @@ -124,7 +124,7 @@ public class IdentityReaderWriterTest element.setAttribute("type", "foo"); try { - Principal p = IdentityReader.read(element); + Principal p = getPrincipal(element); fail("element with unknown 'type' attribute should throw ReaderException"); } catch (ReaderException e) {} @@ -134,17 +134,18 @@ public class IdentityReaderWriterTest public void testWriterExceptions() throws Exception { + Principal p = null; try { - Element element = IdentityWriter.write(null); + Element element = getElement(p); fail("null Identity should throw WriterException"); } catch (WriterException e) {} - Principal p = new JMXPrincipal("foo"); + p = new JMXPrincipal("foo"); try { - Element element = IdentityWriter.write(p); + Element element = getElement(p); fail("Unsupported Principal type should throw IllegalArgumentException"); } catch (IllegalArgumentException e) {} @@ -156,40 +157,40 @@ public class IdentityReaderWriterTest { // X500 Principal expected = new X500Principal("cn=foo,o=bar"); - Element element = IdentityWriter.write(expected); + Element element = getElement(expected); assertNotNull(element); - Principal actual = IdentityReader.read(element); + Principal actual = getPrincipal(element); assertNotNull(actual); assertEquals(expected, actual); - - // UID + + // CADC expected = new NumericPrincipal(123l); - element = IdentityWriter.write(expected); + element = getElement(expected); assertNotNull(element); - actual = IdentityReader.read(element); + actual = getPrincipal(element); assertNotNull(actual); assertEquals(expected, actual); // OpenID expected = new OpenIdPrincipal("bar"); - element = IdentityWriter.write(expected); + element = getElement(expected); assertNotNull(element); - actual = IdentityReader.read(element); + actual = getPrincipal(element); assertNotNull(actual); assertEquals(expected, actual); // HTTP expected = new HttpPrincipal("baz"); - element = IdentityWriter.write(expected); + element = getElement(expected); assertNotNull(element); - actual = IdentityReader.read(element); + actual = getPrincipal(element); assertNotNull(actual); assertEquals(expected, actual); diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserDetailsReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserDetailsReaderWriterTest.java index a7badabbaa4ce6e745946e879b6d4636b5141d65..4a6304dd4e31fa88ccb2fd34f53c46164972ce27 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserDetailsReaderWriterTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserDetailsReaderWriterTest.java @@ -85,7 +85,7 @@ import static org.junit.Assert.fail; * * @author jburke */ -public class UserDetailsReaderWriterTest +public class UserDetailsReaderWriterTest extends AbstractReaderWriter { private static Logger log = Logger.getLogger(UserDetailsReaderWriterTest.class); @@ -96,7 +96,7 @@ public class UserDetailsReaderWriterTest Element element = null; try { - UserDetails ud = UserDetailsReader.read(element); + UserDetails ud = getUserDetails(element); fail("null element should throw ReaderException"); } catch (ReaderException e) {} @@ -104,7 +104,7 @@ public class UserDetailsReaderWriterTest element = new Element("foo"); try { - UserDetails ud = UserDetailsReader.read(element); + UserDetails ud = getUserDetails(element); fail("element not named 'userDetails' should throw ReaderException"); } catch (ReaderException e) {} @@ -112,7 +112,7 @@ public class UserDetailsReaderWriterTest element = new Element(UserDetails.NAME); try { - UserDetails ud = UserDetailsReader.read(element); + UserDetails ud = getUserDetails(element); fail("element without 'type' attribute should throw ReaderException"); } catch (ReaderException e) {} @@ -120,7 +120,7 @@ public class UserDetailsReaderWriterTest element.setAttribute("type", "foo"); try { - UserDetails ud = UserDetailsReader.read(element); + UserDetails ud = getUserDetails(element); fail("element with unknown 'type' attribute should throw ReaderException"); } catch (ReaderException e) {} @@ -132,7 +132,8 @@ public class UserDetailsReaderWriterTest { try { - Element element = UserDetailsWriter.write(null); + UserDetails ud = null; + Element element = getElement(ud); fail("null UserDetails should throw WriterException"); } catch (WriterException e) {} @@ -148,10 +149,10 @@ public class UserDetailsReaderWriterTest expected.country = "country"; expected.email = "email"; expected.institute = "institute"; - Element element = UserDetailsWriter.write(expected); + Element element = getElement(expected); assertNotNull(element); - PersonalDetails actual = (PersonalDetails) UserDetailsReader.read(element); + PersonalDetails actual = (PersonalDetails) getUserDetails(element); assertNotNull(actual); assertEquals(expected, actual); assertEquals(expected.address, actual.address); @@ -166,10 +167,10 @@ public class UserDetailsReaderWriterTest throws Exception { UserDetails expected = new PosixDetails(123l, 456, "/dev/null"); - Element element = UserDetailsWriter.write(expected); + Element element = getElement(expected); assertNotNull(element); - UserDetails actual = UserDetailsReader.read(element); + UserDetails actual = getUserDetails(element); assertNotNull(actual); assertEquals(expected, actual); } diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserListReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserListReaderWriterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ddca58a35b80651b347fb533e575b9f825a19b40 --- /dev/null +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserListReaderWriterTest.java @@ -0,0 +1,92 @@ +package ca.nrc.cadc.ac.xml; + +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.ac.WriterException; +import ca.nrc.cadc.auth.HttpPrincipal; +import org.apache.log4j.Logger; +import org.junit.Test; + +import javax.security.auth.x500.X500Principal; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +public class UserListReaderWriterTest +{ + private static Logger log = Logger.getLogger(UserListReaderWriterTest.class); + + @Test + public void testReaderExceptions() + throws Exception + { + try + { + String s = null; + UserListReader UserListReader = new UserListReader(); + List<User<Principal>> u = UserListReader.read(s); + fail("null String should throw IllegalArgumentException"); + } + catch (IllegalArgumentException e) {} + + try + { + InputStream in = null; + UserListReader userListReader = new UserListReader(); + List<User<Principal>> u = userListReader.read(in); + fail("null InputStream should throw IOException"); + } + catch (IOException e) {} + + try + { + Reader r = null; + UserListReader userListReader = new UserListReader(); + List<User<Principal>> u = userListReader.read(r); + fail("null element should throw ReaderException"); + } + catch (IllegalArgumentException e) {} + } + + @Test + public void testWriterExceptions() + throws Exception + { + try + { + UserListWriter userListWriter = new UserListWriter(); + userListWriter.write(null, new StringBuilder()); + fail("null User should throw WriterException"); + } + catch (WriterException e) {} + } + + @Test + public void testMinimalReadWrite() + throws Exception + { + List<User<Principal>> expected = new ArrayList<User<Principal>>(); + expected.add(new User<Principal>(new HttpPrincipal("foo"))); + expected.add(new User<Principal>(new X500Principal("cn=foo,o=bar"))); + + StringBuilder xml = new StringBuilder(); + UserListWriter userListWriter = new UserListWriter(); + userListWriter.write(expected, xml); + assertFalse(xml.toString().isEmpty()); + + UserListReader userListReader = new UserListReader(); + List<User<Principal>> actual = userListReader.read(xml.toString()); + assertNotNull(actual); + assertEquals(expected.size(), actual.size()); + assertEquals(expected.get(0), actual.get(0)); + assertEquals(expected.get(1), actual.get(1)); + } + +} \ No newline at end of file diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserReaderWriterTest.java index 4e8d9f2b066ef053fb815854ee2a3ce03c66e30c..c420e7ca34c5bf47b8290238d9011d4f73c5a6d5 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserReaderWriterTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserReaderWriterTest.java @@ -102,7 +102,7 @@ public class UserReaderWriterTest { String s = null; UserReader userReader = new UserReader(); - User<? extends Principal> u = userReader.read(s); + User<Principal> u = userReader.read(s); fail("null String should throw IllegalArgumentException"); } catch (IllegalArgumentException e) {} @@ -111,7 +111,7 @@ public class UserReaderWriterTest { InputStream in = null; UserReader userReader = new UserReader(); - User<? extends Principal> u = userReader.read(in); + User<Principal> u = userReader.read(in); fail("null InputStream should throw IOException"); } catch (IOException e) {} @@ -120,7 +120,7 @@ public class UserReaderWriterTest { Reader r = null; UserReader userReader = new UserReader(); - User<? extends Principal> u = userReader.read(r); + User<Principal> u = userReader.read(r); fail("null Reader should throw IllegalArgumentException"); } catch (IllegalArgumentException e) {} @@ -143,7 +143,7 @@ public class UserReaderWriterTest public void testReadWrite() throws Exception { - User<? extends Principal> expected = new User<Principal>(new HttpPrincipal("foo")); + User<Principal> expected = new User<Principal>(new HttpPrincipal("foo")); expected.getIdentities().add(new NumericPrincipal(123l)); expected.details.add(new PersonalDetails("firstname", "lastname"));