From 19d1da8cd8189235890224bcd4071a2fd18a174f Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Tue, 24 Apr 2012 16:12:47 +0000 Subject: [PATCH] Merged BRANCHES/DEV/V4.0-BUG-FIX to HEAD: 35637: RemoteCredentialsService and RemoteAlfrescoTicketService, with tests git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@35639 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../LocalWebScriptConnectorServiceImpl.java | 295 +++++++++++++ .../RemoteAlfrescoTicketServiceTest.java | 406 ++++++++++++++++++ 2 files changed, 701 insertions(+) create mode 100644 source/java/org/alfresco/repo/remoteconnector/LocalWebScriptConnectorServiceImpl.java create mode 100644 source/java/org/alfresco/repo/remoteticket/RemoteAlfrescoTicketServiceTest.java diff --git a/source/java/org/alfresco/repo/remoteconnector/LocalWebScriptConnectorServiceImpl.java b/source/java/org/alfresco/repo/remoteconnector/LocalWebScriptConnectorServiceImpl.java new file mode 100644 index 0000000000..7ed460a5d9 --- /dev/null +++ b/source/java/org/alfresco/repo/remoteconnector/LocalWebScriptConnectorServiceImpl.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2005-2012 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.remoteconnector; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import net.sf.acegisecurity.Authentication; + +import org.alfresco.service.cmr.remoteconnector.RemoteConnectorRequest; +import org.alfresco.service.cmr.remoteconnector.RemoteConnectorResponse; +import org.alfresco.service.cmr.remoteconnector.RemoteConnectorService; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.repo.web.scripts.servlet.BasicHttpAuthenticatorFactory; +import org.alfresco.repo.web.scripts.servlet.BasicHttpAuthenticatorFactory.BasicHttpAuthenticator; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.simple.JSONObject; +import org.json.simple.parser.ParseException; +import org.springframework.extensions.webscripts.Authenticator; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer; +import org.springframework.extensions.webscripts.TestWebScriptServer.Request; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; +import org.springframework.extensions.webscripts.servlet.ServletAuthenticatorFactory; +import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest; +import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse; + +/** + * Testing implementation of {@link RemoteConnectorService} which talks to + * the local webscripts only + * + * @author Nick Burch + * @since 4.0.2 + */ +public class LocalWebScriptConnectorServiceImpl implements RemoteConnectorService +{ + /** + * The logger + */ + private static Log logger = LogFactory.getLog(LocalWebScriptConnectorServiceImpl.class); + + private WebScriptHelper helper; + private LocalAndRemoteAuthenticator auth; + + public LocalWebScriptConnectorServiceImpl(BaseWebScriptTest webScriptTest) throws Exception + { + this.helper = new WebScriptHelper(webScriptTest); + this.auth = new LocalAndRemoteAuthenticator(webScriptTest); + } + + /** + * Builds a new Request object + */ + public RemoteConnectorRequest buildRequest(String url, String method) + { + // Ensure we accept this URL + String local = "http://localhost:8080/alfresco/"; + String service = "/service/"; + if (url.startsWith(local)) + { + // Good, that's probably us, make it a relative url + url = url.substring(local.length()-1); + + // Make sure it's a service one + if (url.startsWith(service)) + { + // Strip off and use + url = url.substring(service.length()-1); + } + else + { + throw new IllegalArgumentException("Only /service/ local URLs are supported, can't handle " + url); + } + } + else + { + throw new IllegalArgumentException("Not a local URL: " + url); + } + + // Build and return + return new RemoteConnectorRequestImpl(url, method); + } + + /** + * Builds a new Request object, using HttpClient method descriptions + */ + public RemoteConnectorRequest buildRequest(String url, Class method) + { + // Get the method name + String methodName; + try + { + HttpMethodBase httpMethod = method.getConstructor(String.class).newInstance(url); + methodName = httpMethod.getName(); + } + catch(Exception e) + { + throw new AlfrescoRuntimeException("Error identifying method name", e); + } + + // Build and return + return buildRequest(url, methodName); + } + + /** + * Executes the specified request, and return the response + */ + public RemoteConnectorResponse executeRequest(RemoteConnectorRequest request) throws IOException, AuthenticationException + { + // Convert the request object + RemoteConnectorRequestImpl requestImpl = (RemoteConnectorRequestImpl)request; + Request req = new Request(request.getMethod(), request.getURL()); + req.setType(request.getContentType()); + + if (request.getRequestBody() != null) + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + requestImpl.getRequestBody().writeRequest(baos); + req.setBody(baos.toByteArray()); + } + + // Log + if (logger.isInfoEnabled()) + logger.info("Performing local " + request.getMethod() + " request to " + request.getURL()); + + // Capture the user details, as they may be changed during the request processing + Authentication fullAuth = AuthenticationUtil.getFullAuthentication(); + String runAsUser = AuthenticationUtil.getRunAsUser(); + + // If they've specified Authentication details in the request, clear our security context + // and switch to that user, to avoid our context confusing the real request + Header authHeader = null; + Map headers = new HashMap(); + for (Header header : request.getRequestHeaders()) + { + if (header.getName().equals("Authorization")) + { + authHeader = header; + } + headers.put(header.getName(), header.getValue()); + } + if (authHeader != null) + { + AuthenticationUtil.clearCurrentSecurityContext(); + if (logger.isDebugEnabled()) + logger.debug("HTTP Authorization found for the request, clearing security context, Auth is " + authHeader); + } + req.setHeaders(headers); + + // Execute the request against the WebScript Test Framework + Response resp; + try + { + resp = helper.sendRequest(req, -1); + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Problem requesting", e); + } + + // Reset the user details, now we're done performing the request + AuthenticationUtil.setFullAuthentication(fullAuth); + if (runAsUser != null && !runAsUser.equals(fullAuth.getName())) + { + AuthenticationUtil.setRunAsUser(runAsUser); + } + + // Log + if (logger.isInfoEnabled()) + logger.info("Response to request was " + resp.getStatus() + " - " + resp); + + // Check the status + if (resp.getStatus() == Status.STATUS_UNAUTHORIZED) + { + throw new AuthenticationException("Not Authorized to access this resource"); + } + if (resp.getStatus() == Status.STATUS_FORBIDDEN) + { + throw new AuthenticationException("Forbidden to access this resource"); + } + // TODO Handle others too + + // Convert the response + String charset = null; + String contentType = resp.getContentType(); + if (contentType != null && contentType.contains("charset=")) + { + int splitAt = contentType.indexOf("charset=") + "charset=".length(); + charset = contentType.substring(splitAt); + } + + InputStream body = new ByteArrayInputStream(resp.getContentAsByteArray()); + Header[] respHeaders = new Header[0]; // TODO Can't easily get the list... + + RemoteConnectorResponse response = new RemoteConnectorResponseImpl(request, contentType, charset, respHeaders, body); + return response; + } + + /** + * Executes the given request, requesting a JSON response, and + * returns the parsed JSON received back + * + * @throws ParseException If the response is not valid JSON + */ + public JSONObject executeJSONRequest(RemoteConnectorRequest request) throws ParseException, IOException, AuthenticationException + { + return RemoteConnectorServiceImpl.doExecuteJSONRequest(request, this); + } + + private static class WebScriptHelper + { + private BaseWebScriptTest test; + private Method sendRequest; + + private WebScriptHelper(BaseWebScriptTest test) throws Exception + { + this.test = test; + + sendRequest = BaseWebScriptTest.class.getDeclaredMethod("sendRequest", Request.class, Integer.TYPE); + sendRequest.setAccessible(true); + } + + private Response sendRequest(Request request, int expectedStatus) throws Exception + { + return (Response)sendRequest.invoke(test, request, expectedStatus); + } + } + + /** + * A wrapper around {@link BasicHttpAuthenticator}, which uses the + * Authentication Context if present, otherwise HTTP Auth + */ + private static class LocalAndRemoteAuthenticator implements ServletAuthenticatorFactory + { + private BasicHttpAuthenticatorFactory httpAuthFactory; + + private LocalAndRemoteAuthenticator(BaseWebScriptTest test) throws Exception + { + // Get the test server + Method getServer = BaseWebScriptTest.class.getDeclaredMethod("getServer"); + getServer.setAccessible(true); + TestWebScriptServer server = (TestWebScriptServer)getServer.invoke(test); + + // Grab the real auth factory from the context + httpAuthFactory = (BasicHttpAuthenticatorFactory)server.getApplicationContext().getBean("webscripts.authenticator.basic"); + + // Wire us into the test + server.setServletAuthenticatorFactory(this); + } + + @Override + public Authenticator create(WebScriptServletRequest req, WebScriptServletResponse res) + { + // Do we have current details? + if (AuthenticationUtil.getFullyAuthenticatedUser() != null) + { + // There are already details existing + // Allow these to be kept and used + logger.debug("Existing Authentication found, remaining as " + AuthenticationUtil.getFullyAuthenticatedUser()); + return null; + } + + // Fall back to the http auth one + logger.debug("No existing Authentication found, using regular HTTP Auth"); + return httpAuthFactory.create(req, res); + } + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/remoteticket/RemoteAlfrescoTicketServiceTest.java b/source/java/org/alfresco/repo/remoteticket/RemoteAlfrescoTicketServiceTest.java new file mode 100644 index 0000000000..4ce134ba5f --- /dev/null +++ b/source/java/org/alfresco/repo/remoteticket/RemoteAlfrescoTicketServiceTest.java @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2005-2012 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.remoteticket; + +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.repo.remoteconnector.LocalWebScriptConnectorServiceImpl; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.cache.EhCacheAdapter; +import org.alfresco.repo.remotecredentials.PasswordCredentialsInfoImpl; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.remotecredentials.BaseCredentialsInfo; +import org.alfresco.service.cmr.remotecredentials.RemoteCredentialsService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.util.PropertyMap; + +/** + * Tests for {@link RemoteAlfrescoTicketServiceImpl}, which work by + * looping back to the local repo. Because this tests talks to local + * webscripts, it needs to be run in the Remote API package + * + * TODO Test OAuth support, once added + * + * @author Nick Burch + * @since 4.0.2 + */ +public class RemoteAlfrescoTicketServiceTest extends BaseWebScriptTest +{ + private static final String TEST_REMOTE_SYSTEM_ID = "testingRemoteSystem"; + private static final String INVALID_REMOTE_SYSTEM_ID = "testingInvalidRemoteSystem"; + + private MutableAuthenticationService authenticationService; + private RetryingTransactionHelper retryingTransactionHelper; + private PersonService personService; + + private RemoteAlfrescoTicketService remoteAlfrescoTicketService; + private RemoteCredentialsService remoteCredentialsService; + private EhCacheAdapter ticketsCache; + + private static final String USER_ONE = "UserOneSecondToo"; + private static final String USER_TWO = "UserTwoSecondToo"; + private static final String PASSWORD = "passwordTEST"; + + // General methods + + @Override + @SuppressWarnings("unchecked") + protected void setUp() throws Exception + { + super.setUp(); + + this.retryingTransactionHelper = (RetryingTransactionHelper)getServer().getApplicationContext().getBean("retryingTransactionHelper"); + this.authenticationService = (MutableAuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService"); + this.personService = (PersonService)getServer().getApplicationContext().getBean("PersonService"); + + this.remoteAlfrescoTicketService = (RemoteAlfrescoTicketService)getServer().getApplicationContext().getBean("remoteAlfrescoTicketService"); + this.remoteCredentialsService = (RemoteCredentialsService)getServer().getApplicationContext().getBean("RemoteCredentialsService"); + this.ticketsCache = (EhCacheAdapter)getServer().getApplicationContext().getBean("remoteAlfrescoTicketService.ticketsCache"); + + // Do the setup as admin + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + + // Add our local system as a remote service + remoteAlfrescoTicketService.registerRemoteSystem(TEST_REMOTE_SYSTEM_ID, "http://localhost:8080/alfresco/service/", null); + + // Wire up the loop-back connector + ((RemoteAlfrescoTicketServiceImpl)remoteAlfrescoTicketService).setRemoteConnectorService( + new LocalWebScriptConnectorServiceImpl(this)); + + // Ensure the invalid one isn't registered + remoteAlfrescoTicketService.registerRemoteSystem(INVALID_REMOTE_SYSTEM_ID, null, null); + + // Create users + createUser(USER_ONE); + createUser(USER_TWO); + + // Do tests as first user + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + + // Admin user required to delete user + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + + // Delete the users, which also zaps their credentials + if(personService.personExists(USER_ONE)) + { + personService.deletePerson(USER_ONE); + } + if(this.authenticationService.authenticationExists(USER_ONE)) + { + this.authenticationService.deleteAuthentication(USER_ONE); + } + + if(personService.personExists(USER_TWO)) + { + personService.deletePerson(USER_TWO); + } + if(this.authenticationService.authenticationExists(USER_TWO)) + { + this.authenticationService.deleteAuthentication(USER_TWO); + } + + // Unregister the system + remoteAlfrescoTicketService.registerRemoteSystem(TEST_REMOTE_SYSTEM_ID, null, null); + } + + private void createUser(String userName) + { + // if user with given user name doesn't already exist then create user + if (this.authenticationService.authenticationExists(userName) == false) + { + // create user + this.authenticationService.createAuthentication(userName, PASSWORD.toCharArray()); + + // create person properties + PropertyMap personProps = new PropertyMap(); + personProps.put(ContentModel.PROP_USERNAME, userName); + personProps.put(ContentModel.PROP_FIRSTNAME, "First"); + personProps.put(ContentModel.PROP_LASTNAME, "Last"); + personProps.put(ContentModel.PROP_EMAIL, "FirstName123.LastName123@email.com"); + personProps.put(ContentModel.PROP_JOBTITLE, "JobTitle123"); + personProps.put(ContentModel.PROP_JOBTITLE, "Organisation123"); + + // create person node for user + this.personService.createPerson(personProps); + } + } + + /** + * Getting, storing and fetching credentials + */ + public void testGetStoreGetCredentials() throws Exception + { + // Run this test initially as the first user + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + + // First, try an invalid system + try + { + remoteAlfrescoTicketService.getRemoteCredentials(INVALID_REMOTE_SYSTEM_ID); + fail("Shouldn't work for an invalid system"); + } + catch(NoSuchSystemException e) {} + try + { + remoteAlfrescoTicketService.storeRemoteCredentials(INVALID_REMOTE_SYSTEM_ID, null, null); + fail("Shouldn't work for an invalid system"); + } + catch(NoSuchSystemException e) {} + + + // Our user starts out without credentials + BaseCredentialsInfo credentials = + remoteAlfrescoTicketService.getRemoteCredentials(TEST_REMOTE_SYSTEM_ID); + assertEquals(null, credentials); + + + // Try to store some invalid credentials (real user, but password wrong) + try + { + remoteAlfrescoTicketService.storeRemoteCredentials(TEST_REMOTE_SYSTEM_ID, USER_ONE, "invalid"); + fail("Credentials invalid, shouldn't be allowed"); + } + catch (AuthenticationException e) {} + + // And an invalid user + try + { + remoteAlfrescoTicketService.storeRemoteCredentials(TEST_REMOTE_SYSTEM_ID, "thisUSERdoesNOTexist", "invalid"); + fail("Credentials invalid, shouldn't be allowed"); + } + catch (AuthenticationException e) {} + + + // Still none there + credentials = remoteAlfrescoTicketService.getRemoteCredentials(TEST_REMOTE_SYSTEM_ID); + assertEquals(null, credentials); + + + // Store some valid ones + credentials = remoteAlfrescoTicketService.storeRemoteCredentials(TEST_REMOTE_SYSTEM_ID, USER_ONE, PASSWORD); + assertNotNull(credentials); + assertEquals(TEST_REMOTE_SYSTEM_ID, credentials.getRemoteSystemName()); + assertEquals(USER_ONE, credentials.getRemoteUsername()); + + // Check we can find them + credentials = remoteAlfrescoTicketService.getRemoteCredentials(TEST_REMOTE_SYSTEM_ID); + assertNotNull(credentials); + assertEquals(TEST_REMOTE_SYSTEM_ID, credentials.getRemoteSystemName()); + assertEquals(USER_ONE, credentials.getRemoteUsername()); + + + // Store some different, valid credentials for the user + credentials = remoteAlfrescoTicketService.storeRemoteCredentials(TEST_REMOTE_SYSTEM_ID, USER_TWO, PASSWORD); + assertNotNull(credentials); + assertEquals(TEST_REMOTE_SYSTEM_ID, credentials.getRemoteSystemName()); + assertEquals(USER_TWO, credentials.getRemoteUsername()); + + // Check we see the change + credentials = remoteAlfrescoTicketService.getRemoteCredentials(TEST_REMOTE_SYSTEM_ID); + assertNotNull(credentials); + assertEquals(TEST_REMOTE_SYSTEM_ID, credentials.getRemoteSystemName()); + assertEquals(USER_TWO, credentials.getRemoteUsername()); + + + // Switch to the other user, no credentials there + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + credentials = remoteAlfrescoTicketService.getRemoteCredentials(TEST_REMOTE_SYSTEM_ID); + assertEquals(null, credentials); + + + // Switch back, and delete + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + credentials = remoteAlfrescoTicketService.getRemoteCredentials(TEST_REMOTE_SYSTEM_ID); + assertNotNull(credentials); + + boolean deleted = remoteAlfrescoTicketService.deleteRemoteCredentials(TEST_REMOTE_SYSTEM_ID); + assertEquals(true, deleted); + + // Will have gone + credentials = remoteAlfrescoTicketService.getRemoteCredentials(TEST_REMOTE_SYSTEM_ID); + assertEquals(null, credentials); + + // Double delete is reported + deleted = remoteAlfrescoTicketService.deleteRemoteCredentials(TEST_REMOTE_SYSTEM_ID); + assertEquals(false, deleted); + } + + /** + * Getting cached and non-cached credentials + */ + public void testGetTicket() throws Exception + { + // Run this test initially as the first user + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + + // First, try an invalid system + try + { + remoteAlfrescoTicketService.getAlfrescoTicket(INVALID_REMOTE_SYSTEM_ID); + fail("Shouldn't work for an invalid system"); + } + catch(NoSuchSystemException e) {} + try + { + remoteAlfrescoTicketService.refetchAlfrescoTicket(INVALID_REMOTE_SYSTEM_ID); + fail("Shouldn't work for an invalid system"); + } + catch(NoSuchSystemException e) {} + + + // Can't get or refresh if no credentials exist + try + { + remoteAlfrescoTicketService.getAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + fail("Shouldn't work when no credentials"); + } + catch(NoCredentialsFoundException e) {} + try + { + remoteAlfrescoTicketService.refetchAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + fail("Shouldn't work when no credentials"); + } + catch(NoCredentialsFoundException e) {} + + + // Have some stored + remoteAlfrescoTicketService.storeRemoteCredentials(TEST_REMOTE_SYSTEM_ID, USER_ONE, PASSWORD); + + + // A ticket will now exist + RemoteAlfrescoTicketInfo ticket = remoteAlfrescoTicketService.getAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + assertNotNull(ticket); + assertNotNull(ticket.getAsUrlParameters()); + + + // Ask again, will get the same one + RemoteAlfrescoTicketInfo ticket2 = remoteAlfrescoTicketService.getAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + assertNotNull(ticket2); + assertEquals(ticket.getAsUrlParameters(), ticket2.getAsUrlParameters()); + + + // Force a re-fetch, will get another + RemoteAlfrescoTicketInfo ticket3 = remoteAlfrescoTicketService.refetchAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + assertNotNull(ticket3); + assertNotSame(ticket.getAsUrlParameters(), ticket3.getAsUrlParameters()); + + // Ask for the ticket again, get the 2nd one again + RemoteAlfrescoTicketInfo ticket4 = remoteAlfrescoTicketService.getAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + assertNotNull(ticket4); + assertEquals(ticket3.getAsUrlParameters(), ticket4.getAsUrlParameters()); + + + // Zap from the cache, will trigger another to be fetched + ticketsCache.clear(); + + RemoteAlfrescoTicketInfo ticket5 = remoteAlfrescoTicketService.getAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + assertNotNull(ticket5); + assertNotSame(ticket.getAsUrlParameters(), ticket5.getAsUrlParameters()); + assertNotSame(ticket3.getAsUrlParameters(), ticket5.getAsUrlParameters()); + + + // Change the password so it's no longer valid + PasswordCredentialsInfoImpl creds = (PasswordCredentialsInfoImpl)remoteCredentialsService.getPersonCredentials(TEST_REMOTE_SYSTEM_ID); + assertNotNull(creds); + creds.setRemotePassword("INVALID"); + remoteCredentialsService.updateCredentials(creds); + + // Currently will be marked as still working + assertEquals(true, creds.getLastAuthenticationSucceeded()); + + + // Get will work, as ticket was previously cached + RemoteAlfrescoTicketInfo ticket6 = remoteAlfrescoTicketService.getAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + assertNotNull(ticket6); + assertEquals(ticket5.getAsUrlParameters(), ticket6.getAsUrlParameters()); + + // Re-fetch will fail with authentication error + try + { + remoteAlfrescoTicketService.refetchAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + fail("Shouldn't be able to refetch with wrong details"); + } + catch(AuthenticationException e) {} + + // Now a get will fail too, as the cache will be invalidated + try + { + remoteAlfrescoTicketService.getAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + fail("Shouldn't be able to get after refresh with wrong details"); + } + catch(AuthenticationException e) {} + + + // If we check the credentials, will now be marked as failing + creds = (PasswordCredentialsInfoImpl)remoteCredentialsService.getPersonCredentials(TEST_REMOTE_SYSTEM_ID); + assertEquals(false, creds.getLastAuthenticationSucceeded()); + + + // Change the password back to what it should be, and re-get + creds.setRemotePassword(PASSWORD); + remoteCredentialsService.updateCredentials(creds); + + RemoteAlfrescoTicketInfo ticket7 = remoteAlfrescoTicketService.getAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + assertNotNull(ticket7); + assertNotSame(ticket.getAsUrlParameters(), ticket7.getAsUrlParameters()); + assertNotSame(ticket3.getAsUrlParameters(), ticket7.getAsUrlParameters()); + assertNotSame(ticket5.getAsUrlParameters(), ticket7.getAsUrlParameters()); + + // Should now be marked as working again + creds = (PasswordCredentialsInfoImpl)remoteCredentialsService.getPersonCredentials(TEST_REMOTE_SYSTEM_ID); + assertEquals(true, creds.getLastAuthenticationSucceeded()); + + + // Check that failure can be marked in a read only transaction + creds.setRemotePassword("INVALID"); + remoteCredentialsService.updateCredentials(creds); + + retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() { + public Void execute() + { + try + { + remoteAlfrescoTicketService.refetchAlfrescoTicket(TEST_REMOTE_SYSTEM_ID); + fail("Shouldn't be able to refetch with wrong details"); + } + catch(AuthenticationException e) {} + return null; + } + }, true, true); + + // Check it was still marked as invalid, despite a read only transaction + creds = (PasswordCredentialsInfoImpl)remoteCredentialsService.getPersonCredentials(TEST_REMOTE_SYSTEM_ID); + assertEquals(false, creds.getLastAuthenticationSucceeded()); + } +}