mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	125606 rmunteanu: Merged 5.1.1 (5.1.1) to 5.1.N (5.1.2)
      125515 slanglois: MNT-16155 Update source headers - add new Copyrights for Java and JSP source files + automatic check in the build
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@125788 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
	
		
			
				
	
	
		
			439 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * #%L
 | |
|  * Alfresco Repository
 | |
|  * %%
 | |
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | |
|  * %%
 | |
|  * This file is part of the Alfresco software. 
 | |
|  * If the software was purchased under a paid Alfresco license, the terms of 
 | |
|  * the paid license agreement will prevail.  Otherwise, the software is 
 | |
|  * provided under the following open source license terms:
 | |
|  * 
 | |
|  * 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 <http://www.gnu.org/licenses/>.
 | |
|  * #L%
 | |
|  */
 | |
| package org.alfresco.repo.remoteticket;
 | |
| 
 | |
| import java.io.IOException;
 | |
| import java.util.HashMap;
 | |
| import java.util.Map;
 | |
| 
 | |
| import org.alfresco.error.AlfrescoRuntimeException;
 | |
| import org.alfresco.repo.cache.SimpleCache;
 | |
| import org.alfresco.repo.remotecredentials.PasswordCredentialsInfoImpl;
 | |
| import org.alfresco.repo.security.authentication.AuthenticationException;
 | |
| import org.alfresco.repo.transaction.RetryingTransactionHelper;
 | |
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
 | |
| import org.alfresco.service.cmr.remoteconnector.RemoteConnectorRequest;
 | |
| import org.alfresco.service.cmr.remoteconnector.RemoteConnectorService;
 | |
| import org.alfresco.service.cmr.remotecredentials.BaseCredentialsInfo;
 | |
| import org.alfresco.service.cmr.remotecredentials.PasswordCredentialsInfo;
 | |
| import org.alfresco.service.cmr.remotecredentials.RemoteCredentialsService;
 | |
| import org.alfresco.service.cmr.remoteticket.NoCredentialsFoundException;
 | |
| import org.alfresco.service.cmr.remoteticket.NoSuchSystemException;
 | |
| import org.alfresco.service.cmr.remoteticket.RemoteAlfrescoTicketInfo;
 | |
| import org.alfresco.service.cmr.remoteticket.RemoteAlfrescoTicketService;
 | |
| import org.alfresco.service.cmr.remoteticket.RemoteSystemUnavailableException;
 | |
| import org.apache.commons.logging.Log;
 | |
| import org.apache.commons.logging.LogFactory;
 | |
| import org.json.simple.JSONObject;
 | |
| import org.json.simple.parser.ParseException;
 | |
| 
 | |
| /**
 | |
|  * Service for working with a Remote Alfresco instance, which
 | |
|  *  holds user credentials for the remote system via the 
 | |
|  *  {@link RemoteCredentialsService}, and handles ticket 
 | |
|  *  negotiation for you.
 | |
|  *  
 | |
|  * Note - this service will be moved to the Repository Core once
 | |
|  *  it has stabilised (likely after OAuth support is added)
 | |
|  * 
 | |
|  * TODO OAuth support
 | |
|  * 
 | |
|  * @author Nick Burch
 | |
|  * @since 4.0.2
 | |
|  */
 | |
| public class RemoteAlfrescoTicketServiceImpl implements RemoteAlfrescoTicketService
 | |
