diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java index 5f378f85a0..f1fd92bb9d 100644 --- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java @@ -104,6 +104,7 @@ import org.alfresco.service.cmr.repository.Path.Element; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.usage.ContentQuotaException; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.NamespaceService; @@ -157,6 +158,7 @@ public class NodesImpl implements Nodes private ContentService contentService; private ActionService actionService; private VersionService versionService; + private PersonService personService; // note: circular - Nodes/QuickShareLinks currently use each other (albeit for different methods) private QuickShareLinks quickShareLinks; @@ -186,6 +188,7 @@ public class NodesImpl implements Nodes this.contentService = sr.getContentService(); this.actionService = sr.getActionService(); this.versionService = sr.getVersionService(); + this.personService = sr.getPersonService(); if (defaultIgnoreTypesAndAspects != null) { @@ -1178,6 +1181,8 @@ public class NodesImpl implements Nodes } props.put(ContentModel.PROP_NAME, nodeName); + validatePropValues(props); + QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(nodeName)); try { @@ -1204,6 +1209,20 @@ public class NodesImpl implements Nodes } } + // special cases: additional validation of property values (if not done by underlying foundation services) + private void validatePropValues(Map props) + { + String newOwner = (String)props.get(ContentModel.PROP_OWNER); + if (newOwner != null) + { + // validate that user exists + if (! personService.personExists(newOwner)) + { + throw new InvalidArgumentException("Unknown owner: "+newOwner); + } + } + } + public Node updateNode(String nodeId, Node nodeInfo, Parameters parameters) { final NodeRef nodeRef = validateNode(nodeId); @@ -1334,6 +1353,8 @@ public class NodesImpl implements Nodes if (props.size() > 0) { + validatePropValues(props); + try { // update node properties - note: null will unset the specified property diff --git a/source/java/org/alfresco/rest/api/model/Node.java b/source/java/org/alfresco/rest/api/model/Node.java index 0ef0dd3c3e..2e6eba5000 100644 --- a/source/java/org/alfresco/rest/api/model/Node.java +++ b/source/java/org/alfresco/rest/api/model/Node.java @@ -41,6 +41,8 @@ import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; import org.apache.chemistry.opencmis.commons.data.PropertyData; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Concrete class carrying general information for alf_node data @@ -51,6 +53,8 @@ import org.apache.chemistry.opencmis.commons.data.PropertyData; */ public class Node implements Comparable { + private static final Log logger = LogFactory.getLog(Node.class); + protected NodeRef nodeRef; protected String name; @@ -123,14 +127,27 @@ public class Node implements Comparable } else { + PersonService.PersonInfo pInfo = null; try { - PersonService.PersonInfo pInfo = personService.getPerson(personService.getPerson(userName)); - userInfo = new UserInfo(userName, pInfo.getFirstName(), pInfo.getLastName()); + NodeRef pNodeRef = personService.getPerson(userName, false); + if (pNodeRef != null) + { + pInfo = personService.getPerson(pNodeRef); + } } catch (NoSuchPersonException nspe) { - // belts-and-braces (seen in dev/test env, eg. userName = Bobd58ba329-b702-41ee-a9ae-2b3c7029b5bc + // drop-through + } + + if (pInfo != null) + { + userInfo = new UserInfo(userName, pInfo.getFirstName(), pInfo.getLastName()); + } + else + { + logger.warn("Unknown person: "+userName); userInfo = new UserInfo(userName, userName, ""); } diff --git a/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java b/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java index 01d2fe5dda..745ff0701c 100644 --- a/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java @@ -97,7 +97,9 @@ import org.springframework.util.ResourceUtils; *
  • {@literal :/alfresco/api//public/alfresco/versions/1/nodes//children}
  • * * - * TODO replace most (all ?) usages of repoService test data setup code with actual FileFolder API calls (where appropriate) + * TODO + * - improve test 'fwk' to enable api tests to be run against remote repo (rather than embedded jetty) + * - requires replacement of non-remote calls (eg. repoService, siteService, permissionService) with calls to remote (preferably public) apis * * @author Jamal Kaabi-Mofrad * @author janv @@ -1806,6 +1808,118 @@ public class NodeApiTest extends AbstractBaseApiTest put("nodes", user1, fId, toJsonAsStringNonNull(fUpdate), null, 200); } + /** + * Tests update owner (file or folder) + *

    PUT:

    + * {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes/} + */ + @Test + public void testUpdateOwner() throws Exception + { + AuthenticationUtil.setFullyAuthenticatedUser(user1); + + String ownerProp = "cm:owner"; + + // create folder f1 + String folderName = "f1 "+System.currentTimeMillis(); + Folder folderResp = createFolder(user1, Nodes.PATH_SHARED, folderName); + String f1Id = folderResp.getId(); + + assertNull(user1, folderResp.getProperties()); // owner is implied + + // explicitly set owner to oneself + Map props = new HashMap<>(); + props.put(ownerProp, user1); + Folder fUpdate = new Folder(); + fUpdate.setProperties(props); + + HttpResponse response = put("nodes", user1, f1Id, toJsonAsStringNonNull(fUpdate), null, 200); + folderResp = jacksonUtil.parseEntry(response.getJsonResponse(), Folder.class); + + assertEquals(user1, ((Map)folderResp.getProperties().get(ownerProp)).get("id")); + + // create doc d1 + NodeRef f1Ref = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, f1Id); + String d1Name = "content1 " + System.currentTimeMillis(); + NodeRef d1Ref = repoService.createDocument(f1Ref, d1Name, "The quick brown fox jumps over the lazy dog."); + String d1Id = d1Ref.getId(); + + // get node info + response = getSingle(NodesEntityResource.class, user1, d1Id, null, 200); + Document documentResp = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class); + + assertNull(user1, documentResp.getProperties()); // owner is implied + + props = new HashMap<>(); + props.put(ownerProp, user1); + Document dUpdate = new Document(); + dUpdate.setProperties(props); + + response = put("nodes", user1, d1Id, toJsonAsStringNonNull(dUpdate), null, 200); + documentResp = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class); + + assertEquals(user1, ((Map)documentResp.getProperties().get(ownerProp)).get("id")); + + // -ve test - cannot set owner to a nonexistent user + + props = new HashMap<>(); + props.put(ownerProp, "unknownusernamedoesnotexist"); + dUpdate = new Document(); + dUpdate.setProperties(props); + + put("nodes", user1, d1Id, toJsonAsStringNonNull(dUpdate), null, 400); + + AuthenticationUtil.setFullyAuthenticatedUser(user2); + + response = getSingle("nodes", user1, d1Id, 200); + documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); + + assertEquals(user1, ((Map)documentResp.getProperties().get(ownerProp)).get("id")); + + // -ve test - cannot take/change ownership + + props = new HashMap<>(); + props.put(ownerProp, user2); + dUpdate = new Document(); + dUpdate.setProperties(props); + + put("nodes", user2, d1Id, toJsonAsStringNonNull(dUpdate), null, 403); + + props = new HashMap<>(); + props.put(ownerProp, user1); + dUpdate = new Document(); + dUpdate.setProperties(props); + + put("nodes", user2, d1Id, toJsonAsStringNonNull(dUpdate), null, 403); + + AuthenticationUtil.setFullyAuthenticatedUser(user1); + + props = new HashMap<>(); + props.put(ownerProp, user2); + dUpdate = new Document(); + dUpdate.setProperties(props); + + response = put("nodes", user1, d1Id, toJsonAsStringNonNull(dUpdate), null, 200); + documentResp = jacksonUtil.parseEntry(response.getJsonResponse(), Document.class); + + assertEquals(user2, ((Map)documentResp.getProperties().get(ownerProp)).get("id")); + + AuthenticationUtil.setFullyAuthenticatedUser(user2); + + response = getSingle("nodes", user2, d1Id, 200); + documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); + + assertEquals(user2, ((Map)documentResp.getProperties().get(ownerProp)).get("id")); + + // -ve test - user2 cannot delete the test folder/file - TODO is that expected ? + delete("nodes", user2, f1Id, 403); + + AuthenticationUtil.setFullyAuthenticatedUser(user1); + + delete("nodes", user1, f1Id, 204); + } + + /** * Tests update file content *

    PUT: