diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index fd5a8f35ca..6a5c4fb0ca 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -55,7 +55,7 @@ - + @@ -122,19 +122,6 @@ - @@ -166,7 +153,7 @@ - + @@ -199,7 +186,7 @@ - + diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java index ebf289b554..2c0148e676 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java @@ -16,6 +16,9 @@ */ package org.alfresco.repo.security.authentication; +import java.util.Collections; +import java.util.Set; + import org.alfresco.service.cmr.security.AuthenticationService; public class AuthenticationServiceImpl implements AuthenticationService @@ -26,6 +29,14 @@ public class AuthenticationServiceImpl implements AuthenticationService TicketComponent ticketComponent; + private String domain; + + private boolean allowsUserCreation = true; + + private boolean allowsUserDeletion = true; + + private boolean allowsUserPasswordChange = true; + public AuthenticationServiceImpl() { super(); @@ -142,6 +153,90 @@ public class AuthenticationServiceImpl implements AuthenticationService { authenticationComponent.setGuestUserAsCurrentUser(); } + + + public boolean getAllowsUserCreation() + { + return allowsUserCreation; + } + + public void setAllowsUserCreation(boolean allowsUserCreation) + { + this.allowsUserCreation = allowsUserCreation; + } + + public boolean getAllowsUserDeletion() + { + return allowsUserDeletion; + } + + public void setAllowsUserDeletion(boolean allowsUserDeletion) + { + this.allowsUserDeletion = allowsUserDeletion; + } + + public boolean getAllowsUserPasswordChange() + { + return allowsUserPasswordChange; + } + + public void setAllowsUserPasswordChange(boolean allowsUserPasswordChange) + { + this.allowsUserPasswordChange = allowsUserPasswordChange; + } + + public String getDomain() + { + return domain; + } + + public void setDomain(String domain) + { + this.domain = domain; + } + + public Set getDomains() + { + return Collections.singleton(getDomain()); + } + + public Set getDomainsThatAllowUserCreation() + { + if(getAllowsUserCreation()) + { + return Collections.singleton(getDomain()); + } + else + { + return Collections.emptySet(); + } + } + + public Set getDomainsThatAllowUserDeletion() + { + if(getAllowsUserDeletion()) + { + return Collections.singleton(getDomain()); + } + else + { + return Collections.emptySet(); + } + } + + public Set getDomiansThatAllowUserPasswordChanges() + { + if(getAllowsUserPasswordChange()) + { + return Collections.singleton(getDomain()); + } + else + { + return Collections.emptySet(); + } + } + + } diff --git a/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceImpl.java b/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceImpl.java new file mode 100644 index 0000000000..cb6276ca44 --- /dev/null +++ b/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceImpl.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.security.authentication; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.alfresco.service.cmr.security.AuthenticationService; + +/** + * This class implements a simple chaining authentication service. + * + * It chains together other authentication services so that authentication can happen against more than one authentication service. + * + * The authentication services it uses are stored as a list. + * + * Each authentication service must belong to the same domain. This is checked at configuration time. + * + * Authentication will try each authentication service in order. If any allow authentication given the user name and password then the user will be accepted. + * + * Additions, deletions and password changes are made to one special authentication service. This service will be tried first for authentication. Users can not be created if they + * exist in another authentication service. + * + * To avoid transactional issues in chaining, the services registered with this service must not have transactional wrappers. If not, errors will mark the transaction for roll back + * and we can not chain down the list of authentication services. + * + * + * @author Andy Hind + */ +public class ChainingAuthenticationServiceImpl implements AuthenticationService +{ + + private List authenticationServices; + + private AuthenticationService mutableAuthenticationService; + + public ChainingAuthenticationServiceImpl() + { + super(); + } + + public List getAuthenticationServices() + { + return authenticationServices; + } + + public void setAuthenticationServices(List authenticationServices) + { + this.authenticationServices = authenticationServices; + } + + public AuthenticationService getMutableAuthenticationService() + { + return mutableAuthenticationService; + } + + public void setMutableAuthenticationService(AuthenticationService mutableAuthenticationService) + { + this.mutableAuthenticationService = mutableAuthenticationService; + } + + public void createAuthentication(String userName, char[] password) throws AuthenticationException + { + if (mutableAuthenticationService == null) + { + throw new AuthenticationException( + "Unable to create authentication as there is no suitable authentication service."); + } + mutableAuthenticationService.createAuthentication(userName, password); + } + + public void updateAuthentication(String userName, char[] oldPassword, char[] newPassword) + throws AuthenticationException + { + if (mutableAuthenticationService == null) + { + throw new AuthenticationException( + "Unable to update authentication as there is no suitable authentication service."); + } + mutableAuthenticationService.updateAuthentication(userName, oldPassword, newPassword); + + } + + public void setAuthentication(String userName, char[] newPassword) throws AuthenticationException + { + if (mutableAuthenticationService == null) + { + throw new AuthenticationException( + "Unable to set authentication as there is no suitable authentication service."); + } + mutableAuthenticationService.setAuthentication(userName, newPassword); + } + + public void deleteAuthentication(String userName) throws AuthenticationException + { + if (mutableAuthenticationService == null) + { + throw new AuthenticationException( + "Unable to delete authentication as there is no suitable authentication service."); + } + mutableAuthenticationService.deleteAuthentication(userName); + + } + + public void setAuthenticationEnabled(String userName, boolean enabled) throws AuthenticationException + { + if (mutableAuthenticationService == null) + { + throw new AuthenticationException( + "Unable to set authentication enabled as there is no suitable authentication service."); + } + mutableAuthenticationService.setAuthenticationEnabled(userName, enabled); + } + + public boolean getAuthenticationEnabled(String userName) throws AuthenticationException + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + if (authService.getAuthenticationEnabled(userName)) + { + return true; + } + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + return false; + } + + public void authenticate(String userName, char[] password) throws AuthenticationException + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + authService.authenticate(userName, password); + return; + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + throw new AuthenticationException("Failed to authenticate"); + + } + + public void authenticateAsGuest() throws AuthenticationException + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + authService.authenticateAsGuest(); + return; + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + throw new AuthenticationException("Guest authentication not supported"); + + } + + public String getCurrentUserName() throws AuthenticationException + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + return authService.getCurrentUserName(); + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + return null; + } + + public void invalidateUserSession(String userName) throws AuthenticationException + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + authService.invalidateUserSession(userName); + return; + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + throw new AuthenticationException("Unable to invalidate user session"); + + } + + public void invalidateTicket(String ticket) throws AuthenticationException + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + authService.invalidateTicket(ticket); + return; + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + throw new AuthenticationException("Unable to invalidate ticket"); + + } + + public void validate(String ticket) throws AuthenticationException + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + authService.validate(ticket); + return; + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + throw new AuthenticationException("Unable to validate ticket"); + + } + + public String getCurrentTicket() + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + return authService.getCurrentTicket(); + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + return null; + } + + public void clearCurrentSecurityContext() + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + authService.clearCurrentSecurityContext(); + return; + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + throw new AuthenticationException("Failed to clear security context"); + + } + + public boolean isCurrentUserTheSystemUser() + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + try + { + return authService.isCurrentUserTheSystemUser(); + } + catch (AuthenticationException e) + { + // Ignore and chain + } + } + return false; + } + + private List getUsableAuthenticationServices() + { + if (mutableAuthenticationService == null) + { + return authenticationServices; + } + else + { + ArrayList services = new ArrayList( + authenticationServices == null ? 1 : (authenticationServices.size() + 1)); + services.add(mutableAuthenticationService); + if (authenticationServices != null) + { + services.addAll(authenticationServices); + } + return services; + } + } + + public Set getDomains() + { + HashSet domains = new HashSet(); + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + domains.addAll(authService.getDomains()); + } + return domains; + } + + public Set getDomainsThatAllowUserCreation() + { + HashSet domains = new HashSet(); + if (mutableAuthenticationService != null) + { + domains.addAll(mutableAuthenticationService.getDomainsThatAllowUserCreation()); + } + return domains; + } + + public Set getDomainsThatAllowUserDeletion() + { + HashSet domains = new HashSet(); + if (mutableAuthenticationService != null) + { + domains.addAll(mutableAuthenticationService.getDomainsThatAllowUserDeletion()); + } + return domains; + } + + public Set getDomiansThatAllowUserPasswordChanges() + { + HashSet domains = new HashSet(); + if (mutableAuthenticationService != null) + { + domains.addAll(mutableAuthenticationService.getDomiansThatAllowUserPasswordChanges()); + } + return domains; + } + +} diff --git a/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceTest.java b/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceTest.java new file mode 100644 index 0000000000..88930dae26 --- /dev/null +++ b/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceTest.java @@ -0,0 +1,654 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.security.authentication; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; + +import junit.framework.TestCase; + +public class ChainingAuthenticationServiceTest extends TestCase +{ + private static final String EMPTY = "Empty"; + + private static final String FIVE_AND_MORE = "FiveAndMore"; + + private static final String FIVE = "Five"; + + private static final String LONELY_DISABLE = "LonelyDisable"; + + private static final String LONELY_ENABLED = "LonelyEnabled"; + + private static final String ALFRESCO = "Alfresco"; + + TestAuthenticationServiceImpl service1; + + TestAuthenticationServiceImpl service2; + + TestAuthenticationServiceImpl service3; + + TestAuthenticationServiceImpl service4; + + TestAuthenticationServiceImpl service5; + + private TestAuthenticationServiceImpl service6; + + public ChainingAuthenticationServiceTest() + { + super(); + } + + public ChainingAuthenticationServiceTest(String arg0) + { + super(arg0); + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + service1 = new TestAuthenticationServiceImpl(ALFRESCO, true, true, true, false); + service1.createAuthentication("andy", "andy".toCharArray()); + + HashMap up = new HashMap(); + HashSet disabled = new HashSet(); + up.put("lone", "lone"); + service2 = new TestAuthenticationServiceImpl(LONELY_ENABLED, false, false, false, true, up, disabled); + + up.clear(); + disabled.clear(); + + up.put("ranger", "ranger"); + disabled.add("ranger"); + + service3 = new TestAuthenticationServiceImpl(LONELY_DISABLE, false, false, false, false, up, disabled); + + service4 = new TestAuthenticationServiceImpl(EMPTY, true, true, true, false); + + up.clear(); + disabled.clear(); + + up.put("A", "A"); + up.put("B", "B"); + up.put("C", "C"); + up.put("D", "D"); + up.put("E", "E"); + service5 = new TestAuthenticationServiceImpl(FIVE, false, false, false, false, up, disabled); + + up.clear(); + disabled.clear(); + + up.put("A", "a"); + up.put("B", "b"); + up.put("C", "c"); + up.put("D", "d"); + up.put("E", "e"); + up.put("F", "f"); + up.put("G", "g"); + up.put("H", "h"); + up.put("I", "i"); + up.put("J", "j"); + up.put("K", "k"); + service6 = new TestAuthenticationServiceImpl(FIVE_AND_MORE, false, false, false, false, up, disabled); + } + + // + // Single service test + // + + public void testServiceOne_Auth() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service1); + as.setAuthenticationServices(ases); + as.authenticate("andy", "andy".toCharArray()); + assertEquals(as.getCurrentUserName(), "andy"); + } + + public void testServiceOne_AuthFail() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service1); + as.setAuthenticationServices(ases); + try + { + as.authenticate("andy", "woof".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + } + + public void testServiceOne_GuestDenied() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service1); + as.setAuthenticationServices(ases); + try + { + as.authenticateAsGuest(); + fail(); + } + catch (AuthenticationException e) + { + + } + + } + + public void testServiceTwo_GuestAllowed() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service2); + as.setAuthenticationServices(ases); + as.authenticateAsGuest(); + assertEquals(as.getCurrentUserName(), PermissionService.GUEST_AUTHORITY); + as.clearCurrentSecurityContext(); + assertNull(as.getCurrentUserName()); + } + + public void testServiceOne_CRUD_Fails() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service1); + as.setAuthenticationServices(ases); + try + { + as.authenticate("bob", "bob".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + try + { + as.createAuthentication("bob", "bob".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + } + + public void testServiceOne_CRUD() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + as.setMutableAuthenticationService(service1); + try + { + as.authenticate("bob", "bob".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + as.createAuthentication("bob", "bob".toCharArray()); + as.authenticate("bob", "bob".toCharArray()); + as.updateAuthentication("bob", "bob".toCharArray(), "carol".toCharArray()); + try + { + as.authenticate("bob", "bob".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + as.authenticate("bob", "carol".toCharArray()); + as.deleteAuthentication("bob"); + try + { + as.authenticate("bob", "carol".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + } + + public void testServiceOne_Enabled() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + as.setMutableAuthenticationService(service1); + + assertTrue(as.getAuthenticationEnabled("andy")); + + as.setAuthenticationEnabled("andy", false); + assertFalse(as.getAuthenticationEnabled("andy")); + + as.setAuthenticationEnabled("andy", true); + assertTrue(as.getAuthenticationEnabled("andy")); + as.authenticate("andy", "andy".toCharArray()); + + as.setAuthenticationEnabled("andy", false); + assertFalse(as.getAuthenticationEnabled("andy")); + + try + { + as.authenticate("andy", "andy".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + } + + public void testServiceOneDomains() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + as.setMutableAuthenticationService(service1); + + HashSet testDomains = new HashSet(); + testDomains.add(ALFRESCO); + + assertTrue(as.getDomains().equals(testDomains)); + assertTrue(as.getDomainsThatAllowUserCreation().equals(testDomains)); + assertTrue(as.getDomainsThatAllowUserDeletion().equals(testDomains)); + assertTrue(as.getDomiansThatAllowUserPasswordChanges().equals(testDomains)); + assertTrue(as.getDomains().equals(service1.getDomains())); + assertTrue(as.getDomainsThatAllowUserCreation().equals(service1.getDomainsThatAllowUserCreation())); + assertTrue(as.getDomainsThatAllowUserDeletion().equals(service1.getDomainsThatAllowUserDeletion())); + assertTrue(as.getDomiansThatAllowUserPasswordChanges() + .equals(service1.getDomiansThatAllowUserPasswordChanges())); + + } + + public void testServiceOneTickets() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + as.setMutableAuthenticationService(service1); + as.authenticate("andy", "andy".toCharArray()); + + String ticket = as.getCurrentTicket(); + assertTrue(ticket == as.getCurrentTicket()); + + as.validate(ticket); + as.invalidateTicket(ticket); + try + { + as.validate(ticket); + fail(); + } + catch (AuthenticationException e) + { + + } + + ticket = as.getCurrentTicket(); + as.validate(ticket); + as.invalidateUserSession("andy"); + try + { + as.validate(ticket); + fail(); + } + catch (AuthenticationException e) + { + + } + } + + // + // Multi service tests + // + + public void testAll_Auth() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service2); + ases.add(service3); + ases.add(service4); + ases.add(service5); + ases.add(service6); + as.setAuthenticationServices(ases); + as.setMutableAuthenticationService(service1); + + as.authenticate("andy", "andy".toCharArray()); + assertEquals(as.getCurrentUserName(), "andy"); + as.authenticate("lone", "lone".toCharArray()); + try + { + as.authenticate("ranger", "ranger".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + as.authenticate("A", "A".toCharArray()); + as.authenticate("B", "B".toCharArray()); + as.authenticate("C", "C".toCharArray()); + as.authenticate("D", "D".toCharArray()); + as.authenticate("E", "E".toCharArray()); + as.authenticate("A", "a".toCharArray()); + as.authenticate("B", "b".toCharArray()); + as.authenticate("C", "c".toCharArray()); + as.authenticate("D", "d".toCharArray()); + as.authenticate("E", "e".toCharArray()); + as.authenticate("F", "f".toCharArray()); + as.authenticate("G", "g".toCharArray()); + as.authenticate("H", "h".toCharArray()); + as.authenticate("I", "i".toCharArray()); + as.authenticate("J", "j".toCharArray()); + as.authenticate("K", "k".toCharArray()); + } + + public void testAll_AuthOverLapReversed() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service2); + ases.add(service3); + ases.add(service4); + ases.add(service6); + ases.add(service5); + as.setAuthenticationServices(ases); + as.setMutableAuthenticationService(service1); + + as.authenticate("andy", "andy".toCharArray()); + assertEquals(as.getCurrentUserName(), "andy"); + as.authenticate("lone", "lone".toCharArray()); + try + { + as.authenticate("ranger", "ranger".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + + try + { + as.authenticate("A", "B".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + as.authenticate("A", "A".toCharArray()); + as.authenticate("B", "B".toCharArray()); + as.authenticate("C", "C".toCharArray()); + as.authenticate("D", "D".toCharArray()); + as.authenticate("E", "E".toCharArray()); + as.authenticate("A", "a".toCharArray()); + as.authenticate("B", "b".toCharArray()); + as.authenticate("C", "c".toCharArray()); + as.authenticate("D", "d".toCharArray()); + as.authenticate("E", "e".toCharArray()); + as.authenticate("F", "f".toCharArray()); + as.authenticate("G", "g".toCharArray()); + as.authenticate("H", "h".toCharArray()); + as.authenticate("I", "i".toCharArray()); + as.authenticate("J", "j".toCharArray()); + as.authenticate("K", "k".toCharArray()); + } + + public void testAll_MutAuth() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service2); + ases.add(service3); + ases.add(service4); + ases.add(service5); + ases.add(service6); + as.setAuthenticationServices(ases); + as.setMutableAuthenticationService(service1); + + as.authenticate("andy", "andy".toCharArray()); + assertEquals(as.getCurrentUserName(), "andy"); + as.authenticate("lone", "lone".toCharArray()); + try + { + as.authenticate("ranger", "ranger".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + as.authenticate("A", "A".toCharArray()); + as.authenticate("B", "B".toCharArray()); + as.authenticate("C", "C".toCharArray()); + as.authenticate("D", "D".toCharArray()); + as.authenticate("E", "E".toCharArray()); + as.authenticate("A", "a".toCharArray()); + as.authenticate("B", "b".toCharArray()); + as.authenticate("C", "c".toCharArray()); + as.authenticate("D", "d".toCharArray()); + as.authenticate("E", "e".toCharArray()); + as.authenticate("F", "f".toCharArray()); + as.authenticate("G", "g".toCharArray()); + as.authenticate("H", "h".toCharArray()); + as.authenticate("I", "i".toCharArray()); + as.authenticate("J", "j".toCharArray()); + as.authenticate("K", "k".toCharArray()); + + as.createAuthentication("A", "woof".toCharArray()); + as.authenticate("A", "woof".toCharArray()); + as.updateAuthentication("A", "woof".toCharArray(), "bark".toCharArray()); + as.authenticate("A", "bark".toCharArray()); + as.setAuthentication("A", "tree".toCharArray()); + as.authenticate("A", "tree".toCharArray()); + as.deleteAuthentication("A"); + as.authenticate("A", "A".toCharArray()); + as.authenticate("A", "a".toCharArray()); + try + { + as.authenticate("A", "woof".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + try + { + as.authenticate("A", "bark".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + try + { + as.authenticate("A", "tree".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + } + + public void testAll_AuthEnabled() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service2); + ases.add(service3); + ases.add(service4); + ases.add(service5); + ases.add(service6); + as.setAuthenticationServices(ases); + as.setMutableAuthenticationService(service1); + + assertTrue(as.getAuthenticationEnabled("andy")); + assertTrue(as.getAuthenticationEnabled("lone")); + assertFalse(as.getAuthenticationEnabled("ranger")); + assertTrue(as.getAuthenticationEnabled("A")); + assertTrue(as.getAuthenticationEnabled("B")); + assertTrue(as.getAuthenticationEnabled("C")); + assertTrue(as.getAuthenticationEnabled("D")); + assertTrue(as.getAuthenticationEnabled("E")); + assertTrue(as.getAuthenticationEnabled("F")); + assertTrue(as.getAuthenticationEnabled("G")); + assertTrue(as.getAuthenticationEnabled("H")); + assertTrue(as.getAuthenticationEnabled("I")); + assertTrue(as.getAuthenticationEnabled("J")); + assertTrue(as.getAuthenticationEnabled("K")); + + as.setAuthenticationEnabled("andy", false); + assertFalse(as.getAuthenticationEnabled("andy")); + as.setAuthenticationEnabled("andy", true); + assertTrue(as.getAuthenticationEnabled("andy")); + as.setAuthenticationEnabled("andy", false); + + try + { + as.authenticate("andy", "andy".toCharArray()); + fail(); + } + catch (AuthenticationException e) + { + + } + } + + public void testService_GuestDenied() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service1); + ases.add(service3); + ases.add(service4); + ases.add(service5); + ases.add(service6); + as.setAuthenticationServices(ases); + try + { + as.authenticateAsGuest(); + fail(); + } + catch (AuthenticationException e) + { + + } + + } + + public void testService_GuestAllowed() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service1); + ases.add(service2); + ases.add(service3); + ases.add(service4); + ases.add(service5); + ases.add(service6); + as.setAuthenticationServices(ases); + as.authenticateAsGuest(); + assertEquals(as.getCurrentUserName(), PermissionService.GUEST_AUTHORITY); + as.clearCurrentSecurityContext(); + assertNull(as.getCurrentUserName()); + } + + public void testService_Domains() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service2); + ases.add(service3); + ases.add(service4); + ases.add(service5); + ases.add(service6); + as.setAuthenticationServices(ases); + as.setMutableAuthenticationService(service1); + + HashSet testDomains = new HashSet(); + testDomains.add(ALFRESCO); + testDomains.add(LONELY_ENABLED); + testDomains.add(LONELY_DISABLE); + testDomains.add(EMPTY); + testDomains.add(FIVE); + testDomains.add(FIVE_AND_MORE); + + HashSet onlyAlfDomain = new HashSet(); + onlyAlfDomain.add(ALFRESCO); + + assertTrue(as.getDomains().equals(testDomains)); + assertTrue(as.getDomainsThatAllowUserCreation().equals(onlyAlfDomain)); + assertTrue(as.getDomainsThatAllowUserDeletion().equals(onlyAlfDomain)); + assertTrue(as.getDomiansThatAllowUserPasswordChanges().equals(onlyAlfDomain)); + + } + + public void testServiceTickets() + { + ChainingAuthenticationServiceImpl as = new ChainingAuthenticationServiceImpl(); + ArrayList ases = new ArrayList(); + ases.add(service2); + ases.add(service3); + ases.add(service4); + ases.add(service5); + ases.add(service6); + as.setAuthenticationServices(ases); + as.setMutableAuthenticationService(service1); + + as.authenticate("andy", "andy".toCharArray()); + + String ticket = as.getCurrentTicket(); + assertTrue(ticket == as.getCurrentTicket()); + + as.validate(ticket); + as.invalidateTicket(ticket); + try + { + as.validate(ticket); + fail(); + } + catch (AuthenticationException e) + { + + } + + ticket = as.getCurrentTicket(); + as.validate(ticket); + as.invalidateUserSession("andy"); + try + { + as.validate(ticket); + fail(); + } + catch (AuthenticationException e) + { + + } + } + +} diff --git a/source/java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java b/source/java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java new file mode 100644 index 0000000000..2668a38325 --- /dev/null +++ b/source/java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.security.authentication; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.GrantedAuthority; +import net.sf.acegisecurity.GrantedAuthorityImpl; +import net.sf.acegisecurity.UserDetails; +import net.sf.acegisecurity.context.Context; +import net.sf.acegisecurity.context.ContextHolder; +import net.sf.acegisecurity.context.security.SecureContext; +import net.sf.acegisecurity.context.security.SecureContextImpl; +import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; +import net.sf.acegisecurity.providers.dao.User; + +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.util.EqualsHelper; +import org.alfresco.util.GUID; + +public class TestAuthenticationServiceImpl implements AuthenticationService +{ + private Map userNamesAndPasswords = new HashMap(); + + private Set disabledUsers = new HashSet(); + + private Map userToTicket = new HashMap(); + + String domain; + + boolean allowCreate; + + boolean allowDelete; + + boolean allowUpdate; + + boolean allowGuest; + + public TestAuthenticationServiceImpl(String domain, boolean allowCreate, boolean allowDelete, boolean allowUpdate, boolean allowGuest) + { + super(); + this.domain = domain; + this.allowCreate = allowCreate; + this.allowDelete = allowDelete; + this.allowUpdate = allowUpdate; + this.allowGuest = allowGuest; + } + + public TestAuthenticationServiceImpl(String domain, boolean allowCreate, boolean allowDelete, boolean allowUpdate, boolean allowGuest, + Map users, Set disabled) + { + this(domain, allowCreate, allowDelete, allowUpdate, allowGuest); + if (users != null) + { + userNamesAndPasswords.putAll(users); + } + if (disabled != null) + { + disabledUsers.addAll(disabled); + } + + } + + public void createAuthentication(String userName, char[] password) throws AuthenticationException + { + if (!allowCreate) + { + throw new AuthenticationException("Create not allowed"); + } + if (userNamesAndPasswords.containsKey(userName)) + { + throw new AuthenticationException("User exists"); + } + else + { + userNamesAndPasswords.put(userName, new String(password)); + } + + } + + public void updateAuthentication(String userName, char[] oldPassword, char[] newPassword) + throws AuthenticationException + { + if (!allowUpdate) + { + throw new AuthenticationException("Update not allowed"); + } + if (!userNamesAndPasswords.containsKey(userName)) + { + throw new AuthenticationException("User does not exist"); + } + else + { + if (userNamesAndPasswords.get(userName).equals(new String(oldPassword))) + { + userNamesAndPasswords.put(userName, new String(newPassword)); + } + else + { + throw new AuthenticationException("Password does not match existing"); + } + } + + } + + public void setAuthentication(String userName, char[] newPassword) throws AuthenticationException + { + if (!allowUpdate) + { + throw new AuthenticationException("Update not allowed"); + } + if (!userNamesAndPasswords.containsKey(userName)) + { + throw new AuthenticationException("User does not exist"); + } + else + { + userNamesAndPasswords.put(userName, new String(newPassword)); + } + + } + + public void deleteAuthentication(String userName) throws AuthenticationException + { + if (!allowDelete) + { + throw new AuthenticationException("Delete not allowed"); + } + if (!userNamesAndPasswords.containsKey(userName)) + { + throw new AuthenticationException("User does not exist"); + } + else + { + userNamesAndPasswords.remove(userName); + } + + } + + public void setAuthenticationEnabled(String userName, boolean enabled) throws AuthenticationException + { + if (!allowUpdate) + { + throw new AuthenticationException("Update not allowed"); + } + if (!userNamesAndPasswords.containsKey(userName)) + { + throw new AuthenticationException("User does not exist"); + } + else + { + if (enabled) + { + disabledUsers.remove(userName); + } + else + { + disabledUsers.add(userName); + } + } + + } + + public boolean getAuthenticationEnabled(String userName) throws AuthenticationException + { + if (!userNamesAndPasswords.containsKey(userName)) + { + return false; + } + else + { + return !disabledUsers.contains(userName); + } + + } + + public void authenticate(String userName, char[] password) throws AuthenticationException + { + if (!userNamesAndPasswords.containsKey(userName)) + { + throw new AuthenticationException("User does not exist"); + } + else if (disabledUsers.contains(userName)) + { + throw new AuthenticationException("User disabled0"); + } + else + { + if (userNamesAndPasswords.get(userName).equals(new String(password))) + { + setCurrentUser(userName); + } + else + { + throw new AuthenticationException("Unknown user/password"); + } + } + + } + + public void authenticateAsGuest() throws AuthenticationException + { + if (allowGuest) + { + setCurrentUser(PermissionService.GUEST_AUTHORITY); + } + else + { + throw new AuthenticationException("Guest access denied"); + } + } + + public String getCurrentUserName() throws AuthenticationException + { + Context context = ContextHolder.getContext(); + if ((context == null) || !(context instanceof SecureContext)) + { + return null; + } + return getUserName(((SecureContext) context).getAuthentication()); + } + + private String getUserName(Authentication authentication) + { + String username = authentication.getPrincipal().toString(); + + if (authentication.getPrincipal() instanceof UserDetails) + { + username = ((UserDetails) authentication.getPrincipal()).getUsername(); + } + + return username; + } + + public void invalidateUserSession(String userName) throws AuthenticationException + { + userToTicket.remove(userName); + } + + public void invalidateTicket(String ticket) throws AuthenticationException + { + String userToRemove = null; + for (String user : userToTicket.keySet()) + { + String currentTicket = userToTicket.get(user); + if (EqualsHelper.nullSafeEquals(currentTicket, ticket)) + { + userToRemove = user; + } + } + if (userToRemove != null) + { + userToTicket.remove(userToRemove); + } + + } + + public void validate(String ticket) throws AuthenticationException + { + String userToSet = null; + for (String user : userToTicket.keySet()) + { + String currentTicket = userToTicket.get(user); + if (EqualsHelper.nullSafeEquals(currentTicket, ticket)) + { + userToSet = user; + } + } + if (userToSet != null) + { + setCurrentUser(userToSet); + } + else + { + throw new AuthenticationException("Invalid ticket"); + } + + } + + public String getCurrentTicket() + { + String currentUser = getCurrentUserName(); + String ticket = userToTicket.get(currentUser); + if (ticket == null) + { + ticket = GUID.generate(); + userToTicket.put(currentUser, ticket); + } + return ticket; + } + + public void clearCurrentSecurityContext() + { + ContextHolder.setContext(null); + } + + public boolean isCurrentUserTheSystemUser() + { + String userName = getCurrentUserName(); + if ((userName != null) && userName.equals(SYSTEM_USER_NAME)) + { + return true; + } + return false; + } + + public Set getDomains() + { + return Collections.singleton(domain); + } + + public Set getDomainsThatAllowUserCreation() + { + if (allowCreate) + { + return Collections.singleton(domain); + } + else + { + return Collections. emptySet(); + } + } + + public Set getDomainsThatAllowUserDeletion() + { + if (allowDelete) + { + return Collections.singleton(domain); + } + else + { + return Collections. emptySet(); + } + } + + public Set getDomiansThatAllowUserPasswordChanges() + { + if (allowUpdate) + { + return Collections.singleton(domain); + } + else + { + return Collections. emptySet(); + } + } + + /** + * Explicitly set the current user to be authenticated. + * + * @param userName + * String + * @return Authentication + */ + public Authentication setCurrentUser(String userName) throws AuthenticationException + { + if (userName == null) + { + throw new AuthenticationException("Null user name"); + } + + try + { + UserDetails ud = null; + if (userName.equals(SYSTEM_USER_NAME)) + { + GrantedAuthority[] gas = new GrantedAuthority[1]; + gas[0] = new GrantedAuthorityImpl("ROLE_SYSTEM"); + ud = new User(SYSTEM_USER_NAME, "", true, true, true, true, gas); + } + else if (userName.equalsIgnoreCase(PermissionService.GUEST_AUTHORITY)) + { + GrantedAuthority[] gas = new GrantedAuthority[0]; + ud = new User(PermissionService.GUEST_AUTHORITY.toLowerCase(), "", true, true, true, true, gas); + } + else + { + ud = getUserDetails(userName); + } + + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(ud, "", ud + .getAuthorities()); + auth.setDetails(ud); + auth.setAuthenticated(true); + return setCurrentAuthentication(auth); + } + catch (net.sf.acegisecurity.AuthenticationException ae) + { + throw new AuthenticationException(ae.getMessage(), ae); + } + } + + /** + * Default implementation that makes an ACEGI object on the fly + * + * @param userName + * @return + */ + protected UserDetails getUserDetails(String userName) + { + GrantedAuthority[] gas = new GrantedAuthority[1]; + gas[0] = new GrantedAuthorityImpl("ROLE_AUTHENTICATED"); + UserDetails ud = new User(userName, "", true, true, true, true, gas); + return ud; + } + + public Authentication setCurrentAuthentication(Authentication authentication) + { + Context context = ContextHolder.getContext(); + SecureContext sc = null; + if ((context == null) || !(context instanceof SecureContext)) + { + sc = new SecureContextImpl(); + ContextHolder.setContext(sc); + } + else + { + sc = (SecureContext) context; + } + authentication.setAuthenticated(true); + sc.setAuthentication(authentication); + return authentication; + } + + private static final String SYSTEM_USER_NAME = "System"; + +} diff --git a/source/java/org/alfresco/service/cmr/security/AuthenticationService.java b/source/java/org/alfresco/service/cmr/security/AuthenticationService.java index c34db55d6d..1d56d09cda 100644 --- a/source/java/org/alfresco/service/cmr/security/AuthenticationService.java +++ b/source/java/org/alfresco/service/cmr/security/AuthenticationService.java @@ -16,6 +16,8 @@ */ package org.alfresco.service.cmr.security; +import java.util.Set; + import org.alfresco.repo.security.authentication.AuthenticationException; /** @@ -148,6 +150,34 @@ public interface AuthenticationService */ public boolean isCurrentUserTheSystemUser(); + + /** + * Get the domain to which this instance of an authentication service applies. + * + * @return The domain name + */ + public Set getDomains(); + + /** + * Does this instance alow user to be created? + * + * @return + */ + public Set getDomainsThatAllowUserCreation(); + + /** + * Does this instance allow users to be deleted? + * + * @return + */ + public Set getDomainsThatAllowUserDeletion(); + + /** + * Does this instance allow users to update their passwords? + * + * @return + */ + public Set getDomiansThatAllowUserPasswordChanges(); }