diff --git a/source/java/org/alfresco/rest/api/PublicApiDeclarativeRegistry.java b/source/java/org/alfresco/rest/api/PublicApiDeclarativeRegistry.java index ecabb4bec7..28d8178325 100644 --- a/source/java/org/alfresco/rest/api/PublicApiDeclarativeRegistry.java +++ b/source/java/org/alfresco/rest/api/PublicApiDeclarativeRegistry.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2017 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -23,14 +23,16 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.api; - +package org.alfresco.rest.api; + import java.io.IOException; import java.io.InputStream; import java.io.Serializable; -import java.util.*; - -import org.alfresco.rest.api.authentications.AuthenticationTicketsEntityResource; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Set; + import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.core.ResourceLocator; import org.alfresco.rest.framework.core.ResourceWithMetadata; @@ -43,24 +45,25 @@ import org.alfresco.rest.framework.resource.actions.interfaces.ResourceAction; import org.alfresco.rest.framework.tools.ApiAssistant; import org.apache.commons.lang.StringUtils; import org.springframework.extensions.webscripts.*; -import org.springframework.extensions.webscripts.Description.FormatStyle; -import org.springframework.extensions.webscripts.Description.RequiredAuthentication; -import org.springframework.extensions.webscripts.Description.RequiredTransaction; -import org.springframework.extensions.webscripts.Description.TransactionCapability; +import org.springframework.extensions.webscripts.Description.FormatStyle; +import org.springframework.extensions.webscripts.Description.RequiredAuthentication; +import org.springframework.extensions.webscripts.Description.RequiredTransaction; +import org.springframework.extensions.webscripts.Description.TransactionCapability; import org.springframework.http.HttpMethod; - + /** * * @author steveglover * @author janv + * @author Jamal Kaabi-Mofrad * @since PublicApi1.0 */ -public class PublicApiDeclarativeRegistry extends DeclarativeRegistry -{ - private WebScript getNetworksWebScript; - private WebScript getNetworkWebScript; - private Container container; - +public class PublicApiDeclarativeRegistry extends DeclarativeRegistry +{ + private WebScript getNetworksWebScript; + private WebScript getNetworkWebScript; + private Container container; + private ResourceLocator locator; public void setLocator(ResourceLocator locator) @@ -68,27 +71,27 @@ public class PublicApiDeclarativeRegistry extends DeclarativeRegistry this.locator = locator; } - public void setGetNetworksWebScript(WebScript getNetworksWebScript) - { - this.getNetworksWebScript = getNetworksWebScript; - } - - public void setGetNetworkWebScript(WebScript getNetworkWebScript) - { - this.getNetworkWebScript = getNetworkWebScript; - } - - public void setContainer(Container container) - { - super.setContainer(container); - this.container = container; - } - - /* (non-Javadoc) - * @see org.alfresco.web.scripts.Registry#findWebScript(java.lang.String, java.lang.String) - */ - public Match findWebScript(String method, String uri) - { + public void setGetNetworksWebScript(WebScript getNetworksWebScript) + { + this.getNetworksWebScript = getNetworksWebScript; + } + + public void setGetNetworkWebScript(WebScript getNetworkWebScript) + { + this.getNetworkWebScript = getNetworkWebScript; + } + + public void setContainer(Container container) + { + super.setContainer(container); + this.container = container; + } + + /* (non-Javadoc) + * @see org.alfresco.web.scripts.Registry#findWebScript(java.lang.String, java.lang.String) + */ + public Match findWebScript(String method, String uri) + { Match match; HttpMethod httpMethod = HttpMethod.valueOf(method); @@ -189,17 +192,46 @@ public class PublicApiDeclarativeRegistry extends DeclarativeRegistry else if (HttpMethod.POST.equals(httpMethod)) { match = super.findWebScript(method, uri); - if (match != null && uri.endsWith(AuthenticationTicketsEntityResource.COLLECTION_RESOURCE_NAME)) + if (match != null) { ResourceWithMetadata rwm = getResourceWithMetadataOrNull(match.getTemplateVars(), httpMethod); - if (rwm != null && AuthenticationTicketsEntityResource.class.equals(rwm.getResource().getClass())) + if (rwm != null) { Class resAction = null; - if (EntityResourceAction.Create.class.isAssignableFrom(rwm.getResource().getClass())) + Boolean noAuth = null; + switch (rwm.getMetaData().getType()) { - resAction = EntityResourceAction.Create.class; + case ENTITY: + if (EntityResourceAction.Create.class.isAssignableFrom(rwm.getResource().getClass())) + { + resAction = EntityResourceAction.Create.class; + } + else if (EntityResourceAction.CreateWithResponse.class.isAssignableFrom(rwm.getResource().getClass())) + { + resAction = EntityResourceAction.CreateWithResponse.class; + } + break; + case RELATIONSHIP: + if (RelationshipResourceAction.Create.class.isAssignableFrom(rwm.getResource().getClass())) + { + resAction = RelationshipResourceAction.Create.class; + } + else if (RelationshipResourceAction.CreateWithResponse.class.isAssignableFrom(rwm.getResource().getClass())) + { + resAction = RelationshipResourceAction.CreateWithResponse.class; + } + break; + case OPERATION: + noAuth = rwm.getMetaData().isNoAuth(null); + break; + default: + break; + } + + if (noAuth == null) + { + noAuth = (resAction != null && rwm.getMetaData().isNoAuth(resAction)); } - final boolean noAuth = (resAction != null && rwm.getMetaData().isNoAuth(resAction)); if (noAuth) { // override match with noAuth @@ -224,7 +256,7 @@ public class PublicApiDeclarativeRegistry extends DeclarativeRegistry { if (templateVars.get("apiName") != null) { - // NOTE: noAuth currently only exposed for GET or Create Ticket (login) + // NOTE: noAuth currently only exposed for GET or POST Api api = ApiAssistant.determineApi(templateVars); // TODO can we avoid locating resource more than once (or at least provide a common code to determine the GET resourceAction) ? @@ -435,27 +467,27 @@ public class PublicApiDeclarativeRegistry extends DeclarativeRegistry // override match with noAuth return new Match(match.getTemplate(), match.getTemplateVars(), match.getPath(), noAuthWebScriptWrapper); - } + } - private void initWebScript(WebScript webScript, String name) - { - DescriptionImpl serviceDesc = new DescriptionImpl(name, name, name, name); - serviceDesc.setRequiredAuthentication(RequiredAuthentication.user); - TransactionParameters transactionParameters = new TransactionParameters(); - transactionParameters.setRequired(RequiredTransaction.required); - transactionParameters.setCapability(TransactionCapability.readonly); - serviceDesc.setRequiredTransactionParameters(transactionParameters); - serviceDesc.setFormatStyle(FormatStyle.argument); - serviceDesc.setDefaultFormat("json"); - serviceDesc.setUris(new String[] { name }); - webScript.init(container, serviceDesc); - } - - public void reset() - { - super.reset(); - initWebScript(getNetworksWebScript, "networks"); - initWebScript(getNetworkWebScript, "network"); - } -} + private void initWebScript(WebScript webScript, String name) + { + DescriptionImpl serviceDesc = new DescriptionImpl(name, name, name, name); + serviceDesc.setRequiredAuthentication(RequiredAuthentication.user); + TransactionParameters transactionParameters = new TransactionParameters(); + transactionParameters.setRequired(RequiredTransaction.required); + transactionParameters.setCapability(TransactionCapability.readonly); + serviceDesc.setRequiredTransactionParameters(transactionParameters); + serviceDesc.setFormatStyle(FormatStyle.argument); + serviceDesc.setDefaultFormat("json"); + serviceDesc.setUris(new String[] { name }); + webScript.init(container, serviceDesc); + } + + public void reset() + { + super.reset(); + initWebScript(getNetworksWebScript, "networks"); + initWebScript(getNetworkWebScript, "network"); + } +} diff --git a/source/java/org/alfresco/rest/framework/core/OperationResourceMetaData.java b/source/java/org/alfresco/rest/framework/core/OperationResourceMetaData.java index 56c7f926a8..919028df79 100644 --- a/source/java/org/alfresco/rest/framework/core/OperationResourceMetaData.java +++ b/source/java/org/alfresco/rest/framework/core/OperationResourceMetaData.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2017 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -40,6 +40,7 @@ import java.util.Set; public class OperationResourceMetaData extends ResourceMetadata { private final Method operationMethod; + private final boolean noAuthRequired; /** * Use this constructor to create the resource metadata @@ -47,8 +48,9 @@ public class OperationResourceMetaData extends ResourceMetadata * @param operations * @param api * @param operationMethod + * @param noAuthRequired */ - public OperationResourceMetaData(String uniqueId, List operations, Api api, Method operationMethod) + public OperationResourceMetaData(String uniqueId, List operations, Api api, Method operationMethod, boolean noAuthRequired) { super(uniqueId, RESOURCE_TYPE.OPERATION, operations, api, null, null, null); if (operations.size()!= 1) @@ -56,6 +58,7 @@ public class OperationResourceMetaData extends ResourceMetadata throw new IllegalArgumentException("Only 1 operation per url is supported for an entity"); } this.operationMethod = operationMethod; + this.noAuthRequired = noAuthRequired; } /** @@ -63,11 +66,13 @@ public class OperationResourceMetaData extends ResourceMetadata * @param uniqueId * @param api * @param apiDeleted + * @param noAuthRequired */ - public OperationResourceMetaData(String uniqueId, Api api, Set> apiDeleted) + public OperationResourceMetaData(String uniqueId, Api api, Set> apiDeleted, boolean noAuthRequired) { super(uniqueId, RESOURCE_TYPE.OPERATION, null, api, apiDeleted, null, null); this.operationMethod = null; + this.noAuthRequired = noAuthRequired; } public Method getOperationMethod() @@ -75,6 +80,12 @@ public class OperationResourceMetaData extends ResourceMetadata return operationMethod; } + @Override + public boolean isNoAuth(Class resourceAction) + { + return this.noAuthRequired; + } + @Override public String toString() { @@ -91,7 +102,8 @@ public class OperationResourceMetaData extends ResourceMetadata builder.append(this.getOperations()); builder.append(", apiDeleted="); builder.append(this.getApiDeleted()); - builder.append("operationMethod=").append(operationMethod); + builder.append(", operationMethod=").append(operationMethod); + builder.append(", noAuthRequired=").append(noAuthRequired); builder.append("]"); return builder.toString(); } diff --git a/source/java/org/alfresco/rest/framework/core/ResourceInspector.java b/source/java/org/alfresco/rest/framework/core/ResourceInspector.java index 8fde2e6ec6..424c103725 100644 --- a/source/java/org/alfresco/rest/framework/core/ResourceInspector.java +++ b/source/java/org/alfresco/rest/framework/core/ResourceInspector.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2017 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -308,7 +308,7 @@ public class ResourceInspector { if (! (httpMethod.equals(HttpMethod.GET) || httpMethod.equals(HttpMethod.POST))) { - throw new IllegalArgumentException("@WebApiNoAuth should only be on GET methods: "+operation.getTitle()+" Or POST method for creating a ticket."); + throw new IllegalArgumentException("@WebApiNoAuth should only be on GET or POST methods: " + operation.getTitle()); } helper.whenOperationNoAuth(resourceInterfaceWithOneMethod, aMethod); } @@ -327,7 +327,7 @@ public class ResourceInspector Annotation annot = AnnotationUtils.findAnnotation(aMethod, WebApiDescription.class); List parameters = new ArrayList(); parameters.addAll(inspectParameters(resource, aMethod, httpMethod)); - + if (annot != null) { Map annotAttribs = AnnotationUtils.getAnnotationAttributes(annot); @@ -638,6 +638,7 @@ public class ResourceInspector * Inspect a resource to find operations on it. * @param api Api * @param entityPath String + * @param metainfo resource metadata */ public static void inspectOperations(Api api, Class resource, final String entityPath, List metainfo) { @@ -646,13 +647,16 @@ public class ResourceInspector { for (Entry> opera : operations.entrySet()) { - if (isDeleted(opera.getValue().getSecond())) + Method annotatedMethod = opera.getValue().getSecond(); + final boolean isNoAuthRequired = isNoAuth(annotatedMethod); + + if (isDeleted(annotatedMethod)) { - metainfo.add(new OperationResourceMetaData(opera.getKey(), api, new HashSet(Arrays.asList(opera.getValue().getFirst())))); + metainfo.add(new OperationResourceMetaData(opera.getKey(), api, new HashSet(Arrays.asList(opera.getValue().getFirst())), isNoAuthRequired)); } else { - metainfo.add(new OperationResourceMetaData(opera.getKey(), Arrays.asList(opera.getValue().getFirst()), api, opera.getValue().getSecond())); + metainfo.add(new OperationResourceMetaData(opera.getKey(), Arrays.asList(opera.getValue().getFirst()), api, annotatedMethod, isNoAuthRequired)); } } } diff --git a/source/java/org/alfresco/rest/framework/core/ResourceMetadata.java b/source/java/org/alfresco/rest/framework/core/ResourceMetadata.java index 3a008d5f6a..79b7f6bcb8 100644 --- a/source/java/org/alfresco/rest/framework/core/ResourceMetadata.java +++ b/source/java/org/alfresco/rest/framework/core/ResourceMetadata.java @@ -1,8 +1,9 @@ +/* /* * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2017 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -86,16 +87,17 @@ public class ResourceMetadata } /** - * Indicates if this resource can support the specified HTTPMethod - * @param supportedMethod HttpMethod - * @return true if can support it + * Gets the data type of the resource parameter + * + * @param operation {@code ResourceOperation} object + * @return The data type of the resource parameter */ - @SuppressWarnings("rawtypes") public Class getObjectType(ResourceOperation operation) { for (ResourceParameter param : operation.getParameters()) { - if (ResourceParameter.KIND.HTTP_BODY_OBJECT.equals(param.getParamType())) { + if (ResourceParameter.KIND.HTTP_BODY_OBJECT.equals(param.getParamType())) + { return param.getDataType(); } } diff --git a/source/test-java/org/alfresco/rest/framework/tests/api/mocks/GrassEntityResource.java b/source/test-java/org/alfresco/rest/framework/tests/api/mocks/GrassEntityResource.java index 275c4c4eea..c5dd36c21e 100644 --- a/source/test-java/org/alfresco/rest/framework/tests/api/mocks/GrassEntityResource.java +++ b/source/test-java/org/alfresco/rest/framework/tests/api/mocks/GrassEntityResource.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2017 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -27,6 +27,7 @@ package org.alfresco.rest.framework.tests.api.mocks; import org.alfresco.rest.framework.Operation; import org.alfresco.rest.framework.WebApiDescription; +import org.alfresco.rest.framework.WebApiNoAuth; import org.alfresco.rest.framework.WebApiParam; import org.alfresco.rest.framework.core.ResourceParameter; import org.alfresco.rest.framework.resource.EntityResource; @@ -73,4 +74,11 @@ public class GrassEntityResource implements EntityResourceAction.ReadById { //I did a delete } + + @Operation("cut-noAuth") + @WebApiNoAuth + @WebApiDescription(title = "Cut the grass",successStatus = Status.STATUS_NOT_IMPLEMENTED) + public String cutLawnWithoutAuth(String id, Void notused, Parameters parameters, WithResponse withResponse) { + return "All done without Auth"; + } } diff --git a/source/test-java/org/alfresco/rest/framework/tests/core/InspectorTests.java b/source/test-java/org/alfresco/rest/framework/tests/core/InspectorTests.java index 70da787514..164b16b69a 100644 --- a/source/test-java/org/alfresco/rest/framework/tests/core/InspectorTests.java +++ b/source/test-java/org/alfresco/rest/framework/tests/core/InspectorTests.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2017 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -41,7 +41,6 @@ import java.util.Map; import org.alfresco.rest.api.model.Comment; import org.alfresco.rest.api.nodes.NodeCommentsRelation; -import org.alfresco.rest.api.nodes.NodesEntityResource; import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.core.OperationResourceMetaData; import org.alfresco.rest.framework.core.ResourceInspector; @@ -76,9 +75,7 @@ import org.alfresco.rest.framework.tests.api.mocks3.GrassEntityResourceNowDelete import org.alfresco.rest.framework.tests.api.mocks3.SheepBlackSheepResourceIsNoMore; import org.alfresco.rest.framework.tests.api.mocks3.SheepEntityResourceWithDeletedMethods; import org.alfresco.rest.framework.tests.api.mocks3.SlimGoat; -import org.alfresco.rest.framework.tools.ApiAssistant; import org.alfresco.rest.framework.tools.ResponseWriter; -import org.alfresco.rest.framework.webscripts.ApiWebScript; import org.alfresco.rest.framework.webscripts.WithResponse; import org.alfresco.util.Pair; import org.junit.Test; @@ -459,7 +456,7 @@ public class InspectorTests GrassEntityResource grassEntityResource = new GrassEntityResource(); ResourceInspector.inspectOperations(api, GrassEntityResource.class,"-root-", metainfo); - assertTrue(metainfo.size()==2); + assertEquals(3, metainfo.size()); for (ResourceMetadata resourceMetadata : metainfo) { @@ -480,6 +477,7 @@ public class InspectorTests Object paramObj = paramType.newInstance(); result = (String) ResourceInspectorUtil.invokeMethod(actionMethod,grassEntityResource, "xyz", paramObj, Params.valueOf("notUsed", null, mock(WebScriptRequest.class)), wr); assertEquals("Growing well",result); + assertFalse(operationResourceMetaData.isNoAuth(null)); break; case "/-root-/{id}/cut": assertNotNull("GrassEntityResource supports POST", resourceMetadata.getOperation(HttpMethod.POST)); @@ -489,6 +487,16 @@ public class InspectorTests assertEquals("cut should return ACCEPTED", Status.STATUS_NOT_IMPLEMENTED, op.getSuccessStatus()); result = (String) ResourceInspectorUtil.invokeMethod(actionMethod,grassEntityResource, "xyz", null, Params.valueOf("notUsed", null, mock(WebScriptRequest.class)), wr); assertEquals("All done",result); + assertFalse(operationResourceMetaData.isNoAuth(null)); + break; + case "/-root-/{id}/cut-noAuth": + assertNotNull("GrassEntityResource supports POST", resourceMetadata.getOperation(HttpMethod.POST)); + op = resourceMetadata.getOperation(HttpMethod.POST); + assertNull(resourceMetadata.getObjectType(op)); + assertEquals("cut should return ACCEPTED", Status.STATUS_NOT_IMPLEMENTED, op.getSuccessStatus()); + result = (String) ResourceInspectorUtil.invokeMethod(actionMethod,grassEntityResource, "xyz", null, Params.valueOf("notUsed", null, mock(WebScriptRequest.class)), wr); + assertEquals("All done without Auth",result); + assertTrue(operationResourceMetaData.isNoAuth(null)); break; default: fail("Invalid action information.");