From 761099b84ec36c8316a383857d76ba88cad4a75c Mon Sep 17 00:00:00 2001 From: Brian Remmington Date: Mon, 11 Jul 2011 11:21:06 +0000 Subject: [PATCH] Publishing: added authorisation framework for channel types. Migrated YouTube channel type onto it, and added Twitter and SlideShare channel types. Facebook is also added, but there is an issue with the way it authorises apps, so it isn't wired in yet. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28910 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../publishing/authcallback.get.desc.xml | 26 +++++ .../publishing/authform.get.desc.xml | 25 +++++ .../publishing/authform.get.html.ftl | 23 ++++ .../publishing/authform.post.desc.xml | 25 +++++ .../publishing/authstatus.get.desc.xml | 48 ++++++++ .../publishing/authstatus.get.json.ftl | 10 ++ .../publishing/channel.post.desc.xml | 57 ++++++++++ .../publishing/channel.post.json.ftl | 11 ++ .../web-scripts-application-context.xml | 31 +++++- .../publishing/AuthCallbackWebScript.java | 105 ++++++++++++++++++ .../publishing/AuthStatusGetWebScript.java | 70 ++++++++++++ .../publishing/ChannelPostWebScript.java | 82 ++++++++++++++ .../publishing/PublishingRestApiTest.java | 4 +- 13 files changed, 513 insertions(+), 4 deletions(-) create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authcallback.get.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.get.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.get.html.ftl create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.post.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authstatus.get.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authstatus.get.json.ftl create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/publishing/channel.post.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/publishing/channel.post.json.ftl create mode 100644 source/java/org/alfresco/repo/web/scripts/publishing/AuthCallbackWebScript.java create mode 100644 source/java/org/alfresco/repo/web/scripts/publishing/AuthStatusGetWebScript.java create mode 100644 source/java/org/alfresco/repo/web/scripts/publishing/ChannelPostWebScript.java diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authcallback.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authcallback.get.desc.xml new file mode 100644 index 0000000000..4a67176194 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authcallback.get.desc.xml @@ -0,0 +1,26 @@ + + Authorisation Callback + + + /api/publishing/channel/{store_protocol}/{store_id}/{node_id}/authcallback + + user + required + public_api + + + store_protocol + The protocol of the store in which the relevant publishing channel lives. + + + store_id + The identifier of the store in which the relevant publishing channel lives. + + + node_id + The identifier of the node that represents the relevant publishing channel. + + + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.get.desc.xml new file mode 100644 index 0000000000..cb2db8700a --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.get.desc.xml @@ -0,0 +1,25 @@ + + Channel Authorisation Form + + /api/publishing/channel/{store_protocol}/{store_id}/{node_id}/authform + + user + required + public_api + + + store_protocol + The protocol of the store in which the relevant publishing channel lives. + + + store_id + The identifier of the store in which the relevant publishing channel lives. + + + node_id + The identifier of the node that represents the relevant publishing channel. + + + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.get.html.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.get.html.ftl new file mode 100644 index 0000000000..15951c828c --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.get.html.ftl @@ -0,0 +1,23 @@ + + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.post.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.post.desc.xml new file mode 100644 index 0000000000..bfc817ee7e --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authform.post.desc.xml @@ -0,0 +1,25 @@ + + Channel Authorisation Form + + /api/publishing/channel/{store_protocol}/{store_id}/{node_id}/authform + + user + required + public_api + + + store_protocol + The protocol of the store in which the relevant publishing channel lives. + + + store_id + The identifier of the store in which the relevant publishing channel lives. + + + node_id + The identifier of the node that represents the relevant publishing channel. + + + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authstatus.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authstatus.get.desc.xml new file mode 100644 index 0000000000..9bdecd5671 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authstatus.get.desc.xml @@ -0,0 +1,48 @@ + + Authorisation status check for a new publishing channel + +
  • PENDING - we haven't finished the necessary exchanges with the service provider yet.
  • +
  • REJECTED - our request to authorise the channel has been rejected. The channel creation has failed.
  • +
  • AUTHORISED - the channel has been authorised and is now ready to be used.
  • + + ]]>
    + + /api/publishing/channel/{store_protocol}/{store_id}/{node_id}/authstatus + + user + required + public_api + + + store_protocol + The protocol of the store in which the relevant publishing channel lives. + + + store_id + The identifier of the store in which the relevant publishing channel lives. + + + node_id + The identifier of the node that represents the relevant publishing channel. + + + + + json + + + + + +
    \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authstatus.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authstatus.get.json.ftl new file mode 100644 index 0000000000..5619b794bf --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/authstatus.get.json.ftl @@ -0,0 +1,10 @@ +<#-- Response to a request to check the authorisation status of a publishing channel --> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "channelId" : "${channelId}", + "authStatus": "${authStatus}", + } +} + diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/channel.post.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/channel.post.desc.xml new file mode 100644 index 0000000000..b716094636 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/channel.post.desc.xml @@ -0,0 +1,57 @@ + + Create a publishing channel + +
    +
    channelType
    mandatory - the type of delivery channel to create
    +
    siteId
    mandatory - the Share site with which the new delivery channel is to be associated
    +
    channelName
    mandatory - the name of the new delivery channel
    +
    + Returns three pieces of informtation: +
    +
    +
    channelId
    the identifier of the new publishing channel
    +
    pollUrl
    The URL to poll to discover whether the channel has been authorised
    +
    authoriseUrl
    The URL to send the user to in order for them to authorise access to the channel
    +
    + + ]]>
    + + /api/publishing/channel + + user + required + public_api + + + siteId + The id of the site to create a delivery channel on. + + + channelType + The identifier of the type of delivery channel to create. + + + channelName + The name of the channel that is to be created. + + + + + json + + + + + +
    \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/channel.post.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/channel.post.json.ftl new file mode 100644 index 0000000000..5789df16e9 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/publishing/channel.post.json.ftl @@ -0,0 +1,11 @@ +<#-- Response to a request to create a publishing channel --> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "data": + { + "channelId" : "${channelId}", + "pollUrl": "${pollUrl}", + "authoriseUrl": "${authoriseUrl}" + } +} + diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index 27e0625f1a..f539b3f1e3 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -1118,6 +1118,33 @@ + + + + + + + + + + + + + + + + + + + - + - + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/publishing/AuthCallbackWebScript.java b/source/java/org/alfresco/repo/web/scripts/publishing/AuthCallbackWebScript.java new file mode 100644 index 0000000000..2d4da9cfb6 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/publishing/AuthCallbackWebScript.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.web.scripts.publishing; + +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; + +import org.alfresco.repo.publishing.PublishingModel; +import org.alfresco.service.cmr.publishing.channels.Channel; +import org.alfresco.service.cmr.publishing.channels.ChannelService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.AbstractWebScript; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + +public class AuthCallbackWebScript extends AbstractWebScript +{ + private final static Log log = LogFactory.getLog(AuthCallbackWebScript.class); + private NodeService nodeService; + private ChannelService channelService; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setChannelService(ChannelService channelService) + { + this.channelService = channelService; + } + + @Override + public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException + { + res.setContentType("text/html"); + res.setContentEncoding("UTF-8"); + + Map templateVars = req.getServiceMatch().getTemplateVars(); + Map params = new TreeMap(); + Map headers = new TreeMap(); + + for (String paramName : req.getParameterNames()) + { + params.put(paramName, req.getParameterValues(paramName)); + } + + for (String header : req.getHeaderNames()) + { + headers.put(header, req.getHeaderValues(header)); + } + + if (log.isDebugEnabled()) + { + log.debug("templateVars = " + templateVars); + log.debug("params = " + params); + log.debug("headers = " + headers); + } + + String channelNodeUuid = templateVars.get("node_id"); + String channelNodeStoreProtocol = templateVars.get("store_protocol"); + String channelNodeStoreId = templateVars.get("store_id"); + + NodeRef channelNodeRef = new NodeRef(channelNodeStoreProtocol, channelNodeStoreId, channelNodeUuid); + Channel channel = channelService.getChannel(channelNodeRef.toString()); + + if (channel.getChannelType().acceptAuthorisationCallback(channel, headers, params)) + { + nodeService.setProperty(channelNodeRef, PublishingModel.PROP_AUTHORISATION_COMPLETE, Boolean.TRUE); + res.getWriter().write("Authorisation granted!"); + } + else + { + Boolean authorised = (Boolean)nodeService.getProperty(channelNodeRef, PublishingModel.PROP_AUTHORISATION_COMPLETE); + if (authorised != null && !authorised) + { + //If we have not been granted access by the service provider then we + //simply delete this publishing channel + nodeService.deleteNode(channelNodeRef); + } + res.getWriter().write("Authorisation denied!"); + } + } + +} diff --git a/source/java/org/alfresco/repo/web/scripts/publishing/AuthStatusGetWebScript.java b/source/java/org/alfresco/repo/web/scripts/publishing/AuthStatusGetWebScript.java new file mode 100644 index 0000000000..78e857e72a --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/publishing/AuthStatusGetWebScript.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.web.scripts.publishing; + +import java.util.Map; +import java.util.TreeMap; + +import org.alfresco.repo.publishing.PublishingModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +public class AuthStatusGetWebScript extends DeclarativeWebScript +{ + private NodeService nodeService; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + Map templateVars = req.getServiceMatch().getTemplateVars(); + String channelNodeUuid = templateVars.get("node_id"); + String channelNodeStoreProtocol = templateVars.get("store_protocol"); + String channelNodeStoreId = templateVars.get("store_id"); + + String authStatus = "REJECTED"; + NodeRef channelNodeRef = new NodeRef(channelNodeStoreProtocol, channelNodeStoreId, channelNodeUuid); + if (nodeService.exists(channelNodeRef)) + { + Boolean authComplete = (Boolean)nodeService.getProperty(channelNodeRef, PublishingModel.PROP_AUTHORISATION_COMPLETE); + if (authComplete) + { + authStatus = "AUTHORISED"; + } + else + { + authStatus = "PENDING"; + } + } + Map model = new TreeMap(); + model.put("channelId", channelNodeRef.toString()); + model.put("authStatus", authStatus); + + return model; + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/publishing/ChannelPostWebScript.java b/source/java/org/alfresco/repo/web/scripts/publishing/ChannelPostWebScript.java new file mode 100644 index 0000000000..88515dc766 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/publishing/ChannelPostWebScript.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.web.scripts.publishing; + +import java.util.Map; +import java.util.TreeMap; + +import org.alfresco.service.cmr.publishing.channels.Channel; +import org.alfresco.service.cmr.publishing.channels.ChannelService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +public class ChannelPostWebScript extends DeclarativeWebScript +{ + private ChannelService channelService; + + public void setChannelService(ChannelService channelService) + { + this.channelService = channelService; + } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + String channelType = req.getParameter("channelType"); + String siteId = req.getParameter("siteId"); + String channelName = req.getParameter("channelName"); + + Channel newChannel = channelService.createChannel(siteId, channelType, channelName, null); + + NodeRef channelNodeRef = newChannel.getNodeRef(); + StringBuilder urlBuilder = new StringBuilder(req.getServerPath()); + urlBuilder.append(req.getServiceContextPath()); + urlBuilder.append("/api/publishing/channel/"); + urlBuilder.append(channelNodeRef.getStoreRef().getProtocol()); + urlBuilder.append('/'); + urlBuilder.append(channelNodeRef.getStoreRef().getIdentifier()); + urlBuilder.append('/'); + urlBuilder.append(channelNodeRef.getId()); + urlBuilder.append('/'); + + String baseUrl = urlBuilder.toString(); + String pollUrl = baseUrl + "authstatus"; + String callbackUrl = baseUrl + "authcallback"; + + String authoriseUrl = channelService.getChannelType(channelType).getAuthorisationUrl(newChannel, callbackUrl); + if (authoriseUrl == null) + { + // If a channel type returns null as the authorise URL then we + // assume credentials are to be supplied to us directly. We'll point the + // user at our own credential-gathering form. + authoriseUrl = baseUrl + "authform"; + } + + Map model = new TreeMap(); + model.put("pollUrl", pollUrl); + model.put("authoriseUrl", authoriseUrl); + model.put("channelId", channelNodeRef.toString()); + + return model; + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/publishing/PublishingRestApiTest.java b/source/java/org/alfresco/repo/web/scripts/publishing/PublishingRestApiTest.java index 6e50e1ec23..a2d9cc15f8 100644 --- a/source/java/org/alfresco/repo/web/scripts/publishing/PublishingRestApiTest.java +++ b/source/java/org/alfresco/repo/web/scripts/publishing/PublishingRestApiTest.java @@ -299,12 +299,12 @@ public class PublishingRestApiTest extends BaseWebScriptTest // Check updateStatus is called correctly. ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(statusUpdateChannelType) - .updateStatus(captor.capture(), anyMap()); + .updateStatus(any(Channel.class), captor.capture(), anyMap()); String actualStatusMessage = captor.getValue(); assertTrue(actualStatusMessage.startsWith(statusMessage)); verify(statusUpdateChannelType, never()).publish(any(NodeRef.class), anyMap()); - verify(publishAnyChannelType, never()).updateStatus(anyString(), anyMap()); + verify(publishAnyChannelType, never()).updateStatus(any(Channel.class), anyString(), anyMap()); JSONObject status = json.optJSONObject(STATUS_UPDATE); status.remove(NODE_REF);