| {
 | |
|     /**
 | |
|      * The logger
 | |
|      */
 | |
|     private static Log logger = LogFactory.getLog(RemoteAlfrescoTicketServiceImpl.class);
 | |
|             
 | |
|     private RetryingTransactionHelper retryingTransactionHelper;
 | |
|     private RemoteCredentialsService remoteCredentialsService;
 | |
|     private RemoteConnectorService remoteConnectorService;
 | |
|     private SimpleCache<String, String> ticketsCache;
 | |
|     
 | |
|     private Map<String,String> remoteSystemsUrls = new HashMap<String, String>();
 | |
|     private Map<String,Map<String,String>> remoteSystemsReqHeaders = new HashMap<String, Map<String,String>>();
 | |
|     
 | |
|     /**
 | |
|      * Sets the Remote Credentials Service to use to store and retrieve credentials
 | |
|      */
 | |
|     public void setRemoteCredentialsService(RemoteCredentialsService remoteCredentialsService)
 | |
|     {
 | |
|         this.remoteCredentialsService = remoteCredentialsService;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Sets the Remote Connector Service to use to talk to remote systems with
 | |
|      */
 | |
|     public void setRemoteConnectorService(RemoteConnectorService remoteConnectorService)
 | |
|     {
 | |
|         this.remoteConnectorService = remoteConnectorService;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets the SimpleCache to be used to cache remote tickets in
 | |
|      */
 | |
|     public void setTicketsCache(SimpleCache<String, String> ticketsCache)
 | |
|     {
 | |
|         this.ticketsCache = ticketsCache;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Sets the Retrying Transaction Helper, used to write changes to
 | |
|      *  Credentials which turn out to be invalid 
 | |
|      */
 | |
|     public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper)
 | |
|     {
 | |
|         this.retryingTransactionHelper = retryingTransactionHelper;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Registers the details of a new Remote System with the service.
 | |
|      * Any previous details for the system will be overridden
 | |
|      */
 | |
|     public synchronized void registerRemoteSystem(String remoteSystemId, String baseUrl, Map<String,String> requestHeaders)
 | |
|     {
 | |
|         remoteSystemsUrls.put(remoteSystemId, baseUrl);
 | |
|         remoteSystemsReqHeaders.put(remoteSystemId, requestHeaders);
 | |
|         
 | |
|         if (logger.isDebugEnabled())
 | |
|             logger.debug("Registered System " + remoteSystemId + " as " + baseUrl);
 | |
|     }
 | |
|     
 | |
|     protected void ensureRemoteSystemKnown(String remoteSystemId) throws NoSuchSystemException
 | |
|     {
 | |
|         String baseUrl = remoteSystemsUrls.get(remoteSystemId);
 | |
|         if (baseUrl == null)
 | |
|         {
 | |
|             throw new NoSuchSystemException(remoteSystemId);
 | |
|         }
 | |
|     }
 | |
|     protected PasswordCredentialsInfo ensureCredentialsFound(String remoteSystemId, BaseCredentialsInfo credentails)
 | |
|     {
 | |
|         // Check they exist, and are of the right type
 | |
|         if (credentails == null)
 | |
|         {
 | |
|             throw new NoCredentialsFoundException(remoteSystemId);
 | |
|         }
 | |
|         if (! (credentails instanceof PasswordCredentialsInfo))
 | |
|         {
 | |
|             throw new AlfrescoRuntimeException("Credentials found, but of the wrong type, needed PasswordCredentialsInfo but got " + credentails); 
 | |
|         }
 | |
|         return (PasswordCredentialsInfo)credentails;
 | |
|     }
 | |
|     protected String toCacheKey(String remoteSystemId, BaseCredentialsInfo credentials)
 | |
|     {
 | |
|         // Cache key is system + separator + remote username
 | |
|         return remoteSystemId + "===" + credentials.getRemoteUsername();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Validates and stores the remote credentials for the current user
 | |
|      */
 | |
|     public BaseCredentialsInfo storeRemoteCredentials(String remoteSystemId, String username, String password)
 | |
|        throws AuthenticationException, RemoteSystemUnavailableException, NoSuchSystemException
 | |
|     {
 | |
|         // Check we know about the system
 | |
|         ensureRemoteSystemKnown(remoteSystemId);
 | |
|         
 | |
|         // Build the initial stub credentials
 | |
|         PasswordCredentialsInfoImpl credentials = new PasswordCredentialsInfoImpl();
 | |
|         
 | |
|         // See if there are existing credentials to update
 | |
|         BaseCredentialsInfo existing = getRemoteCredentials(remoteSystemId);
 | |
|         if (existing != null)
 | |
|         {
 | |
|             // Update if we can, otherwise delete for re-add
 | |
|             if (existing instanceof PasswordCredentialsInfoImpl)
 | |
|             {
 | |
|                 credentials = (PasswordCredentialsInfoImpl)existing;
 | |
|                 if (logger.isDebugEnabled())
 | |
|                     logger.debug("Updating existing credentials from " + credentials.getNodeRef());
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // Wrong type, delete and use new ones
 | |
|                 if (logger.isDebugEnabled())
 | |
|                     logger.debug("Unable to update existing credentials from " + existing.getNodeRef() + ", replacing");
 | |
|                 remoteCredentialsService.deleteCredentials(existing);
 | |
|                 existing = null;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Set the remote system credentials for them
 | |
|         credentials.setRemoteUsername(username);
 | |
|         credentials.setRemotePassword(password);
 | |
|         
 | |
|         // Validate their credentials are correct, by attempting to get a ticket for them
 | |
|         refreshTicket(remoteSystemId, credentials);
 | |
|         
 | |
|         if (logger.isDebugEnabled())
 | |
|             logger.debug("Credentials correct for " + username + " on " + remoteSystemId);
 | |
|         
 | |
|         // If we get this far, then there credentials are valid, so store them
 | |
|         credentials.setLastAuthenticationSucceeded(true);
 | |
|         
 | |
|         if (credentials.getNodeRef() != null)
 | |
|         {
 | |
|             return remoteCredentialsService.updateCredentials(credentials);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             return remoteCredentialsService.createPersonCredentials(remoteSystemId, credentials);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Retrieves the remote credentials (if any) for the current user
 | |
|      * 
 | |
|      * @param remoteSystemId The ID of the remote system, as registered with the service
 | |
|      * @return The current user's remote credentials, or null if they don't have any
 | |
|      */
 | |
|     public BaseCredentialsInfo getRemoteCredentials(String remoteSystemId) throws NoSuchSystemException
 | |
|     {
 | |
|         // Check we know about the system
 | |
|         ensureRemoteSystemKnown(remoteSystemId);
 | |
|         
 | |
|         // Retrieve, if available, and return
 | |
|         return remoteCredentialsService.getPersonCredentials(remoteSystemId);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Deletes the remote credentials (if any) for the current user
 | |
|      */
 | |
|     public boolean deleteRemoteCredentials(String remoteSystemId) throws NoSuchSystemException
 | |
|     {
 | |
|         // Try to retrieve
 | |
|         BaseCredentialsInfo credentials = getRemoteCredentials(remoteSystemId);
 | |
|         
 | |
|         // If there are none, nothing to do
 | |
|         if (credentials == null)
 | |
|         {
 | |
|             if (logger.isDebugEnabled())
 | |
|                 logger.debug("No credentials found to delete on " + remoteSystemId);
 | |
|             return false;
 | |
|         }
 | |
|         
 | |
|         // Log that we're going to delete
 | |
|         if (logger.isDebugEnabled())
 | |
|             logger.debug("Deleting credentials for " + credentials.getRemoteUsername() + " on " + remoteSystemId);
 | |
|         
 | |
|         // Delete the credentials
 | |
|         remoteCredentialsService.deleteCredentials(credentials);
 | |
|         
 | |
|         // Zap the cached ticket, if there is one
 | |
|         String cacheKey = toCacheKey(remoteSystemId, credentials);
 | |
|         ticketsCache.remove(cacheKey);
 | |
|         
 | |
|         // Indicate the delete worked
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the current Alfresco Ticket for the current user on
 | |
|      *  the remote system, fetching if it isn't already cached.
 | |
|      */
 | |
|     public RemoteAlfrescoTicketInfo getAlfrescoTicket(String remoteSystemId)
 | |
|        throws AuthenticationException, NoCredentialsFoundException, NoSuchSystemException, RemoteSystemUnavailableException
 | |
|     {
 | |
|         // Check we know about the system
 | |
|         ensureRemoteSystemKnown(remoteSystemId);
 | |
|         
 | |
|         // Grab the user's details
 | |
|         BaseCredentialsInfo creds = getRemoteCredentials(remoteSystemId);
 | |
|         PasswordCredentialsInfo credentials = ensureCredentialsFound(remoteSystemId, creds);
 | |
|         
 | |
|         // Is there a cached ticket?
 | |
|         String cacheKey = toCacheKey(remoteSystemId, credentials);
 | |
|         String ticket = ticketsCache.get(cacheKey);
 | |
|         
 | |
|         // Refresh if if isn't cached
 | |
|         if (ticket == null)
 | |
|         {
 | |
|             return refreshTicket(remoteSystemId, credentials);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             if (logger.isDebugEnabled())
 | |
|                 logger.debug("Cached ticket found for " + creds.getRemoteUsername() + " on " + remoteSystemId);
 | |
|                 
 | |
|             // Wrap and return
 | |
|             return new AlfTicketRemoteAlfrescoTicketImpl(ticket);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Forces a re-fetch of the Alfresco Ticket for the current user,
 | |
|      *  if possible, and marks the credentials as failing if not. 
 | |
|      */
 | |
|     public RemoteAlfrescoTicketInfo refetchAlfrescoTicket(String remoteSystemId)
 | |
|        throws AuthenticationException, NoCredentialsFoundException, NoSuchSystemException, RemoteSystemUnavailableException
 | |
|     {
 | |
|         // Check we know about the system
 | |
|         ensureRemoteSystemKnown(remoteSystemId);
 | |
|         
 | |
|         // Grab the user's details
 | |
|         BaseCredentialsInfo creds = getRemoteCredentials(remoteSystemId);
 | |
|         PasswordCredentialsInfo credentials = ensureCredentialsFound(remoteSystemId, creds);
 | |
|         
 | |
|         // Trigger the refresh
 | |
|         return refreshTicket(remoteSystemId, credentials);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Fetches a new ticket for the given user, and caches it
 | |
|      */
 | |
|     @SuppressWarnings("unchecked")
 | |
|     protected RemoteAlfrescoTicketInfo refreshTicket(final String remoteSystemId, final PasswordCredentialsInfo credentials)
 | |
|        throws AuthenticationException, NoSuchSystemException, RemoteSystemUnavailableException
 | |
|     {
 | |
|         // Check we know about the system
 | |
|         String baseUrl = remoteSystemsUrls.get(remoteSystemId);
 | |
|         if (baseUrl == null)
 | |
|         {
 | |
|             throw new NoSuchSystemException(remoteSystemId);
 | |
|         }
 | |
|         
 | |
|         if (logger.isDebugEnabled())
 | |
|             logger.debug("Fetching new ticket for " + credentials.getRemoteUsername() + " on " + remoteSystemId);
 | |
|         
 | |
|         // Build up the JSON for the ticket request
 | |
|         JSONObject json = new JSONObject();
 | |
|         json.put("username", credentials.getRemoteUsername());
 | |
|         json.put("password", credentials.getRemotePassword());
 | |
|         
 | |
|         // Build the URL
 | |
|         String url = baseUrl + "api/login";
 | |
|         
 | |
|         // Turn this into a remote request
 | |
|         RemoteConnectorRequest request = remoteConnectorService.buildRequest(url, "POST");
 | |
|         request.setRequestBody(json.toJSONString());
 | |
|         
 | |
|         Map<String,String> reqHeaders = remoteSystemsReqHeaders.get(remoteSystemId);
 | |
|         if (reqHeaders != null)
 | |
|         {
 | |
|             for (Map.Entry<String, String> reqHeader : reqHeaders.entrySet())
 | |
|             {
 | |
|                 request.addRequestHeader(reqHeader.getKey(), reqHeader.getValue());
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Work out what key we'll use to cache on
 | |
|         String cacheKey = toCacheKey(remoteSystemId, credentials);
 | |
|         
 | |
|         // Perform the request
 | |
|         String ticket = null;
 | |
|         try {
 | |
|             JSONObject response = remoteConnectorService.executeJSONRequest(request);
 | |
|             if (logger.isDebugEnabled())
 | |
|                 logger.debug("JSON Ticket Response Received: " + response);
 | |
| 
 | |
|             // Pull out the ticket, validating the JSON along the way
 | |
|             Object data = response.get("data");
 | |
|             if (data == null)
 | |
|             {
 | |
|                 throw new RemoteSystemUnavailableException("Invalid JSON received: " + response);
 | |
|             }
 | |
|             if (! (data instanceof JSONObject))
 | |
|             {
 | |
|                 throw new RemoteSystemUnavailableException("Invalid JSON part received: " + data.getClass() + " - from: " + response);
 | |
|             }
 | |
| 
 | |
|             Object ticketJSON = ((JSONObject)data).get("ticket");
 | |
|             if (ticketJSON == null)
 | |
|             {
 | |
|                 throw new RemoteSystemUnavailableException("Invalid JSON received, ticket missing: " + response);
 | |
|             }
 | |
|             if (! (ticketJSON instanceof String))
 | |
|             {
 | |
|                 throw new RemoteSystemUnavailableException("Invalid JSON part received: " + ticketJSON.getClass() + " from: " + response);
 | |
|             }
 | |
|             ticket = (String)ticketJSON;
 | |
|         }
 | |
|         catch (IOException ioEx)
 | |
|         {
 | |
|             if (logger.isDebugEnabled())
 | |
|                 logger.debug("Problem communicating with remote Alfresco instance " + remoteSystemId, ioEx);
 | |
|             
 | |
|             throw new RemoteSystemUnavailableException("Error talking to remote system", ioEx);
 | |
|         }
 | |
|         catch (ParseException jsonEx)
 | |
|         {
 | |
|             if (logger.isDebugEnabled())
 | |
|                 logger.debug("Invalid JSON from remote Alfresco instance " + remoteSystemId, jsonEx);
 | |
|             
 | |
|             throw new RemoteSystemUnavailableException("Invalid JSON response from remote system", jsonEx);
 | |
|         }
 | |
|         catch (AuthenticationException authEx)
 | |
|         {
 | |
|             // Record the credentials as now failing, if they're persisted ones
 | |
|             // Do this in a read-write transaction (most ticket stuff is read only)
 | |
|             if (credentials.getNodeRef() != null && credentials.getLastAuthenticationSucceeded())
 | |
|             {
 | |
|                 retryingTransactionHelper.doInTransaction(
 | |
|                         new RetryingTransactionCallback<Void>()
 | |
|                         {
 | |
|                             public Void execute()
 | |
|                             {
 | |
|                                 remoteCredentialsService.updateCredentialsAuthenticationSucceeded(false, credentials);
 | |
|                                 return null;
 | |
|                             }
 | |
|                         }, false, true
 | |
|                 );
 | |
|             }
 | |
|             
 | |
|             // Clear old the old, invalid ticket from the cache, if it was there
 | |
|             ticketsCache.remove(cacheKey);
 | |
|             
 | |
|             // Propagate up the problem
 | |
|             throw authEx;
 | |
|         }
 | |
|         
 | |
|         // Cache the new ticket
 | |
|         ticketsCache.put(cacheKey, ticket);
 | |
|         
 | |
|         // If the credentials indicate the previous attempt failed, record as now working
 | |
|         if (! credentials.getLastAuthenticationSucceeded())
 | |
|         {
 | |
|             retryingTransactionHelper.doInTransaction(
 | |
|                     new RetryingTransactionCallback<Void>()
 | |
|                     {
 | |
|                         public Void execute()
 | |
|                         {
 | |
|                             remoteCredentialsService.updateCredentialsAuthenticationSucceeded(true, credentials);
 | |
|                             return null;
 | |
|                         }
 | |
|                     }, false, true
 | |
|             );
 | |
|         }
 | |
|         
 | |
|         // Wrap and return
 | |
|         return new AlfTicketRemoteAlfrescoTicketImpl(ticket);
 | |
|     }
 | |
| }
 |