Java tutorial
/* * Copyright (C) 2013 Atol Conseils et Dveloppements. * http://www.atolcd.com/ * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.atolcd.alfresco; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.apache.abdera.protocol.client.util.MethodHelper.Method; import org.apache.commons.httpclient.URIException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.springframework.context.ApplicationContext; import org.springframework.extensions.surf.FrameworkUtil; import org.springframework.extensions.surf.RequestContext; import org.springframework.extensions.surf.RequestContextUtil; import org.springframework.extensions.surf.exception.ConnectorServiceException; import org.springframework.extensions.surf.exception.RequestContextException; import org.springframework.extensions.surf.exception.ResourceLoaderException; import org.springframework.extensions.surf.exception.UserFactoryException; import org.springframework.extensions.surf.support.AlfrescoUserFactory; import org.springframework.extensions.surf.support.ThreadLocalRequestContext; import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.connector.Connector; import org.springframework.extensions.webscripts.connector.ConnectorContext; import org.springframework.extensions.webscripts.connector.HttpMethod; import org.springframework.extensions.webscripts.connector.Response; import org.springframework.extensions.webscripts.connector.User; import org.springframework.web.context.support.WebApplicationContextUtils; import com.atolcd.alfresco.helper.AuditHelper; @SuppressWarnings("deprecation") public class ProxyAuditFilter extends AuditFilterConstants implements Filter { // Logger private static final Log logger = LogFactory.getLog(ProxyAuditFilter.class); private ServletContext servletContext; // XXX: externalize configuration? public static final String SHARE_WEBAPP_NAME = "share"; public static final String ALFRESCO_ENDPOINT_ID = "alfresco"; public static final String SHORT_PROXY_URL = "/" + SHARE_WEBAPP_NAME + "/proxy/" + ALFRESCO_ENDPOINT_ID + "/"; public static final String KEY_SITE = "site"; public static final String KEY_MODULE = "module"; public static final String KEY_ACTION = "action"; // Fake site id (for repository) public static final String TEMP_SITE = "/service"; // URIs parsed private static final String URI_BLOG = SHORT_PROXY_URL + "api/blog/"; private static final String URI_LINKS = SHORT_PROXY_URL + "api/links/"; private static final String URI_DOWNLOAD = SHORT_PROXY_URL + "api/node/content/"; private static final String URI_CALENDAR = "/calendar/create"; // Check the method of the request private static final String URI_DISCUSSIONS = SHORT_PROXY_URL + "api/forum/"; private static final String URI_WIKI = SHORT_PROXY_URL + "slingshot/wiki/page/"; private static final String URI_DATALIST = SHORT_PROXY_URL + "slingshot/datalists/item/"; private static final String URI_DATALIST_DELETE = SHORT_PROXY_URL + "slingshot/datalists/action/item"; // Updated from form private static final String URI_NODE_UPDATE = SHORT_PROXY_URL + "api/node/"; private static final String FORMPROCESSOR = "/formprocessor"; // Repository and sites private static final String URI_ACTION = SHORT_PROXY_URL + "slingshot/doclib/action/files"; private static final String URI_UPLOAD = SHORT_PROXY_URL + "api/upload"; // Social features private static final String URI_SOCIAL_PUBLISHING = SHORT_PROXY_URL + "api/publishing/queue"; private static final String GET = Method.GET.toString(); private static final String POST = Method.POST.toString(); private static final String PUT = Method.PUT.toString(); private static final String DELETE = Method.DELETE.toString(); @Override public void destroy() { } @Override public void init(FilterConfig args) throws ServletException { this.servletContext = args.getServletContext(); } private ApplicationContext getApplicationContext() { return WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); } @Override public void doFilter(ServletRequest sReq, ServletResponse sRes, FilterChain chain) throws IOException, ServletException { // Get the HTTP request/response/session HttpServletRequest request = (HttpServletRequest) sReq; // HttpServletResponse response = (HttpServletResponse) sRes; RequestWrapper requestWrapper = new RequestWrapper(request); // Initialize a new request context RequestContext context = ThreadLocalRequestContext.getRequestContext(); String referer = request.getHeader("referer"); if (context == null) { try { // Perform a "silent" init - i.e. no user creation or remote // connections context = RequestContextUtil.initRequestContext(getApplicationContext(), request, true); try { RequestContextUtil.populateRequestContext(context, request); } catch (ResourceLoaderException e) { // e.printStackTrace(); } catch (UserFactoryException e) { // e.printStackTrace(); } } catch (RequestContextException ex) { throw new ServletException(ex); } } User user = context.getUser(); String requestURI = request.getRequestURI(); String method = request.getMethod().toUpperCase(); if (user != null) { try { JSONObject auditSample = new JSONObject(); auditSample.put(AUDIT_ID, "0"); auditSample.put(AUDIT_USER_ID, user.getId()); auditSample.put(AUDIT_SITE, ""); auditSample.put(AUDIT_APP_NAME, ""); auditSample.put(AUDIT_ACTION_NAME, ""); auditSample.put(AUDIT_OBJECT, ""); auditSample.put(AUDIT_TIME, Long.toString(System.currentTimeMillis())); // For documents only (only in sites!) if (requestURI.endsWith("/doclib/activity") && request.getMethod().equals(POST)) { String type = request.getContentType().split(";")[0]; if (type.equals("application/json")) { // Get JSON Object JSONObject activityFeed = new JSONObject(requestWrapper.getStringContent()); String activityType = activityFeed.getString("type"); if (activityType != null) { if ("file-added".equals(activityType) || ("file-updated".equals(activityType) && (referer != null && !referer.contains("document-details"))) // Done in JavaScript on "document-details" page || "file-deleted".equals(activityType)) { if (activityFeed.has("nodeRef")) { auditSample.put(AUDIT_APP_NAME, MOD_DOCUMENT); auditSample.put(AUDIT_SITE, activityFeed.getString("site")); auditSample.put(AUDIT_ACTION_NAME, activityType); auditSample.put(AUDIT_OBJECT, activityFeed.getString("nodeRef")); auditSample.put(AUDIT_APP_NAME, MOD_DOCUMENT); remoteCall(request, auditSample); } } else if ("files-added".equals(activityType) || "files-deleted".equals(activityType)) { // multiple file uploads/deletions (5 or more) Integer fileCount = activityFeed.getInt("fileCount"); if (fileCount != null && fileCount > 0) { auditSample.put(AUDIT_APP_NAME, MOD_DOCUMENT); auditSample.put(AUDIT_SITE, activityFeed.getString("site")); auditSample.put(AUDIT_ACTION_NAME, "file-" + activityType.split("-")[1]); auditSample.put(AUDIT_APP_NAME, MOD_DOCUMENT); // auditSample.put(AUDIT_OBJECT, ""); for (int i = 0; i < fileCount; i++) { remoteCall(request, auditSample); } } } } } } else if (requestURI.startsWith(URI_NODE_UPDATE) && requestURI.endsWith(FORMPROCESSOR)) { JSONObject updatedData = new JSONObject(requestWrapper.getStringContent()); // Online edit used the same form (plus the cm_content // metadata) if (!updatedData.has("prop_cm_content")) { auditSample.put(AUDIT_APP_NAME, MOD_DOCUMENT); auditSample.put(AUDIT_OBJECT, getNodeRefFromUrl(requestURI, 1)); auditSample.put(AUDIT_ACTION_NAME, "update"); auditSample.put(AUDIT_SITE, TEMP_SITE); remoteCall(request, auditSample); } } else if (requestURI.endsWith("/activity/create")) { String jsonPost = requestWrapper.getStringContent(); if (jsonPost != null && !jsonPost.isEmpty()) { JSONObject json = new JSONObject(jsonPost); String mod = AuditHelper.extractModFromActivity(json); if (mod != null) { auditSample.put(AUDIT_APP_NAME, mod); auditSample.put(AUDIT_SITE, json.getString("site")); auditSample.put(AUDIT_ACTION_NAME, AuditHelper.extractActionFromActivity(json)); auditSample.put(AUDIT_OBJECT, json.getString("nodeRef")); remoteCall(request, auditSample); } } } else if (requestURI.equals(URI_UPLOAD)) { // XXX: issue with big files // Nothing to do - Insert request is done in JavaScript } else if (requestURI.endsWith("/comments") || requestURI.endsWith("/replies")) { // Comments & replies String[] urlTokens = request.getHeader("referer").toString().split("/"); HashMap<String, String> auditData = this.getUrlData(urlTokens); auditSample.put(AUDIT_SITE, auditData.get(KEY_SITE)); auditSample.put(AUDIT_APP_NAME, auditData.get(KEY_MODULE)); auditSample.put(AUDIT_ACTION_NAME, "comments"); auditSample.put(AUDIT_OBJECT, getNodeRefFromUrl(requestURI, 1)); // Remote call for DB remoteCall(request, auditSample); } else if (requestURI.startsWith(URI_WIKI)) { String[] urlTokens = requestURI.split("/"); String wikiPageId = urlTokens[urlTokens.length - 1]; String siteId = urlTokens[urlTokens.length - 2]; if (method.equals(Method.PUT.toString().toString())) { JSONObject params = new JSONObject(requestWrapper.getStringContent()); auditSample.put(AUDIT_SITE, siteId); auditSample.put(AUDIT_APP_NAME, MOD_WIKI); if (params.has("currentVersion")) { auditSample.put(AUDIT_ACTION_NAME, "update-post"); } else { auditSample.put(AUDIT_ACTION_NAME, "create-post"); } String auditObject = getNodeRefRemoteCall(request, user.getId(), siteId, MOD_WIKI, wikiPageId); auditSample.put(AUDIT_OBJECT, auditObject); // Remote call remoteCall(request, auditSample); } else if (method.equals(DELETE)) { auditSample.put(AUDIT_SITE, siteId); auditSample.put(AUDIT_APP_NAME, MOD_WIKI); auditSample.put(AUDIT_ACTION_NAME, "delete-post"); auditSample.put(AUDIT_OBJECT, wikiPageId); // Remote call remoteCall(request, auditSample); } } else if (requestURI.startsWith(URI_BLOG)) { auditSample.put(AUDIT_APP_NAME, MOD_BLOG); if (method.equals(POST)) { JSONObject params = new JSONObject(requestWrapper.getStringContent()); auditSample.put(AUDIT_SITE, params.get("site")); auditSample.put(AUDIT_ACTION_NAME, "blog-create"); auditSample.put(AUDIT_OBJECT, params.get("title")); remoteCall(request, auditSample); } else if (method.equals(PUT)) { JSONObject params = new JSONObject(requestWrapper.getStringContent()); auditSample.put(AUDIT_SITE, params.get("site")); auditSample.put(AUDIT_ACTION_NAME, "blog-update"); auditSample.put(AUDIT_OBJECT, getNodeRefFromUrl(requestURI, 0)); remoteCall(request, auditSample); } else if (method.equals(DELETE)) { String[] urlTokens = requestURI.split("/"); auditSample.put(AUDIT_OBJECT, urlTokens[urlTokens.length - 1]); auditSample.put(AUDIT_SITE, urlTokens[urlTokens.length - 3]); auditSample.put(AUDIT_ACTION_NAME, "blog-delete"); remoteCall(request, auditSample); } } else if (requestURI.startsWith(URI_DISCUSSIONS)) { auditSample.put(AUDIT_APP_NAME, "discussions"); if (method.equals(POST)) { JSONObject params = new JSONObject(requestWrapper.getStringContent()); auditSample.put(AUDIT_SITE, params.get("site")); auditSample.put(AUDIT_ACTION_NAME, "discussions-create"); auditSample.put(AUDIT_OBJECT, params.get("title")); remoteCall(request, auditSample); } else if (method.equals(PUT)) { JSONObject params = new JSONObject(requestWrapper.getStringContent()); String siteId = (String) params.get("site"); auditSample.put(AUDIT_SITE, siteId); auditSample.put(AUDIT_ACTION_NAME, "discussions-update"); String[] urlTokens = requestURI.split("/"); String discussionId = urlTokens[urlTokens.length - 1]; String auditObject = getNodeRefRemoteCall(request, user.getId(), siteId, "discussions", discussionId); auditSample.put(AUDIT_OBJECT, auditObject); remoteCall(request, auditSample); } else if (method.equals(DELETE)) { String[] urlTokens = requestURI.split("/"); auditSample.put(AUDIT_ACTION_NAME, "discussions-deleted"); auditSample.put(AUDIT_OBJECT, urlTokens[urlTokens.length - 1]); auditSample.put(AUDIT_SITE, urlTokens[urlTokens.length - 3]); remoteCall(request, auditSample); } } else if (requestURI.startsWith(URI_LINKS) && !method.equals(GET)) { String[] urlTokens = requestURI.split("/"); JSONObject params = new JSONObject(requestWrapper.getStringContent()); auditSample.put(AUDIT_APP_NAME, MOD_LINKS); if (method.equals(POST)) { if (requestURI.startsWith(URI_LINKS + "delete/")) { auditSample.put(AUDIT_SITE, urlTokens[urlTokens.length - 2]); auditSample.put(AUDIT_OBJECT, params.getJSONArray("items").get(0)); auditSample.put(AUDIT_ACTION_NAME, "links-delete"); } else { auditSample.put(AUDIT_OBJECT, params.get("title")); auditSample.put(AUDIT_SITE, urlTokens[urlTokens.length - 3]); auditSample.put(AUDIT_ACTION_NAME, "links-create"); } remoteCall(request, auditSample); } else if (method.equals(PUT)) { String siteId = urlTokens[urlTokens.length - 3]; auditSample.put(AUDIT_SITE, siteId); auditSample.put(AUDIT_ACTION_NAME, "links-update"); String auditObject = getNodeRefRemoteCall(request, user.getId(), siteId, MOD_LINKS, urlTokens[urlTokens.length - 1]); auditSample.put(AUDIT_OBJECT, auditObject); remoteCall(request, auditSample); } } else if (requestURI.startsWith(URI_DOWNLOAD)) { String a = request.getParameter("a"); if (a != null && !a.isEmpty()) { auditSample.put(AUDIT_APP_NAME, MOD_DOCUMENT); auditSample.put(AUDIT_OBJECT, getNodeRefFromUrl(requestURI, 1)); auditSample.put(AUDIT_ACTION_NAME, "true".equalsIgnoreCase(a) ? "download" : "stream"); auditSample.put(AUDIT_SITE, TEMP_SITE); remoteCall(request, auditSample); } } else if (requestURI.startsWith(URI_ACTION)) { // XXX: done in JavaScript } else if (requestURI.endsWith("memberships") && method.equals(GET)) { String type = request.getParameter("authorityType"); String nf = request.getParameter("nf"); String[] urlTokens = requestURI.split("/"); auditSample.put(AUDIT_SITE, urlTokens[urlTokens.length - 2]); auditSample.put(AUDIT_APP_NAME, MOD_MEMBERS); auditSample.put(AUDIT_ACTION_NAME, type.toLowerCase()); auditSample.put(AUDIT_OBJECT, nf); remoteCall(request, auditSample); } else if (requestURI.endsWith(URI_CALENDAR)) { JSONObject params = new JSONObject(requestWrapper.getStringContent()); auditSample.put(AUDIT_APP_NAME, MOD_CALENDAR); auditSample.put(AUDIT_ACTION_NAME, "create"); auditSample.put(AUDIT_SITE, params.get("site")); auditSample.put(AUDIT_OBJECT, params.get("what")); remoteCall(request, auditSample); } else if ((requestURI.startsWith(URI_DATALIST) || requestURI.startsWith(URI_DATALIST_DELETE)) && method.equals(POST)) { boolean isDeleteRequest = request.getParameter("alf_method") != null; auditSample.put(AUDIT_APP_NAME, MOD_DATA); auditSample.put(AUDIT_SITE, TEMP_SITE); if (isDeleteRequest) { auditSample.put(AUDIT_ACTION_NAME, "datalist-delete"); JSONObject params = new JSONObject(requestWrapper.getStringContent()); JSONArray items = params.getJSONArray("nodeRefs"); for (int i = 0; i < items.length(); i++) { auditSample.put(AUDIT_OBJECT, items.getString(i)); remoteCall(request, auditSample); } } else { auditSample.put(AUDIT_ACTION_NAME, "datalist-post"); auditSample.put(AUDIT_OBJECT, getNodeRefFromUrl(requestURI, 0)); remoteCall(request, auditSample); } } else if (requestURI.startsWith(URI_SOCIAL_PUBLISHING) && method.equals(POST)) { auditSample.put(AUDIT_APP_NAME, MOD_DOCUMENT); auditSample.put(AUDIT_SITE, TEMP_SITE); auditSample.put(AUDIT_ACTION_NAME, "publish"); JSONObject params = new JSONObject(requestWrapper.getStringContent()); JSONArray items = params.getJSONArray("publishNodes"); for (int i = 0; i < items.length(); i++) { auditSample.put(AUDIT_OBJECT, items.getString(i)); remoteCall(request, auditSample); } } else if (requestURI.indexOf("/ratings") != -1) { auditSample.put(AUDIT_APP_NAME, MOD_DOCUMENT); auditSample.put(AUDIT_SITE, TEMP_SITE); int offset = 1; if (POST.equals(method)) { auditSample.put(AUDIT_ACTION_NAME, "rate"); } else if (DELETE.equals(method)) { auditSample.put(AUDIT_ACTION_NAME, "unrate"); offset = 2; } auditSample.put(AUDIT_OBJECT, getNodeRefFromUrl(requestURI, offset)); remoteCall(request, auditSample); } } catch (JSONException e) { logger.error("JSON Error during a remote call ..."); if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } } } chain.doFilter(requestWrapper, sRes); } private void remoteCall(HttpServletRequest request, JSONObject auditSample) throws JSONException, URIException, UnsupportedEncodingException { Connector connector; try { connector = FrameworkUtil.getConnector(request.getSession(true), auditSample.getString(AUDIT_USER_ID), AlfrescoUserFactory.ALFRESCO_ENDPOINT_ID); ConnectorContext postContext = new ConnectorContext(null, buildDefaultHeaders()); postContext.setMethod(HttpMethod.POST); postContext.setContentType("text/plain;charset=UTF-8"); InputStream in = new ByteArrayInputStream(auditSample.toString().getBytes("UTF-8")); // Webscript call connector.call("/share-stats/insert-audit", postContext, in); } catch (ConnectorServiceException e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } } } private String getNodeRefRemoteCall(HttpServletRequest request, String userId, String siteId, String componentId, String objectId) throws JSONException, URIException, UnsupportedEncodingException { Connector connector; try { connector = FrameworkUtil.getConnector(request.getSession(true), userId, AlfrescoUserFactory.ALFRESCO_ENDPOINT_ID); // <url>/share-stats/slingshot/details/{siteId}/{componentId}/{objectId}</url> Response resp = connector.call("/share-stats/slingshot/details/" + siteId + "/" + componentId + "/" + URLEncoder.encode(objectId, "UTF-8")); if (resp.getStatus().getCode() == Status.STATUS_OK) { try { JSONObject json = new JSONObject(resp.getResponse()); if (json.has("nodeRef")) { return (String) json.get("nodeRef"); } } catch (JSONException e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } } } } catch (ConnectorServiceException e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } } return objectId; } /** * * @param url * @param offset * @return nodeRef */ public String getNodeRefFromUrl(String url, int offset) { String nodeRef = ""; String[] urlTokens = url.split("/"); nodeRef = urlTokens[urlTokens.length - offset - 3] + "://" + urlTokens[urlTokens.length - offset - 2] + "/" + urlTokens[urlTokens.length - offset - 1]; return nodeRef; } /** * * @param urlTokens * @return */ public HashMap<String, String> getUrlData(String[] urlTokens) { HashMap<String, String> urlData = new HashMap<String, String>(); urlData.put(KEY_MODULE, ""); urlData.put(KEY_ACTION, ""); urlData.put(KEY_SITE, ""); boolean siteFlag = false; for (int i = 0; i < urlTokens.length; i++) { if (urlTokens[i].equals(KEY_SITE) && !siteFlag) { siteFlag = true; } else if (siteFlag && (urlData.get(KEY_SITE).equals(""))) { urlData.put(KEY_SITE, urlTokens[i]); String[] splittedModuleAction = urlTokens[i + 1].split("-"); urlData.put(KEY_MODULE, splittedModuleAction[0]); if (splittedModuleAction.length > 1) { urlData.put(KEY_ACTION, splittedModuleAction[1]); } else if (splittedModuleAction.length == 1) { urlData.put(KEY_ACTION, ""); } } } return urlData; } /** * Helper to build a map of the default headers for script requests - we * send over the current users locale so it can be respected by any * appropriate REST APIs. * * @return map of headers */ private static Map<String, String> buildDefaultHeaders() { Map<String, String> headers = new HashMap<String, String>(1, 1.0f); headers.put("Accept-Language", I18NUtil.getLocale().toString().replace('_', '-')); return headers; } }