diff options
author | Olivier Lamy <olamy@apache.org> | 2012-04-06 09:58:14 +0000 |
---|---|---|
committer | Olivier Lamy <olamy@apache.org> | 2012-04-06 09:58:14 +0000 |
commit | 5b06b6673ee8eaed4b46ad8e847e98fe4c90319d (patch) | |
tree | e184a7512cd005f5baaf82a94e13fd67646cc2ea /redback-users | |
parent | be9e1800fdcb3c37c566220c1b2b79650d375000 (diff) | |
download | archiva-5b06b6673ee8eaed4b46ad8e847e98fe4c90319d.tar.gz archiva-5b06b6673ee8eaed4b46ad8e847e98fe4c90319d.zip |
import of redback core sources
http://svn.codehaus.org/redback/redback/trunk/ r1724
git-svn-id: https://svn.apache.org/repos/asf/archiva/redback/redback-core/trunk@1310268 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'redback-users')
62 files changed, 7452 insertions, 0 deletions
diff --git a/redback-users/pom.xml b/redback-users/pom.xml new file mode 100644 index 000000000..f8d7889e6 --- /dev/null +++ b/redback-users/pom.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2006 The Codehaus. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback</artifactId> + <version>1.5-SNAPSHOT</version> + </parent> + <artifactId>redback-users</artifactId> + <name>Redback :: User Management</name> + <packaging>pom</packaging> + <modules> + <module>redback-users-api</module> + <module>redback-users-providers</module> + <module>redback-users-tests</module> + <module>redback-authentication-users</module> + </modules> +</project> diff --git a/redback-users/redback-authentication-users/pom.xml b/redback-users/redback-authentication-users/pom.xml new file mode 100644 index 000000000..b4c88dc58 --- /dev/null +++ b/redback-users/redback-authentication-users/pom.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2006 The Codehaus. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-authentication-providers</artifactId> + <version>1.5-SNAPSHOT</version> + <relativePath>../../redback-authentication/redback-authentication-providers/pom.xml</relativePath> + </parent> + <artifactId>redback-authentication-users</artifactId> + <name>Redback :: Authentication Provider :: Users</name> + <dependencies> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-authentication-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-policy</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-configurable</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-cached</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>jsr250-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-memory</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/redback-users/redback-authentication-users/src/main/java/org/codehaus/plexus/redback/authentication/users/UserManagerAuthenticator.java b/redback-users/redback-authentication-users/src/main/java/org/codehaus/plexus/redback/authentication/users/UserManagerAuthenticator.java new file mode 100644 index 000000000..81bbd63af --- /dev/null +++ b/redback-users/redback-authentication-users/src/main/java/org/codehaus/plexus/redback/authentication/users/UserManagerAuthenticator.java @@ -0,0 +1,194 @@ +package org.codehaus.plexus.redback.authentication.users; + +/* + * Copyright 2005 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.authentication.AuthenticationConstants; +import org.codehaus.plexus.redback.authentication.AuthenticationDataSource; +import org.codehaus.plexus.redback.authentication.AuthenticationException; +import org.codehaus.plexus.redback.authentication.AuthenticationResult; +import org.codehaus.plexus.redback.authentication.Authenticator; +import org.codehaus.plexus.redback.authentication.PasswordBasedAuthenticationDataSource; +import org.codehaus.plexus.redback.policy.AccountLockedException; +import org.codehaus.plexus.redback.policy.MustChangePasswordException; +import org.codehaus.plexus.redback.policy.PasswordEncoder; +import org.codehaus.plexus.redback.policy.PolicyViolationException; +import org.codehaus.plexus.redback.policy.UserSecurityPolicy; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link Authenticator} implementation that uses a wrapped {@link UserManager} to authenticate. + * + * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a> + * @version $Id$ + */ +@Service( "authenticator#user-manager" ) +public class UserManagerAuthenticator + implements Authenticator +{ + private Logger log = LoggerFactory.getLogger( UserManagerAuthenticator.class ); + + @Inject + @Named( value = "userManager#jdo" ) + private UserManager userManager; + + @Inject + private UserSecurityPolicy securityPolicy; + + public String getId() + { + return "UserManagerAuthenticator"; + } + + /** + * @throws org.codehaus.plexus.redback.policy.AccountLockedException + * + * @throws MustChangePasswordException + * @throws MustChangePasswordException + * @throws PolicyViolationException + * @see org.codehaus.plexus.redback.authentication.Authenticator#authenticate(org.codehaus.plexus.redback.authentication.AuthenticationDataSource) + */ + public AuthenticationResult authenticate( AuthenticationDataSource ds ) + throws AuthenticationException, AccountLockedException, MustChangePasswordException + { + boolean authenticationSuccess = false; + String username = null; + Exception resultException = null; + PasswordBasedAuthenticationDataSource source = (PasswordBasedAuthenticationDataSource) ds; + Map<String, String> authnResultExceptionsMap = new HashMap<String, String>(); + + try + { + log.debug( "Authenticate: {}", source ); + User user = userManager.findUser( source.getPrincipal() ); + username = user.getUsername(); + + if ( user.isLocked() ) + { + throw new AccountLockedException( "Account " + source.getPrincipal() + " is locked.", user ); + } + + if ( user.isPasswordChangeRequired() && source.isEnforcePasswordChange() ) + { + throw new MustChangePasswordException( "Password expired.", user ); + } + + PasswordEncoder encoder = securityPolicy.getPasswordEncoder(); + log.debug( "PasswordEncoder: {}", encoder.getClass().getName() ); + + boolean isPasswordValid = encoder.isPasswordValid( user.getEncodedPassword(), source.getPassword() ); + if ( isPasswordValid ) + { + log.debug( "User {} provided a valid password", source.getPrincipal() ); + + try + { + securityPolicy.extensionPasswordExpiration( user ); + } + catch ( MustChangePasswordException e ) + { + user.setPasswordChangeRequired( true ); + throw e; + } + + authenticationSuccess = true; + + //REDBACK-151 do not make unnessesary updates to the user object + if ( user.getCountFailedLoginAttempts() > 0 ) + { + user.setCountFailedLoginAttempts( 0 ); + userManager.updateUser( user ); + } + + return new AuthenticationResult( true, source.getPrincipal(), null ); + } + else + { + log.warn( "Password is Invalid for user " + source.getPrincipal() + "." ); + authnResultExceptionsMap.put( AuthenticationConstants.AUTHN_NO_SUCH_USER, + "Password is Invalid for user " + source.getPrincipal() + "." ); + + try + { + securityPolicy.extensionExcessiveLoginAttempts( user ); + } + finally + { + userManager.updateUser( user ); + } + + return new AuthenticationResult( false, source.getPrincipal(), null, authnResultExceptionsMap ); + } + } + catch ( UserNotFoundException e ) + { + log.warn( "Login for user " + source.getPrincipal() + " failed. user not found." ); + resultException = e; + authnResultExceptionsMap.put( AuthenticationConstants.AUTHN_NO_SUCH_USER, + "Login for user \" + source.getPrincipal() + \" failed. user not found." ); + } + + return new AuthenticationResult( authenticationSuccess, username, resultException, authnResultExceptionsMap ); + } + + /** + * Returns the wrapped {@link UserManager} used by this {@link Authenticator} + * implementation for authentication. + * + * @return the userManager + */ + public UserManager getUserManager() + { + return userManager; + } + + /** + * Sets a {@link UserManager} to be used by this {@link Authenticator} + * implementation for authentication. + * + * @param userManager the userManager to set + */ + public void setUserManager( UserManager userManager ) + { + this.userManager = userManager; + } + + public boolean supportsDataSource( AuthenticationDataSource source ) + { + return ( source instanceof PasswordBasedAuthenticationDataSource ); + } + + public UserSecurityPolicy getSecurityPolicy() + { + return securityPolicy; + } + + public void setSecurityPolicy( UserSecurityPolicy securityPolicy ) + { + this.securityPolicy = securityPolicy; + } +} diff --git a/redback-users/redback-authentication-users/src/main/resources/META-INF/spring-context.xml b/redback-users/redback-authentication-users/src/main/resources/META-INF/spring-context.xml new file mode 100644 index 000000000..de5596e90 --- /dev/null +++ b/redback-users/redback-authentication-users/src/main/resources/META-INF/spring-context.xml @@ -0,0 +1,34 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd" + default-lazy-init="true"> + + <context:annotation-config /> + <context:component-scan + base-package="org.codehaus.plexus.redback.authentication.users"/> + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-authentication-users/src/test/java/org/codehaus/plexus/redback/authentication/users/UserManagerAuthenticatorTest.java b/redback-users/redback-authentication-users/src/test/java/org/codehaus/plexus/redback/authentication/users/UserManagerAuthenticatorTest.java new file mode 100644 index 000000000..4238c34a7 --- /dev/null +++ b/redback-users/redback-authentication-users/src/test/java/org/codehaus/plexus/redback/authentication/users/UserManagerAuthenticatorTest.java @@ -0,0 +1,211 @@ +package org.codehaus.plexus.redback.authentication.users; + +/* + * Copyright 2005 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import junit.framework.TestCase; +import org.codehaus.plexus.redback.authentication.AuthenticationException; +import org.codehaus.plexus.redback.authentication.AuthenticationResult; +import org.codehaus.plexus.redback.authentication.Authenticator; +import org.codehaus.plexus.redback.authentication.PasswordBasedAuthenticationDataSource; +import org.codehaus.plexus.redback.policy.AccountLockedException; +import org.codehaus.plexus.redback.policy.MustChangePasswordException; +import org.codehaus.plexus.redback.policy.UserSecurityPolicy; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.Calendar; +import java.util.Date; + +/** + * Tests for {@link UserManagerAuthenticator} implementation. + * + * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a> + */ +@RunWith( SpringJUnit4ClassRunner.class ) +@ContextConfiguration( locations = { "classpath*:/META-INF/spring-context.xml", "classpath*:/spring-context.xml" } ) +public class UserManagerAuthenticatorTest + extends TestCase +{ + @Inject + private UserSecurityPolicy userSecurityPolicy; + + @Inject + @Named(value = "authenticator#user-manager") + Authenticator component; + + @Inject + @Named(value = "userManager#memory") + UserManager um; + + @Before + public void setUp() + throws Exception + { + super.setUp(); + userSecurityPolicy.setEnabled( false ); + } + + @Test + public void testLookup() + throws Exception + { + assertNotNull( component ); + assertEquals( UserManagerAuthenticator.class.getName(), component.getClass().getName() ); + } + + @Test + public void testAuthenticate() + throws Exception + { + // Set up a few users for the Authenticator + + User user = um.createUser( "test", "Test User", "testuser@somedomain.com" ); + user.setPassword( "testpass" ); + um.addUser( user ); + + user = um.createUser( "guest", "Guest User", "testuser@somedomain.com" ); + user.setPassword( "guestpass" ); + um.addUser( user ); + + user = um.createUser( "anonymous", "Anonymous User", "testuser@somedomain.com" ); + user.setPassword( "nopass" ); + um.addUser( user ); + + // test with valid credentials + Authenticator auth = component; + assertNotNull( auth ); + + AuthenticationResult result = auth.authenticate( createAuthDataSource( "anonymous", "nopass" ) ); + assertTrue( result.isAuthenticated() ); + + // test with invalid password + result = auth.authenticate( createAuthDataSource( "anonymous", "wrongpass" ) ); + assertFalse( result.isAuthenticated() ); + assertNull( result.getException() ); + + // test with unknown user + result = auth.authenticate( createAuthDataSource( "unknownuser", "wrongpass" ) ); + assertFalse( result.isAuthenticated() ); + assertNotNull( result.getException() ); + assertEquals( result.getException().getClass().getName(), UserNotFoundException.class.getName() ); + } + + @Test + public void testAuthenticateLockedPassword() + throws AuthenticationException, MustChangePasswordException, UserNotFoundException + { + userSecurityPolicy.setEnabled( true ); + + // Set up a user for the Authenticator + User user = um.createUser( "testuser", "Test User Locked Password", "testuser@somedomain.com" ); + user.setPassword( "correctpass1" ); + user.setValidated( true ); + user.setPasswordChangeRequired( false ); + um.addUser( user ); + + Authenticator auth = component; + assertNotNull( auth ); + + boolean hasException = false; + AuthenticationResult result = null; + + try + { + // test password lock + for ( int i = 0; i < 11; i++ ) + { + result = auth.authenticate( createAuthDataSource( "testuser", "wrongpass" ) ); + } + } + catch ( AccountLockedException e ) + { + hasException = true; + } + finally + { + assertNotNull( result ); + assertFalse( result.isAuthenticated() ); + assertTrue( hasException ); + } + } + + @Test + public void testAuthenticateExpiredPassword() + throws AuthenticationException, AccountLockedException, UserNotFoundException + { + userSecurityPolicy.setEnabled( true ); + userSecurityPolicy.setPasswordExpirationDays( 15 ); + + // Set up a user for the Authenticator + User user = um.createUser( "testuser", "Test User Expired Password", "testuser@somedomain.com" ); + user.setPassword( "expiredpass1" ); + user.setValidated( true ); + user.setPasswordChangeRequired( false ); + um.addUser( user ); + + Authenticator auth = component; + assertNotNull( auth ); + + boolean hasException = false; + + try + { + // test successful authentication + AuthenticationResult result = auth.authenticate( createAuthDataSource( "testuser", "expiredpass1" ) ); + assertTrue( result.isAuthenticated() ); + + // test expired password + user = um.findUser( "testuser" ); + + Calendar currentDate = Calendar.getInstance(); + currentDate.set( Calendar.YEAR, currentDate.get( Calendar.YEAR ) - 1 ); + Date lastPasswordChange = currentDate.getTime(); + user.setLastPasswordChange( lastPasswordChange ); + + um.updateUser( user ); + + auth.authenticate( createAuthDataSource( "testuser", "expiredpass1" ) ); + } + catch ( MustChangePasswordException e ) + { + hasException = true; + } + finally + { + assertTrue( hasException ); + } + } + + private PasswordBasedAuthenticationDataSource createAuthDataSource( String username, String password ) + { + PasswordBasedAuthenticationDataSource source = new PasswordBasedAuthenticationDataSource(); + + source.setPrincipal( username ); + source.setPassword( password ); + + return source; + + } +} diff --git a/redback-users/redback-authentication-users/src/test/resources/spring-context.xml b/redback-users/redback-authentication-users/src/test/resources/spring-context.xml new file mode 100644 index 000000000..19c84c510 --- /dev/null +++ b/redback-users/redback-authentication-users/src/test/resources/spring-context.xml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd" + default-lazy-init="true"> + + <bean name="authenticator#user-manager" class="org.codehaus.plexus.redback.authentication.users.UserManagerAuthenticator"> + <property name="userManager" ref="userManager#memory"/> + <property name="securityPolicy" ref="userSecurityPolicy"/> + </bean> + + <alias name="userManager#memory" alias="userManager#jdo"/> + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-users-api/pom.xml b/redback-users/redback-users-api/pom.xml new file mode 100644 index 000000000..4aa7188b6 --- /dev/null +++ b/redback-users/redback-users-api/pom.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2006 The Codehaus. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users</artifactId> + <version>1.5-SNAPSHOT</version> + </parent> + <artifactId>redback-users-api</artifactId> + <name>Redback :: User Management API</name> + + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + </dependencies> +</project> diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/AbstractUserManager.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/AbstractUserManager.java new file mode 100644 index 000000000..55eea3c61 --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/AbstractUserManager.java @@ -0,0 +1,139 @@ +package org.codehaus.plexus.redback.users; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AbstractUserManager + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public abstract class AbstractUserManager + implements UserManager +{ + protected Logger log = LoggerFactory.getLogger( getClass() ); + + private List<UserManagerListener> listeners = new ArrayList<UserManagerListener>(); + + public void addUserManagerListener( UserManagerListener listener ) + { + if ( !listeners.contains( listener ) ) + { + listeners.add( listener ); + } + } + + public void removeUserManagerListener( UserManagerListener listener ) + { + listeners.remove( listener ); + } + + protected void fireUserManagerInit( boolean freshDatabase ) + { + for ( UserManagerListener listener : listeners ) + { + try + { + listener.userManagerInit( freshDatabase ); + } + catch ( Exception e ) + { + // Ignore + } + } + } + + protected void fireUserManagerUserAdded( User addedUser ) + { + for ( UserManagerListener listener : listeners ) + { + try + { + listener.userManagerUserAdded( addedUser ); + } + catch ( Exception e ) + { + // Ignore + } + } + } + + protected void fireUserManagerUserRemoved( User removedUser ) + { + for ( UserManagerListener listener : listeners ) + { + try + { + listener.userManagerUserRemoved( removedUser ); + } + catch ( Exception e ) + { + // Ignore + } + } + } + + protected void fireUserManagerUserUpdated( User updatedUser ) + { + for ( UserManagerListener listener : listeners ) + { + try + { + listener.userManagerUserUpdated( updatedUser ); + } + catch ( Exception e ) + { + // Ignore + } + } + } + + public User getGuestUser() + throws UserNotFoundException + { + return findUser( GUEST_USERNAME ); + } + + public User createGuestUser() + { + try + { + User u = getGuestUser(); + if ( u != null ) + { + return u; + } + } + catch ( UserNotFoundException e ) + { + //Nothing to do + } + + User user = createUser( GUEST_USERNAME, "Guest", "" ); + user.setPermanent( true ); + user.setPasswordChangeRequired( false ); + + user = addUser( user ); + return user; + } +} diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/AbstractUserQuery.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/AbstractUserQuery.java new file mode 100644 index 000000000..746b51685 --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/AbstractUserQuery.java @@ -0,0 +1,119 @@ +package org.codehaus.plexus.redback.users; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +/** + * Abstract Implementation of UserQuery. + * Intended to be subclassed by UserManager providers. + */ +public abstract class AbstractUserQuery + implements UserQuery +{ + + private String username; + + private String fullName; + + private String email; + + private long maxResults = -1; + + private long firstResult; + + private String orderBy = ORDER_BY_USERNAME; + + private boolean ascending = true; + + public String getUsername() + { + return username; + } + + public void setUsername( String userName ) + { + this.username = userName; + } + + public String getFullName() + { + return fullName; + } + + public void setFullName( String fullName ) + { + this.fullName = fullName; + } + + public String getEmail() + { + return email; + } + + public void setEmail( String email ) + { + this.email = email; + } + + public long getFirstResult() + { + return firstResult; + } + + public void setFirstResult( int firstResult ) + { + this.firstResult = firstResult; + } + + public long getMaxResults() + { + return maxResults; + } + + public void setMaxResults( int maxResults ) + { + this.maxResults = maxResults; + } + + public String getOrderBy() + { + return orderBy; + } + + public void setOrderBy( String orderBy ) + { + if ( orderBy == null ) + { + throw new IllegalArgumentException( "orderBy cannot be set to null" ); + } + else if ( !ALLOWED_ORDER_FIELDS.contains( orderBy ) ) + { + throw new IllegalArgumentException( orderBy + " is not an allowed orderBy field: " + orderBy ); + } + this.orderBy = orderBy; + } + + public boolean isAscending() + { + return ascending; + } + + public void setAscending( boolean ascending ) + { + this.ascending = ascending; + } + +}
\ No newline at end of file diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/Messages.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/Messages.java new file mode 100644 index 000000000..e5727beac --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/Messages.java @@ -0,0 +1,91 @@ +package org.codehaus.plexus.redback.users; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Localized Message Handling. + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public class Messages +{ + private static final String BUNDLE_NAME = "org.codehaus.plexus.redback.users.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle( BUNDLE_NAME ); + + /** + * Get a Message as-is from the Resource Bundle. + * + * @param key the key for the message to get. + * @return the value of the key, or "!key!" if the key is not found. + */ + public static String getString( String key ) + { + try + { + return RESOURCE_BUNDLE.getString( key ); + } + catch ( MissingResourceException e ) + { + return '!' + key + '!'; + } + } + + /** + * Gets a Message from the Resource Bundle, with {1} and {2} style arguments. + * + * @param key the key for the message to get. + * @param arg the argument to pass in. + * @return the value of the key, or "!key!" if the key is not found. + */ + public static String getString( String key, Object arg ) + { + return getString( key, new Object[] { arg } ); + } + + /** + * Gets a Message from the Resource Bundle, with {1} and {2} style arguments. + * + * @param key the key for the message to get. + * @param args the arguments to pass in. + * @return the value of the key, or "!key!" if the key is not found. + */ + public static String getString( String key, Object args[] ) + { + try + { + String pattern = RESOURCE_BUNDLE.getString( key ); + return MessageFormat.format( pattern, args ); + } + catch ( MissingResourceException e ) + { + return '!' + key + '!'; + } + } + + /** + * Prevent Instantiation. + */ + private Messages() + { + } +} diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/PermanentUserException.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/PermanentUserException.java new file mode 100644 index 000000000..025e443ac --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/PermanentUserException.java @@ -0,0 +1,49 @@ +package org.codehaus.plexus.redback.users; + +/* + * Copyright 2001-2006 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +/** + * PermanentUserException - tossed when a forbidden action against a permanent user + * occurs. + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public class PermanentUserException + extends UserManagerException +{ + + public PermanentUserException() + { + super(); + } + + public PermanentUserException( String message, Throwable cause ) + { + super( message, cause ); + } + + public PermanentUserException( String message ) + { + super( message ); + } + + public PermanentUserException( Throwable cause ) + { + super( cause ); + } +} diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/User.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/User.java new file mode 100644 index 000000000..40a0ce8bd --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/User.java @@ -0,0 +1,298 @@ +package org.codehaus.plexus.redback.users; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * The User Object. + * + * @author Jason van Zyl + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + */ +public interface User + extends Serializable +{ + + /** + * This is the object used to track the user within the provider. + * + * @return the principal for this user. + */ + Object getPrincipal(); + + // -------------------------------------------------------------------- + // Standard User Requirements. + // -------------------------------------------------------------------- + + /** + * Gets the User Name for this user. + * <p/> + * This field is required, and should never be empty. + * + * @return the user name. + */ + String getUsername(); + + /** + * Sets the User Name for this user. + * <p/> + * This field is required, and should never be empty. + * + * @param name the user name. + */ + void setUsername( String name ); + + /** + * Gets the Full Name for this user. + * <p/> + * This field is required, and should never be empty. + * + * @return the full name. + */ + String getFullName(); + + /** + * Sets the Full Name for this user. + * <p/> + * This field is required, and should never be empty. + * + * @param name the full name. + */ + void setFullName( String name ); + + /** + * Gets the email address for this user. + * <p/> + * This field is required, and should never be empty. + * + * @return the email address. + */ + String getEmail(); + + /** + * Sets the email address for this user. + * <p/> + * This field is required, and should never be empty. + * + * @param address the email address. + */ + void setEmail( String address ); + + // -------------------------------------------------------------------- + // Password Requirements. + // -------------------------------------------------------------------- + + /** + * Gets the Raw (unencoded) Password. + * Used only on password change requests. + * <p/> + * <p> + * <b>Notes for User Providers</b> + * </p> + * <ol> + * <li> + * Providers need to look for a value in here to indicate if the user is + * intending to change their password. + * </li> + * <li> + * The providers of this interface need to use this field, encode the password, place it's value + * into the encodedPassword field, and clear out the raw unencoded password field. + * </li> + * <li> + * This field should never be stored on disk. + * </li> + * </ol> + * + * @return the raw encoded password. + */ + String getPassword(); + + /** + * Sets the raw (unencoded) password for this user. + * + * @param rawPassword the raw unencoded password for this user. + * @see #getPassword() + */ + void setPassword( String rawPassword ); + + /** + * Gets the Encoded Password. + * + * @return the encoded password. + */ + String getEncodedPassword(); + + /** + * Sets the Encoded Password. + * <p/> + * This field is populated by the {@link UserManager} process. + * + * @param encodedPassword + */ + void setEncodedPassword( String encodedPassword ); + + /** + * Gets the Date of the Last Password Change. + * <p/> + * Used by password management policies to enforce password expiration rules. + * + * @return the date of the last password change. + */ + Date getLastPasswordChange(); + + /** + * Sets the Last Password Change Date. + * <p/> + * This field is populated by the {@link UserManager} process. + * + * @param passwordChangeDate the date that the last password change occured. + */ + void setLastPasswordChange( Date passwordChangeDate ); + + /** + * Gets the list of previous password (in encoded format). + * <p/> + * Used by password management policies to enforce password reuse rules. + * + * @return the list of {@link String} objects. Represents previous passwords (in encoded format). + */ + List<String> getPreviousEncodedPasswords(); + + /** + * Sets the list of previous passwords (in encoded format) + * + * @param encodedPasswordList (list of {@link String} objects.) the previously passwords in encoded format. + */ + void setPreviousEncodedPasswords( List<String> encodedPasswordList ); + + /** + * Add encoded password to previously passwords in encoded format. + * + * @param encodedPassword the encoded password to add. + */ + void addPreviousEncodedPassword( String encodedPassword ); + + // -------------------------------------------------------------------- + // State + // -------------------------------------------------------------------- + + /** + * Gets the flag indicating if this user is a permanent user or not. + * <p/> + * Usually Root / Admin / Guest users are flagged as such. + */ + boolean isPermanent(); + + /** + * Sets the permanent flag for this user. + * <p/> + * Users such as Root / Admin / Guest are typically flagged as permanent. + * + * @param permanent true if permanent. + */ + void setPermanent( boolean permanent ); + + /** + * Determines if this user account is locked from use or not. + * <p/> + * This state is set from an administrative point of view, or due to + * excessive failed login attempts. + * + * @return true if account is locked. + */ + boolean isLocked(); + + /** + * Sets the locked state of this account. + * + * @param locked true if account is to be locked. + */ + void setLocked( boolean locked ); + + /** + * Determines if this user account must change their password on next login. + * + * @return true if user must change password on next login. + */ + boolean isPasswordChangeRequired(); + + /** + * Sets the flag to indicate if this user must change their password on next login. + * + * @param changeRequired true if user must change password on next login. + */ + void setPasswordChangeRequired( boolean changeRequired ); + + /** + * Gets the flag indicating if this user has been validated (or not) + * + * @return true if validated. + */ + boolean isValidated(); + + /** + * Sets the flag indicating if this user has been validated (or not) + * + * @param valid true if validated. + */ + void setValidated( boolean valid ); + + // -------------------------------------------------------------------- + // Statistics + // -------------------------------------------------------------------- + + /** + * Get Count of Failed Login Attempts. + * + * @return the count of failed login attempts. + */ + int getCountFailedLoginAttempts(); + + /** + * Set the count of failed login attempts. + * + * @param count the count of failed login attempts. + */ + void setCountFailedLoginAttempts( int count ); + + /** + * Get the Creation Date for this account. + * + * @return the date of creation for this account. + */ + Date getAccountCreationDate(); + + /** + * Set the Creation Date for this account. + */ + void setAccountCreationDate( Date date ); + + /** + * Get the Last Successful Login Date for this account. + * + * @return the date of the last successful login + */ + Date getLastLoginDate(); + + /** + * Sets the Last Successful Login Date for this account. + */ + void setLastLoginDate( Date date ); +} diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserManager.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserManager.java new file mode 100644 index 000000000..41a02551e --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserManager.java @@ -0,0 +1,198 @@ +package org.codehaus.plexus.redback.users; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.util.List; + +/** + * User Manager Interface + * + * @author Jason van Zyl + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + */ +public interface UserManager +{ + + static final String GUEST_USERNAME = "guest"; + + /** + * Is the UserManager read only? if so then create and modify actions are to be disabled + * + * @return boolean true if user manager is disabled + */ + boolean isReadOnly(); + + /** + * An Identifier for the UserManager. + * + * @return the user manager identifier. + */ + String getId(); + + /** + * Add a {@link UserManagerListener} to track major events in the + * UserManager. + * + * @param listener the listener to add. + */ + void addUserManagerListener( UserManagerListener listener ); + + /** + * Remove a {@link UserManagerListener} from the collection of listeners. + * + * @param listener the listener to remove. + */ + void removeUserManagerListener( UserManagerListener listener ); + + /** + * Factory method to create new User Objects based on provider specific + * implementation. + * <p/> + * User objects created this way do not exist in the provider's underlying + * data store until a call to {@link #addUser(User)} is made. + * + * @param username the username for this user. + * @param fullName the full name for this user. + * @param emailAddress the email address for this user. + * @return the new user object ready to use. + */ + User createUser( String username, String fullName, String emailAddress ); + + /** + * Factory method to create the guest user. + * + * @return The guest user + */ + User createGuestUser(); + + /** + * Factory method to create {@link UserQuery}s based on provider specific + * implementations. + * + * @return the provider implementation of UserQuery + */ + UserQuery createUserQuery(); + + /** + * Get the List of {@link User} objects. + * + * @return the List of {@link User} Objects. + */ + List<User> getUsers(); + + List<User> getUsers( boolean orderAscending ); + + /** + * Add a User. + * + * @param user the user to add. + * @return the user that was just added. + */ + User addUser( User user ); + + /** + * Update a User. + * + * @param user the user to update. + * @return the user that was just updated. + * @throws UserNotFoundException if the user was not found to update. + */ + User updateUser( User user ) + throws UserNotFoundException; + + /** + * Find a User using a User name. + * + * @param username the username to find. + * @return the user. + * @throws UserNotFoundException if the user was not found. + */ + User findUser( String username ) + throws UserNotFoundException; + + /** + * Get the guest user. + * + * @return the guest user. + */ + User getGuestUser() + throws UserNotFoundException; + + List<User> findUsersByUsernameKey( String usernameKey, boolean orderAscending ); + + List<User> findUsersByFullNameKey( String fullNameKey, boolean orderAscending ); + + List<User> findUsersByEmailKey( String emailKey, boolean orderAscending ); + + /** + * Find users matching properties, ordering and range as specified by the + * {@link UserQuery}. + * + * @param query the query. + * @return a List of {@link User} objects. + */ + List<User> findUsersByQuery( UserQuery query ); + + /** + * Find a User using the principal. + * + * @param principal the principal to look for. + * @return the user. + * @throws UserNotFoundException if the user was not found. + */ + User findUser( Object principal ) + throws UserNotFoundException; + + /** + * true if the user exists, false if it doesn't + * + * @param principal + * @return true, if user exists + */ + boolean userExists( Object principal ); + + /** + * Delete a user using the principal. + * + * @param principal the principal to look for. + * @throws UserNotFoundException the user was not found. + */ + void deleteUser( Object principal ) + throws UserNotFoundException; + + /** + * Delete a user using the username. + * + * @param username the username to look for. + * @throws UserNotFoundException the user was not found. + */ + void deleteUser( String username ) + throws UserNotFoundException; + + /** + * Add a user to the database without checking for consistency or adjusting the password. Should only be used for + * re-importing known-good data. + * + * @param user the user to add + */ + void addUserUnchecked( User user ); + + void eraseDatabase(); + + User updateUser( User user, boolean passwordChangeRequired ) + throws UserNotFoundException; +} diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserManagerException.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserManagerException.java new file mode 100644 index 000000000..e1826a658 --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserManagerException.java @@ -0,0 +1,48 @@ +package org.codehaus.plexus.redback.users; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +/** + * UserManagerException + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public class UserManagerException + extends RuntimeException +{ + + public UserManagerException() + { + super(); + } + + public UserManagerException( String message, Throwable cause ) + { + super( message, cause ); + } + + public UserManagerException( String message ) + { + super( message ); + } + + public UserManagerException( Throwable cause ) + { + super( cause ); + } +} diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserManagerListener.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserManagerListener.java new file mode 100644 index 000000000..9da3a07b8 --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserManagerListener.java @@ -0,0 +1,15 @@ +package org.codehaus.plexus.redback.users; + +/** + * UserManagerListener + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public interface UserManagerListener +{ + public void userManagerInit( boolean freshDatabase ); + public void userManagerUserAdded( User user ); + public void userManagerUserRemoved( User user ); + public void userManagerUserUpdated( User user ); +} diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserNotFoundException.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserNotFoundException.java new file mode 100644 index 000000000..71d7f820e --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserNotFoundException.java @@ -0,0 +1,24 @@ +package org.codehaus.plexus.redback.users; + +/** + * @author Jason van Zyl + */ +public class UserNotFoundException + extends Exception +{ + public UserNotFoundException( String string ) + { + super( string ); + } + + public UserNotFoundException( String string, + Throwable throwable ) + { + super( string, throwable ); + } + + public UserNotFoundException( Throwable throwable ) + { + super( throwable ); + } +} diff --git a/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserQuery.java b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserQuery.java new file mode 100644 index 000000000..42f9d16f8 --- /dev/null +++ b/redback-users/redback-users-api/src/main/java/org/codehaus/plexus/redback/users/UserQuery.java @@ -0,0 +1,134 @@ +package org.codehaus.plexus.redback.users; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.util.Set; +import java.util.HashSet; +import java.util.Arrays; + + +public interface UserQuery +{ + final static String ORDER_BY_USERNAME = "username"; + + final static String ORDER_BY_FULLNAME = "fullname"; + + final static String ORDER_BY_EMAIL = "email"; + + final static Set<String> ALLOWED_ORDER_FIELDS = + new HashSet<String>( Arrays.asList( ORDER_BY_USERNAME, ORDER_BY_FULLNAME, ORDER_BY_EMAIL ) ); + + /** + * Returns the case insensitive substring user name criteria. + * + * @return the username criteria. + */ + String getUsername(); + + /** + * Sets the case insensitive substring user name criteria. + * + * @param userName the username criteria + */ + void setUsername( String userName ); + + /** + * Returns the case insensitive substring full name criteria. + * + * @return the username criteria. + */ + String getFullName(); + + /** + * Sets the case insensitive substring full name criteria. + * + * @param fullName the full name criteria + */ + void setFullName( String fullName ); + + /** + * Returns the case insensitive substring email criteria. + * + * @return the email criteria. + */ + String getEmail(); + + /** + * Sets the case insensitive substring email criteria. + * + * @param email the email criteria + */ + void setEmail( String email ); + + /** + * Returns the index (zero based) of the first result to include. Useful for paging. + * + * @return the first index + */ + long getFirstResult(); + + /** + * Sets the index (zero based) of the first result to include. Useful for paging. + * + * @param firstResult the first index + */ + void setFirstResult( int firstResult ); + + /** + * Returns the maximum number of users to return. + * + * @return the maximum number of users to return. + */ + long getMaxResults(); + + /** + * Sets the maximum number of users to return. + * + * @param maxResults the maximum number of users to return. + */ + void setMaxResults( int maxResults ); + + /** + * Returns the property used to order the results of this query. + * This is one of {@link #ORDER_BY_USERNAME}, {@link #ORDER_BY_FULLNAME} or {@link #ORDER_BY_EMAIL}. + * + * @return the order property. + */ + String getOrderBy(); + + /** + * Sets the property used to order the results of this query. + * This is one of {@link #ORDER_BY_USERNAME}, {@link #ORDER_BY_FULLNAME} or {@link #ORDER_BY_EMAIL}. + * + * @param orderBy the order property. + */ + void setOrderBy( String orderBy ); + + /** + * Returns true if the results should be returned in ascending order. + * + * @return ascending + */ + boolean isAscending(); + + /** + * Set this to true if the results should be returned in ascending order. + * + * @param ascending true if the results should be returned in ascending + */ + void setAscending( boolean ascending ); +} diff --git a/redback-users/redback-users-api/src/main/resources/org/codehaus/plexus/redback/users/messages.properties b/redback-users/redback-users-api/src/main/resources/org/codehaus/plexus/redback/users/messages.properties new file mode 100644 index 000000000..c1ca0b89f --- /dev/null +++ b/redback-users/redback-users-api/src/main/resources/org/codehaus/plexus/redback/users/messages.properties @@ -0,0 +1,10 @@ +password.encoder.no.such.algoritm=The specified algorithm {0} is not available in the JAAS Implementation of this JVM. +password.encoder.unsupported.encoding=The UTF-8 Encoding is not available in the JAAS Implementation of this JVM. +user.password.violation.missing=You must provide a password. +user.password.violation.length=You must provide a password between {0} and {1} characters in length. +user.password.violation.length.misconfigured=Password Length Rule is misconfigured. Specified minimum of ({0}) is larger than specified maximum of ({1}). Rule disabled. +user.password.violation.alpha=You must provide a password containing at least {0} alphabetic character(s). +user.password.violation.numeric=You must provide a password containing at least {0} numeric character(s). +user.password.violation.reuse=Your password cannot match any of your previous {0} password(s). +user.password.violation.alphanum.only=You must provide a password containing all alpha-numeric characters. +user.password.violation.whitespace.detected=You must provide a password without whitespace characters.
\ No newline at end of file diff --git a/redback-users/redback-users-providers/pom.xml b/redback-users/redback-users-providers/pom.xml new file mode 100644 index 000000000..738602896 --- /dev/null +++ b/redback-users/redback-users-providers/pom.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2006 The Codehaus. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users</artifactId> + <version>1.5-SNAPSHOT</version> + </parent> + <artifactId>redback-users-providers</artifactId> + <name>Redback :: Users Providers</name> + <packaging>pom</packaging> + <dependencies> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-api</artifactId> + </dependency> + </dependencies> + <modules> + <module>redback-users-cached</module> + <module>redback-users-memory</module> + <module>redback-users-jdo</module> + <module>redback-users-ldap</module> + <module>redback-users-configurable</module> + </modules> +</project> diff --git a/redback-users/redback-users-providers/redback-users-cached/pom.xml b/redback-users/redback-users-providers/redback-users-cached/pom.xml new file mode 100644 index 000000000..3b8cc7dab --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-cached/pom.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2006 The Codehaus. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-providers</artifactId> + <version>1.5-SNAPSHOT</version> + </parent> + <artifactId>redback-users-cached</artifactId> + <name>Redback :: Users Provider :: Cached</name> + + <dependencies> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-policy</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback.components.cache</groupId> + <artifactId>spring-cache-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback.components.cache</groupId> + <artifactId>spring-cache-ehcache</artifactId> + </dependency> + <dependency> + <groupId>net.sf.ehcache</groupId> + <artifactId>ehcache-core</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>jsr250-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-jdo</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-tests</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-memory</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hsqldb</groupId> + <artifactId>hsqldb</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/redback-users/redback-users-providers/redback-users-cached/src/main/java/org/codehaus/plexus/redback/users/cached/CachedUserManager.java b/redback-users/redback-users-providers/redback-users-cached/src/main/java/org/codehaus/plexus/redback/users/cached/CachedUserManager.java new file mode 100644 index 000000000..b0df43c65 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-cached/src/main/java/org/codehaus/plexus/redback/users/cached/CachedUserManager.java @@ -0,0 +1,318 @@ +package org.codehaus.plexus.redback.users.cached; + +/* + * Copyright 2001-2006 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.cache.Cache; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.UserManagerListener; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.codehaus.plexus.redback.users.UserQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.List; + +/** + * CachedUserManager + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +@Service( "userManager#cached" ) +public class CachedUserManager + implements UserManager, UserManagerListener +{ + + private Logger log = LoggerFactory.getLogger( getClass() ); + + @Inject + @Named( value = "userManager#jdo" ) + private UserManager userImpl; + + @Inject + @Named( value = "cache#users" ) + private Cache usersCache; + + public boolean isReadOnly() + { + return userImpl.isReadOnly(); + } + + public User createGuestUser() + { + return userImpl.createGuestUser(); + } + + public User addUser( User user ) + { + if ( user != null ) + { + usersCache.remove( user.getPrincipal() ); + } + return this.userImpl.addUser( user ); + } + + public void addUserManagerListener( UserManagerListener listener ) + { + this.userImpl.addUserManagerListener( listener ); + } + + public void addUserUnchecked( User user ) + { + if ( user != null ) + { + usersCache.remove( user.getPrincipal() ); + } + this.userImpl.addUserUnchecked( user ); + } + + public User createUser( String username, String fullName, String emailAddress ) + { + usersCache.remove( username ); + return this.userImpl.createUser( username, fullName, emailAddress ); + } + + public void deleteUser( Object principal ) + throws UserNotFoundException + { + usersCache.remove( principal ); + this.userImpl.deleteUser( principal ); + } + + public void deleteUser( String username ) + throws UserNotFoundException + { + usersCache.remove( username ); + this.userImpl.deleteUser( username ); + } + + public void eraseDatabase() + { + try + { + this.userImpl.eraseDatabase(); + } + finally + { + usersCache.clear(); + } + } + + public User findUser( String username ) + throws UserNotFoundException + { + if ( GUEST_USERNAME.equals( username ) ) + { + return getGuestUser(); + } + + Object el = usersCache.get( username ); + if ( el != null ) + { + return (User) el; + } + else + { + User user = this.userImpl.findUser( username ); + usersCache.put( username, user ); + return user; + } + } + + public User getGuestUser() + throws UserNotFoundException + { + Object el = usersCache.get( GUEST_USERNAME ); + if ( el != null ) + { + return (User) el; + } + else + { + User user = this.userImpl.getGuestUser(); + usersCache.put( GUEST_USERNAME, user ); + return user; + } + } + + public User findUser( Object principal ) + throws UserNotFoundException + { + Object el = usersCache.get( principal ); + if ( el != null ) + { + return (User) el; + } + else + { + User user = this.userImpl.findUser( principal ); + usersCache.put( principal, user ); + return user; + } + } + + public UserQuery createUserQuery() + { + return userImpl.createUserQuery(); + } + + + public List<User> findUsersByQuery( UserQuery query ) + { + log.debug( "NOT CACHED - .findUsersByQuery(UserQuery)" ); + return this.userImpl.findUsersByQuery( query ); + } + + public List<User> findUsersByEmailKey( String emailKey, boolean orderAscending ) + { + log.debug( "NOT CACHED - .findUsersByEmailKey(String, boolean)" ); + return this.userImpl.findUsersByEmailKey( emailKey, orderAscending ); + } + + public List<User> findUsersByFullNameKey( String fullNameKey, boolean orderAscending ) + { + log.debug( "NOT CACHED - .findUsersByFullNameKey(String, boolean)" ); + return this.userImpl.findUsersByFullNameKey( fullNameKey, orderAscending ); + } + + public List<User> findUsersByUsernameKey( String usernameKey, boolean orderAscending ) + { + log.debug( "NOT CACHED - .findUsersByUsernameKey(String, boolean)" ); + return this.userImpl.findUsersByUsernameKey( usernameKey, orderAscending ); + } + + public String getId() + { + return "Cached User Manager [" + this.userImpl.getId() + "]"; + } + + public List<User> getUsers() + { + log.debug( "NOT CACHED - .getUsers()" ); + return this.userImpl.getUsers(); + } + + public List<User> getUsers( boolean orderAscending ) + { + log.debug( "NOT CACHED - .getUsers(boolean)" ); + return this.userImpl.getUsers( orderAscending ); + } + + public void removeUserManagerListener( UserManagerListener listener ) + { + this.userImpl.removeUserManagerListener( listener ); + } + + public User updateUser( User user ) + throws UserNotFoundException + { + return updateUser( user, false ); + } + + public User updateUser( User user, boolean passwordChangeRequired ) + throws UserNotFoundException + { + if ( user != null ) + { + usersCache.remove( user.getPrincipal() ); + } + return this.userImpl.updateUser( user, passwordChangeRequired ); + } + + public boolean userExists( Object principal ) + { + if ( usersCache.hasKey( principal ) ) + { + return true; + } + + return this.userImpl.userExists( principal ); + } + + public void userManagerInit( boolean freshDatabase ) + { + if ( userImpl instanceof UserManager ) + { + ( (UserManagerListener) this.userImpl ).userManagerInit( freshDatabase ); + } + + usersCache.clear(); + } + + public void userManagerUserAdded( User user ) + { + if ( userImpl instanceof UserManager ) + { + ( (UserManagerListener) this.userImpl ).userManagerUserAdded( user ); + } + + if ( user != null ) + { + usersCache.remove( user.getPrincipal() ); + } + } + + public void userManagerUserRemoved( User user ) + { + if ( userImpl instanceof UserManager ) + { + ( (UserManagerListener) this.userImpl ).userManagerUserRemoved( user ); + } + + if ( user != null ) + { + usersCache.remove( user.getPrincipal() ); + } + } + + public void userManagerUserUpdated( User user ) + { + if ( userImpl instanceof UserManager ) + { + ( (UserManagerListener) this.userImpl ).userManagerUserUpdated( user ); + } + + if ( user != null ) + { + usersCache.remove( user.getPrincipal() ); + } + } + + public UserManager getUserImpl() + { + return userImpl; + } + + public void setUserImpl( UserManager userImpl ) + { + this.userImpl = userImpl; + } + + public Cache getUsersCache() + { + return usersCache; + } + + public void setUsersCache( Cache usersCache ) + { + this.usersCache = usersCache; + } +} diff --git a/redback-users/redback-users-providers/redback-users-cached/src/main/resources/META-INF/spring-context.xml b/redback-users/redback-users-providers/redback-users-cached/src/main/resources/META-INF/spring-context.xml new file mode 100644 index 000000000..10e03f5e9 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-cached/src/main/resources/META-INF/spring-context.xml @@ -0,0 +1,45 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd" + default-lazy-init="true"> + + <context:annotation-config /> + <context:component-scan + base-package="org.codehaus.plexus.redback.users.cached"/> + + <bean name="cache#users" class="org.codehaus.plexus.cache.ehcache.EhcacheCache" + init-method="initialize"> + <property name="diskPersistent" value="false"/> + <property name="eternal" value="false"/> + <property name="maxElementsInMemory" value="1000"/> + <property name="memoryEvictionPolicy" value="LRU"/> + <property name="name" value="usersCache"/> + <property name="timeToIdleSeconds" value="1800"/> + <property name="timeToLiveSeconds" value="14400"/> + </bean> + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-cached/src/test/java/org/codehaus/plexus/redback/users/cached/CachedUserManagerTest.java b/redback-users/redback-users-providers/redback-users-cached/src/test/java/org/codehaus/plexus/redback/users/cached/CachedUserManagerTest.java new file mode 100644 index 000000000..ba1c6c142 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-cached/src/test/java/org/codehaus/plexus/redback/users/cached/CachedUserManagerTest.java @@ -0,0 +1,67 @@ +package org.codehaus.plexus.redback.users.cached; + +/* + * Copyright 2001-2006 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import net.sf.ehcache.CacheManager; + +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.provider.test.AbstractUserManagerTestCase; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * CachedUserManagerTest + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public class CachedUserManagerTest + extends AbstractUserManagerTestCase +{ + + @Inject @Named(value = "userManager#cached") + UserManager userManager; + + + @Before + public void setUp() + throws Exception + { + super.setUp(); + + setUserManager( userManager ); + + assertTrue( getUserManager() instanceof CachedUserManager ); + } + + @After + public void tearDown() + throws Exception + { + super.tearDown(); + } + + @AfterClass + public static void cleanCache() + { + CacheManager.getInstance().removalAll(); + } +} diff --git a/redback-users/redback-users-providers/redback-users-cached/src/test/resources/org/codehaus/plexus/redback/config-defaults.properties b/redback-users/redback-users-providers/redback-users-cached/src/test/resources/org/codehaus/plexus/redback/config-defaults.properties new file mode 100644 index 000000000..d0dd8d724 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-cached/src/test/resources/org/codehaus/plexus/redback/config-defaults.properties @@ -0,0 +1,127 @@ +# +# Copyright 2006 The Codehaus. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. +# + +# -------------------------------------------------------------------- +# Application Configuration + +application.timestamp=EEE d MMM yyyy HH:mm:ss Z + +# -------------------------------------------------------------------- +# JDBC Setup + +jdbc.driver.name=org.hsqldb.jdbcDriver +jdbc.url=jdbc:hsqldb:mem:redback-test +jdbc.username=sa +jdbc.password= + +# -------------------------------------------------------------------- +# Email Settings + +email.jndiSessionName=java:comp/env/mail/Session +email.smtp.host=localhost +email.smtp.port=25 +email.smtp.ssl.enabled=false +email.smtp.tls.enabled=false +email.smtp.username= +email.smtp.password= + +#TODO: move description elsewhere, remove bad default +# All emails sent by the system will be from the following address +#email.from.address=${user.name}@localhost +# All emails sent by the system will be from the following user name (used in conjunction with address) +#email.from.name=Unconfigured Username + +# If all email addresses (from new user registration) require an account validation email. +email.validation.required=true +# Timeout (in minutes) for the key generated for an email validation to remain valid. +# 2880 minutes = 48 hours +email.validation.timeout=2880 +# The subject line for the email message. +email.validation.subject=Welcome + +#TODO: move description elsewhere, remove bad default +# Get the Feedback to use for any outgoing emails. +# NOTE: if feedback.path starts with a "/" it is appended to the end of the value provided in application.url +# This value can be in the format/syntax of "/feedback.action" or even "mailto:feedback@application.com" +#email.feedback.path=/feedback.action + +#Set the application base URL. The default is to derive it from the HTTP request +#application.url=http://myurl.mycompany.com + +# -------------------------------------------------------------------- +# Auto Login Settings + +security.rememberme.enabled=true +# Timeout in minutes ( 525600 minutes = 1 year ) +security.rememberme.timeout=525600 + +# Single Sign On +# Timeout in minutes +security.signon.timeout=30 + +# -------------------------------------------------------------------- +# Default Username Values +redback.default.admin=admin + +# -------------------------------------------------------------------- +# Security Policies + +#security.policy.password.encoder= +security.policy.password.previous.count=6 +security.policy.password.expiration.enabled=true +security.policy.password.expiration.days=90 +security.policy.password.expiration.notify.days=10 +security.policy.allowed.login.attempt=10 + +# turn off the perclick enforcement of various security policies, slightly +# more heavyweight since it will ensure that the User object on each click +# is up to date +security.policy.strict.enforcement.enabled=true +security.policy.strict.force.password.change.enabled=true + +# -------------------------------------------------------------------- +# Password Rules +security.policy.password.rule.alphanumeric.enabled=false +security.policy.password.rule.alphacount.enabled=true +security.policy.password.rule.alphacount.minimum=1 +security.policy.password.rule.characterlength.enabled=true +security.policy.password.rule.characterlength.minimum=1 +security.policy.password.rule.characterlength.maximum=24 +security.policy.password.rule.musthave.enabled=true +security.policy.password.rule.numericalcount.enabled=true +security.policy.password.rule.numericalcount.minimum=1 +security.policy.password.rule.reuse.enabled=true +security.policy.password.rule.nowhitespace.enabled=true + +# -------------------------------------------------------------------- +# ldap settings +# +ldap.bind.authenticator.enabled=false + +# ldap options for configuration via properties file +#ldap.config.hostname= +#ldap.config.port= +#ldap.config.base.dn= +#ldap.config.context.factory= +#ldap.config.bind.dn= +#ldap.config.password= +#ldap.config.authentication.method= + +# config parameter for the ConfigurableUserManager +user.manager.impl=cached + + + diff --git a/redback-users/redback-users-providers/redback-users-cached/src/test/resources/spring-context.xml b/redback-users/redback-users-providers/redback-users-cached/src/test/resources/spring-context.xml new file mode 100644 index 000000000..1168e2c56 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-cached/src/test/resources/spring-context.xml @@ -0,0 +1,47 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd" + default-lazy-init="true"> + + <bean name="userManager#cached" class="org.codehaus.plexus.redback.users.cached.CachedUserManager"> + <property name="userImpl" ref="userManager#memory"/> + <property name="usersCache" ref="cache#users"/> + </bean> + + <bean name="cache#users" class="org.codehaus.plexus.cache.ehcache.EhcacheCache" + init-method="initialize"> + <property name="diskPersistent" value="false"/> + <property name="eternal" value="false"/> + <property name="maxElementsInMemory" value="1000"/> + <property name="memoryEvictionPolicy" value="LRU"/> + <property name="name" value="usersCache"/> + <property name="timeToIdleSeconds" value="1800"/> + <property name="timeToLiveSeconds" value="14400"/> + </bean> + + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-configurable/pom.xml b/redback-users/redback-users-providers/redback-users-configurable/pom.xml new file mode 100644 index 000000000..56d2a1c55 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-configurable/pom.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2006 The Codehaus. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-providers</artifactId> + <version>1.5-SNAPSHOT</version> + </parent> + <artifactId>redback-users-configurable</artifactId> + <name>Redback :: Users Provider :: Configurable</name> + + <dependencies> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-policy</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>jsr250-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-tests</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/redback-users/redback-users-providers/redback-users-configurable/src/main/java/org/codehaus/plexus/redback/users/configurable/ConfigurableUserManager.java b/redback-users/redback-users-providers/redback-users-configurable/src/main/java/org/codehaus/plexus/redback/users/configurable/ConfigurableUserManager.java new file mode 100644 index 000000000..f0d7e3331 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-configurable/src/main/java/org/codehaus/plexus/redback/users/configurable/ConfigurableUserManager.java @@ -0,0 +1,179 @@ +package org.codehaus.plexus.redback.users.configurable; + +/* + * Copyright 2001-2007 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.configuration.UserConfiguration; +import org.codehaus.plexus.redback.users.AbstractUserManager; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.codehaus.plexus.redback.users.UserQuery; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import javax.inject.Named; +import java.util.List; + +/** + * @author <a href="jesse@codehaus.org"> jesse + * @version "Id$ + */ +@Service( "userManager#configurable" ) +public class ConfigurableUserManager + extends AbstractUserManager +{ + @Inject + @Named( value = "userConfiguration" ) + private UserConfiguration config; + + @Inject + private ApplicationContext applicationContext; + + private UserManager userManagerImpl; + + public static final String USER_MANAGER_IMPL = "user.manager.impl"; + + @PostConstruct + public void initialize() + { + String userManagerRole = config.getString( USER_MANAGER_IMPL ); + + if ( userManagerRole == null ) + { + throw new RuntimeException( + "User Manager Configuration Missing: " + USER_MANAGER_IMPL + " configuration property" ); + } + + userManagerImpl = applicationContext.getBean( "userManager#" + userManagerRole, UserManager.class ); + // (UserManager) container.lookup( UserManager.class.getName(), userManagerRole ); + } + + public User addUser( User user ) + { + return userManagerImpl.addUser( user ); + } + + public void addUserUnchecked( User user ) + { + userManagerImpl.addUserUnchecked( user ); + } + + public User createUser( String username, String fullName, String emailAddress ) + { + return userManagerImpl.createUser( username, fullName, emailAddress ); + } + + public UserQuery createUserQuery() + { + return userManagerImpl.createUserQuery(); + } + + public void deleteUser( Object principal ) + throws UserNotFoundException + { + userManagerImpl.deleteUser( principal ); + } + + public void deleteUser( String username ) + throws UserNotFoundException + { + userManagerImpl.deleteUser( username ); + } + + public void eraseDatabase() + { + userManagerImpl.eraseDatabase(); + } + + public User findUser( String username ) + throws UserNotFoundException + { + return userManagerImpl.findUser( username ); + } + + public User findUser( Object principal ) + throws UserNotFoundException + { + return userManagerImpl.findUser( principal ); + } + + @Override + public User getGuestUser() + throws UserNotFoundException + { + return userManagerImpl.getGuestUser(); + } + + public List<User> findUsersByEmailKey( String emailKey, boolean orderAscending ) + { + return userManagerImpl.findUsersByEmailKey( emailKey, orderAscending ); + } + + public List<User> findUsersByFullNameKey( String fullNameKey, boolean orderAscending ) + { + return userManagerImpl.findUsersByFullNameKey( fullNameKey, orderAscending ); + } + + public List<User> findUsersByQuery( UserQuery query ) + { + return userManagerImpl.findUsersByQuery( query ); + } + + public List<User> findUsersByUsernameKey( String usernameKey, boolean orderAscending ) + { + return userManagerImpl.findUsersByUsernameKey( usernameKey, orderAscending ); + } + + public String getId() + { + return ConfigurableUserManager.class.getName() + " wrapping " + userManagerImpl.getId(); + } + + public List<User> getUsers() + { + return userManagerImpl.getUsers(); + } + + public List<User> getUsers( boolean orderAscending ) + { + return userManagerImpl.getUsers( orderAscending ); + } + + public boolean isReadOnly() + { + return userManagerImpl.isReadOnly(); + } + + public User updateUser( User user ) + throws UserNotFoundException + { + return updateUser( user, false ); + } + + public User updateUser( User user, boolean passwordChangeRequired ) + throws UserNotFoundException + { + return userManagerImpl.updateUser( user, passwordChangeRequired ); + } + + public boolean userExists( Object principal ) + { + return userManagerImpl.userExists( principal ); + } +} diff --git a/redback-users/redback-users-providers/redback-users-configurable/src/main/resources/META-INF/spring-context.xml b/redback-users/redback-users-providers/redback-users-configurable/src/main/resources/META-INF/spring-context.xml new file mode 100644 index 000000000..6b39e503b --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-configurable/src/main/resources/META-INF/spring-context.xml @@ -0,0 +1,33 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd" + default-lazy-init="true"> + + <context:annotation-config /> + <context:component-scan base-package="org.codehaus.plexus.redback.users.configurable"/> + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-jdo/pom.xml b/redback-users/redback-users-providers/redback-users-jdo/pom.xml new file mode 100644 index 000000000..c9042c63d --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/pom.xml @@ -0,0 +1,143 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2006 The Codehaus. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-providers</artifactId> + <version>1.5-SNAPSHOT</version> + </parent> + <artifactId>redback-users-jdo</artifactId> + <name>Redback :: Users Provider :: JDO</name> + + <dependencies> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-utils</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-common-jdo</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-policy</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>jsr250-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-tests</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hsqldb</groupId> + <artifactId>hsqldb</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.java.dev.stax-utils</groupId> + <artifactId>stax-utils</artifactId> + <version>20060502</version> + <exclusions> + <exclusion> + <groupId>com.bea.xml</groupId> + <artifactId>jsr173-ri</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>javax.xml.stream</groupId> + <artifactId>stax-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.woodstox</groupId> + <artifactId>wstx-asl</artifactId> + <version>3.2.0</version> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>stax</groupId> + <artifactId>stax-api</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.derby</groupId> + <artifactId>derby</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.modello</groupId> + <artifactId>modello-maven-plugin</artifactId> + <version>1.0-alpha-15</version> + <configuration> + <version>1.0.1</version> + <packageWithVersion>false</packageWithVersion> + <model>src/main/mdo/user.xml</model> + </configuration> + <executions> + <execution> + <id>modello-java</id> + <goals> + <goal>java</goal> + <goal>jpox-metadata-class</goal> + <goal>stax-reader</goal> + <goal>stax-writer</goal> + </goals> + </execution> + <execution> + <id>jpox-jdo-mapping</id> + <goals> + <goal>jpox-jdo-mapping</goal> + </goals> + <configuration> + <outputDirectory>${basedir}/target/classes/org/codehaus/plexus/redback/users/jdo</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>jpox-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>enhance</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/main/java/org/codehaus/plexus/redback/users/jdo/JdoUserManager.java b/redback-users/redback-users-providers/redback-users-jdo/src/main/java/org/codehaus/plexus/redback/users/jdo/JdoUserManager.java new file mode 100644 index 000000000..c04ef0c65 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/main/java/org/codehaus/plexus/redback/users/jdo/JdoUserManager.java @@ -0,0 +1,487 @@ +package org.codehaus.plexus.redback.users.jdo; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.jdo.JdoFactory; +import org.codehaus.plexus.jdo.PlexusJdoUtils; +import org.codehaus.plexus.jdo.PlexusObjectNotFoundException; +import org.codehaus.plexus.jdo.PlexusStoreException; +import org.codehaus.plexus.redback.policy.UserSecurityPolicy; +import org.codehaus.plexus.redback.users.AbstractUserManager; +import org.codehaus.plexus.redback.users.PermanentUserException; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserManagerException; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.codehaus.plexus.redback.users.UserQuery; +import org.codehaus.plexus.util.StringUtils; +import org.jpox.JDOClassLoaderResolver; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.jdo.Extent; +import javax.jdo.PersistenceManager; +import javax.jdo.PersistenceManagerFactory; +import javax.jdo.Query; +import javax.jdo.Transaction; + +import java.util.Date; +import java.util.List; + +/** + * JdoUserManager + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +@Service("userManager#jdo") +public class JdoUserManager + extends AbstractUserManager +{ + @Inject @Named(value="jdoFactory#users") + private JdoFactory jdoFactory; + + @Inject + private UserSecurityPolicy userSecurityPolicy; + + private PersistenceManagerFactory pmf; + + public String getId() + { + return "JDO UserManager - " + this.getClass().getName(); + } + + + public boolean isReadOnly() + { + return false; + } + + public UserQuery createUserQuery() + { + return new JdoUserQuery(); + } + + // ------------------------------------------------------------------ + + public User createUser( String username, String fullname, String email ) + { + User user = new JdoUser(); + user.setUsername( username ); + user.setFullName( fullname ); + user.setEmail( email ); + user.setAccountCreationDate( new Date() ); + + return user; + } + + public List<User> getUsers() + { + return getAllObjectsDetached( null ); + } + + public List<User> getUsers( boolean orderAscending ) + { + String ordering = orderAscending ? "username ascending" : "username descending"; + + return getAllObjectsDetached( ordering ); + } + + @SuppressWarnings("unchecked") + private List<User> getAllObjectsDetached( String ordering ) + { + return PlexusJdoUtils.getAllObjectsDetached( getPersistenceManager(), JdoUser.class, ordering, (String) null ); + } + + public List<User> findUsersByUsernameKey( String usernameKey, boolean orderAscending ) + { + return findUsers( "username", usernameKey, orderAscending ); + } + + public List<User> findUsersByFullNameKey( String fullNameKey, boolean orderAscending ) + { + return findUsers( "fullName", fullNameKey, orderAscending ); + } + + public List<User> findUsersByEmailKey( String emailKey, boolean orderAscending ) + { + return findUsers( "email", emailKey, orderAscending ); + } + + @SuppressWarnings("unchecked") + public List<User> findUsersByQuery( UserQuery userQuery ) + { + JdoUserQuery uq = (JdoUserQuery) userQuery; + + PersistenceManager pm = getPersistenceManager(); + + Transaction tx = pm.currentTransaction(); + + try + { + tx.begin(); + + Extent extent = pm.getExtent( JdoUser.class, true ); + + Query query = pm.newQuery( extent ); + + String ordering = uq.getOrdering(); + + query.setOrdering( ordering ); + + query.declareImports( "import java.lang.String" ); + + query.declareParameters( uq.getParameters() ); + + query.setFilter( uq.getFilter() ); + + query.setRange( uq.getFirstResult(), + uq.getMaxResults() < 0 ? Long.MAX_VALUE : uq.getFirstResult() + uq.getMaxResults() ); + + List<User> result = (List<User>) query.executeWithArray( uq.getSearchKeys() ); + + result = (List<User>) pm.detachCopyAll( result ); + + tx.commit(); + + return result; + } + finally + { + rollback( tx ); + } + } + + @SuppressWarnings("unchecked") + private List<User> findUsers( String searchField, String searchKey, boolean ascendingUsername ) + { + PersistenceManager pm = getPersistenceManager(); + + Transaction tx = pm.currentTransaction(); + + try + { + tx.begin(); + + Extent extent = pm.getExtent( JdoUser.class, true ); + + Query query = pm.newQuery( extent ); + + String ordering = ascendingUsername ? "username ascending" : "username descending"; + + query.setOrdering( ordering ); + + query.declareImports( "import java.lang.String" ); + + query.declareParameters( "String searchKey" ); + + query.setFilter( "this." + searchField + ".toLowerCase().indexOf(searchKey.toLowerCase()) > -1" ); + + List<User> result = (List<User>) query.execute( searchKey ); + + result = (List<User>) pm.detachCopyAll( result ); + + tx.commit(); + + return result; + } + finally + { + rollback( tx ); + } + } + + public User addUser( User user ) + { + if ( !( user instanceof JdoUser ) ) + { + throw new UserManagerException( "Unable to Add User. User object " + user.getClass().getName() + + " is not an instance of " + JdoUser.class.getName() ); + } + + if ( StringUtils.isEmpty( user.getUsername() ) ) + { + throw new IllegalStateException( + Messages.getString( "user.manager.cannot.add.user.without.username" ) ); //$NON-NLS-1$ + } + + userSecurityPolicy.extensionChangePassword( user ); + + fireUserManagerUserAdded( user ); + + // TODO: find a better solution + // workaround for avoiding the admin from providing another password on the next login after the + // admin account has been created + // extensionChangePassword by default sets the password change status to false + if ( "admin".equals( user.getUsername() ) ) + { + user.setPasswordChangeRequired( false ); + } + else + { + user.setPasswordChangeRequired( true ); + } + + return (User) addObject( user ); + } + + public void deleteUser( Object principal ) + { + try + { + User user = findUser( principal ); + + if ( user.isPermanent() ) + { + throw new PermanentUserException( "Cannot delete permanent user [" + user.getUsername() + "]." ); + } + + fireUserManagerUserRemoved( user ); + + removeObject( user ); + } + catch ( UserNotFoundException e ) + { + log.warn( "Unable to delete user " + principal + ", user not found.", e ); + } + } + + public void deleteUser( String username ) + { + try + { + User user = findUser( username ); + + if ( user.isPermanent() ) + { + throw new PermanentUserException( "Cannot delete permanent user [" + user.getUsername() + "]." ); + } + + fireUserManagerUserRemoved( user ); + + PlexusJdoUtils.removeObject( getPersistenceManager(), user ); + } + catch ( UserNotFoundException e ) + { + log.warn( "Unable to delete user " + username + ", user not found.", e ); + } + } + + public void addUserUnchecked( User user ) + { + if ( !( user instanceof JdoUser ) ) + { + throw new UserManagerException( "Unable to Add User. User object " + user.getClass().getName() + + " is not an instance of " + JdoUser.class.getName() ); + } + + if ( StringUtils.isEmpty( user.getUsername() ) ) + { + throw new IllegalStateException( + Messages.getString( "user.manager.cannot.add.user.without.username" ) ); //$NON-NLS-1$ + } + + addObject( user ); + } + + public void eraseDatabase() + { + PlexusJdoUtils.removeAll( getPersistenceManager(), JdoUser.class ); + PlexusJdoUtils.removeAll( getPersistenceManager(), UsersManagementModelloMetadata.class ); + } + + public User findUser( Object principal ) + throws UserNotFoundException + { + if ( principal == null ) + { + throw new UserNotFoundException( "Unable to find user based on null principal." ); + } + + try + { + return (User) PlexusJdoUtils.getObjectById( getPersistenceManager(), JdoUser.class, principal.toString(), + null ); + } + catch ( PlexusObjectNotFoundException e ) + { + throw new UserNotFoundException( "Unable to find user: " + e.getMessage(), e ); + } + catch ( PlexusStoreException e ) + { + throw new UserNotFoundException( "Unable to find user: " + e.getMessage(), e ); + } + } + + public User findUser( String username ) + throws UserNotFoundException + { + if ( StringUtils.isEmpty( username ) ) + { + throw new UserNotFoundException( "User with empty username not found." ); + } + + return (User) getObjectById( username, null ); + } + + public boolean userExists( Object principal ) + { + try + { + findUser( principal ); + return true; + } + catch ( UserNotFoundException ne ) + { + return false; + } + } + + public User updateUser( User user ) + throws UserNotFoundException + { + return updateUser( user, false ); + } + + public User updateUser( User user, boolean passwordChangeRequired ) + throws UserNotFoundException + { + if ( !( user instanceof JdoUser ) ) + { + throw new UserManagerException( "Unable to Update User. User object " + user.getClass().getName() + + " is not an instance of " + JdoUser.class.getName() ); + } + + // If password is supplied, assume changing of password. + // TODO: Consider adding a boolean to the updateUser indicating a password change or not. + if ( StringUtils.isNotEmpty( user.getPassword() ) ) + { + userSecurityPolicy.extensionChangePassword( user, passwordChangeRequired ); + } + + updateObject( user ); + + fireUserManagerUserUpdated( user ); + + return user; + } + + @PostConstruct + public void initialize() + { + JDOClassLoaderResolver d; + pmf = jdoFactory.getPersistenceManagerFactory(); + } + + public PersistenceManager getPersistenceManager() + { + PersistenceManager pm = pmf.getPersistenceManager(); + + pm.getFetchPlan().setMaxFetchDepth( -1 ); + + triggerInit(); + + return pm; + } + + // ---------------------------------------------------------------------- + // jdo utility methods + // ---------------------------------------------------------------------- + + private Object addObject( Object object ) + { + return PlexusJdoUtils.addObject( getPersistenceManager(), object ); + } + + private Object getObjectById( String id, String fetchGroup ) + throws UserNotFoundException, UserManagerException + { + try + { + return PlexusJdoUtils.getObjectById( getPersistenceManager(), JdoUser.class, id, fetchGroup ); + } + catch ( PlexusObjectNotFoundException e ) + { + throw new UserNotFoundException( e.getMessage() ); + } + catch ( PlexusStoreException e ) + { + throw new UserManagerException( "Unable to get object '" + JdoUser.class.getName() + "', id '" + id + + "', fetch-group '" + fetchGroup + "' from jdo store." ); + } + } + + private Object removeObject( Object o ) + { + if ( o == null ) + { + throw new UserManagerException( "Unable to remove null object" ); + } + + PlexusJdoUtils.removeObject( getPersistenceManager(), o ); + return o; + } + + private Object updateObject( Object object ) + throws UserNotFoundException, UserManagerException + { + try + { + return PlexusJdoUtils.updateObject( getPersistenceManager(), object ); + } + catch ( PlexusStoreException e ) + { + throw new UserManagerException( + "Unable to update the '" + object.getClass().getName() + "' object in the jdo database.", e ); + } + } + + private void rollback( Transaction tx ) + { + PlexusJdoUtils.rollbackIfActive( tx ); + } + + private boolean hasTriggeredInit = false; + + public void triggerInit() + { + if ( !hasTriggeredInit ) + { + hasTriggeredInit = true; + List<User> users = getAllObjectsDetached( null ); + + fireUserManagerInit( users.isEmpty() ); + } + } + + public JdoFactory getJdoFactory() + { + return jdoFactory; + } + + public void setJdoFactory( JdoFactory jdoFactory ) + { + this.jdoFactory = jdoFactory; + } + + public UserSecurityPolicy getUserSecurityPolicy() + { + return userSecurityPolicy; + } +} diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/main/java/org/codehaus/plexus/redback/users/jdo/JdoUserQuery.java b/redback-users/redback-users-providers/redback-users-jdo/src/main/java/org/codehaus/plexus/redback/users/jdo/JdoUserQuery.java new file mode 100644 index 000000000..1ceb0ba14 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/main/java/org/codehaus/plexus/redback/users/jdo/JdoUserQuery.java @@ -0,0 +1,133 @@ +package org.codehaus.plexus.redback.users.jdo; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.users.AbstractUserQuery; +import org.codehaus.plexus.redback.users.UserQuery; +import org.codehaus.plexus.util.StringUtils; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +public class JdoUserQuery + extends AbstractUserQuery +{ + + /** + * Create the ordering string for use in {@link javax.jdo.Query#setOrdering(String)} + * + * @return the created filter + */ + public String getOrdering() + { + StringBuffer ordering = new StringBuffer(); + + if ( UserQuery.ORDER_BY_EMAIL.equals( getOrderBy() ) ) + { + ordering.append( "email" ); + } + else if ( UserQuery.ORDER_BY_FULLNAME.equals( getOrderBy() ) ) + { + ordering.append( "fullName" ); + } + else + { + ordering.append( "username" ); + } + ordering.append( " " ).append( isAscending() ? "ascending" : "descending" ); + return ordering.toString(); + } + + /** + * Create and return the filter string for use in {@link javax.jdo.Query#setFilter(String)} + * + * @return the query filter + */ + public String getFilter() + { + Set<String> terms = new HashSet<String>(); + + if ( getUsername() != null ) + { + terms.add( "this.username.toLowerCase().indexOf(usernameKey.toLowerCase()) > -1" ); + } + if ( getFullName() != null ) + { + terms.add( "this.fullName.toLowerCase().indexOf(fullNameKey.toLowerCase()) > -1" ); + } + if ( getEmail() != null ) + { + terms.add( "this.email.toLowerCase().indexOf(emailKey.toLowerCase()) > -1" ); + } + + return StringUtils.join( terms.iterator(), " && " ); + } + + /** + * Return an array of parameters for user in {@link javax.jdo.Query#executeWithArray(Object[])} + * + * @return the parameter array + */ + public String[] getSearchKeys() + { + List<String> keys = new ArrayList<String>(); + + if ( getUsername() != null ) + { + keys.add( getUsername() ); + } + if ( getFullName() != null ) + { + keys.add( getFullName() ); + } + if ( getEmail() != null ) + { + keys.add( getEmail() ); + } + + return (String[]) keys.toArray( new String[0] ); + } + + /** + * Returns the parameters for use in {@link javax.jdo.Query#declareParameters(String)} + * + * @return the parameter list + */ + public String getParameters() + { + + List<String> params = new ArrayList<String>(); + + if ( getUsername() != null ) + { + params.add( "String usernameKey" ); + } + if ( getFullName() != null ) + { + params.add( "String fullNameKey" ); + } + if ( getEmail() != null ) + { + params.add( "String emailKey" ); + } + + return StringUtils.join( params.iterator(), ", " ); + } +} diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/main/java/org/codehaus/plexus/redback/users/jdo/Messages.java b/redback-users/redback-users-providers/redback-users-jdo/src/main/java/org/codehaus/plexus/redback/users/jdo/Messages.java new file mode 100644 index 000000000..e9882574e --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/main/java/org/codehaus/plexus/redback/users/jdo/Messages.java @@ -0,0 +1,91 @@ +package org.codehaus.plexus.redback.users.jdo; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Localized Message Handling. + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public class Messages +{ + private static final String BUNDLE_NAME = "org.codehaus.plexus.redback.users.jdo"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle( BUNDLE_NAME ); + + /** + * Get a Message as-is from the Resource Bundle. + * + * @param key the key for the message to get. + * @return the value of the key, or "!key!" if the key is not found. + */ + public static String getString( String key ) + { + try + { + return RESOURCE_BUNDLE.getString( key ); + } + catch ( MissingResourceException e ) + { + return '!' + key + '!'; + } + } + + /** + * Gets a Message from the Resource Bundle, with {1} and {2} style arguments. + * + * @param key the key for the message to get. + * @param arg the argument to pass in. + * @return the value of the key, or "!key!" if the key is not found. + */ + public static String getString( String key, Object arg ) + { + return getString( key, new Object[] { arg } ); + } + + /** + * Gets a Message from the Resource Bundle, with {1} and {2} style arguments. + * + * @param key the key for the message to get. + * @param args the arguments to pass in. + * @return the value of the key, or "!key!" if the key is not found. + */ + public static String getString( String key, Object args[] ) + { + try + { + String pattern = RESOURCE_BUNDLE.getString( key ); + return MessageFormat.format( pattern, args ); + } + catch ( MissingResourceException e ) + { + return '!' + key + '!'; + } + } + + /** + * Prevent Instantiation. + */ + private Messages() + { + } +} diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/main/mdo/user.xml b/redback-users/redback-users-providers/redback-users-jdo/src/main/mdo/user.xml new file mode 100644 index 000000000..d72004481 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/main/mdo/user.xml @@ -0,0 +1,143 @@ +<?xml version="1.0" ?> +<model> + <id>redback-users-jdo</id> + <name>UsersManagement</name> + <version>1.0.1</version> + <description>Plexus Redback Users object model.</description> + <defaults> + <default> + <key>package</key> + <value>org.codehaus.plexus.redback.users.jdo</value> + </default> + </defaults> + + <classes> + <class stash.storable="false" rootElement="true"> + <name>UserDatabase</name> + <version>1.0.1+</version> + <fields> + <field> + <name>users</name> + <version>1.0.1+</version> + <association> + <type>JdoUser</type> + <multiplicity>*</multiplicity> + </association> + </field> + </fields> + </class> + <class stash.storable="true" jpox.use-identifiers-as-primary-key="false"> + <name>JdoUser</name> + <version>1.0.0+</version> + <interfaces> + <interface>org.codehaus.plexus.redback.users.User</interface> + </interfaces> + <description></description> + <fields> + <field jpox.primary-key="true" jpox.value-strategy="off" jpox.persistence-modifier="persistent"> + <name>username</name> + <version>1.0.0+</version> + <type>String</type> + <identifier>true</identifier> + </field> + <field jpox.column="USER_PASSWORD"> + <name>password</name> + <version>1.0.0+</version> + <type>String</type> + <identifier>true</identifier> + </field> + <field> + <name>encodedPassword</name> + <version>1.0.0+</version> + <type>String</type> + <identifier>true</identifier> + </field> + <field> + <name>fullName</name> + <version>1.0.0+</version> + <type>String</type> + <identifier>true</identifier> + </field> + <field> + <name>email</name> + <version>1.0.0+</version> + <type>String</type> + <identifier>true</identifier> + </field> + <field> + <name>lastPasswordChange</name> + <version>1.0.0+</version> + <type>Date</type> + </field> + <field> + <name>lastLoginDate</name> + <version>1.0.0+</version> + <type>Date</type> + </field> + <field> + <name>countFailedLoginAttempts</name> + <version>1.0.0+</version> + <type>int</type> + </field> + <field> + <name>locked</name> + <version>1.0.0+</version> + <type>boolean</type> + <defaultValue>false</defaultValue> + <identifier>true</identifier> + </field> + <field> + <name>permanent</name> + <version>1.0.0+</version> + <type>boolean</type> + <defaultValue>false</defaultValue> + <identifier>true</identifier> + </field> + <field> + <name>validated</name> + <version>1.0.0+</version> + <type>boolean</type> + <defaultValue>false</defaultValue> + <identifier>true</identifier> + </field> + <field> + <name>passwordChangeRequired</name> + <version>1.0.0+</version> + <type>boolean</type> + <defaultValue>false</defaultValue> + <identifier>true</identifier> + </field> + <field> + <name>previousEncodedPasswords</name> + <version>1.0.0+</version> + <association stash.part="true"> + <type>String</type> + <multiplicity>*</multiplicity> + </association> + </field> + <field> + <name>accountCreationDate</name> + <version>1.0.0+</version> + <type>Date</type> + </field> + </fields> + <codeSegments> + <codeSegment> + <version>1.0.0+</version> + <code><![CDATA[ + public JdoUser() + { + // Intentionally initialize List to avoid JPOX NullPointerException Issues. + previousEncodedPasswords = new java.util.ArrayList(); + } + + public Object getPrincipal() + { + return username; + } + ]]></code> + </codeSegment> + </codeSegments> + </class> + </classes> +</model> diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/main/resources/META-INF/spring-context.xml b/redback-users/redback-users-providers/redback-users-jdo/src/main/resources/META-INF/spring-context.xml new file mode 100644 index 000000000..cc2328dfe --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/main/resources/META-INF/spring-context.xml @@ -0,0 +1,33 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd" + default-lazy-init="true"> + + <context:annotation-config /> + <context:component-scan base-package="org.codehaus.plexus.redback.users.jdo"/> + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/main/resources/org/codehaus/plexus/redback/users/jdo/messages.properties b/redback-users/redback-users-providers/redback-users-jdo/src/main/resources/org/codehaus/plexus/redback/users/jdo/messages.properties new file mode 100644 index 000000000..df9bad860 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/main/resources/org/codehaus/plexus/redback/users/jdo/messages.properties @@ -0,0 +1,2 @@ +user.manager.cannot.add.user.without.username=User.username must be supplied on an .addUser() request. +user.manager.cannot.add.user.without.password=User.password must be supplied on an .addUser() request. diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/test/java/org/codehaus/plexus/redback/users/jdo/JdoUserManagerTest.java b/redback-users/redback-users-providers/redback-users-jdo/src/test/java/org/codehaus/plexus/redback/users/jdo/JdoUserManagerTest.java new file mode 100644 index 000000000..d23ed7822 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/test/java/org/codehaus/plexus/redback/users/jdo/JdoUserManagerTest.java @@ -0,0 +1,120 @@ +package org.codehaus.plexus.redback.users.jdo; + +/* + * Copyright 2001-2006 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.jdo.DefaultConfigurableJdoFactory; +import org.codehaus.plexus.redback.common.jdo.test.StoreManagerDebug; +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.codehaus.plexus.redback.users.provider.test.AbstractUserManagerTestCase; +import org.jpox.AbstractPersistenceManagerFactory; +import org.jpox.SchemaTool; +import org.junit.Before; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.jdo.PersistenceManager; +import javax.jdo.PersistenceManagerFactory; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * JdoUserManagerTest + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public class JdoUserManagerTest + extends AbstractUserManagerTestCase +{ + @Inject + @Named( value = "jdoFactory#users" ) + DefaultConfigurableJdoFactory jdoFactory; + + @Inject + @Named( value = "userManager#jdo" ) + JdoUserManager jdoUserManager; + + private StoreManagerDebug storeManager; + + @Before + public void setUp() + throws Exception + { + super.setUp(); + + jdoFactory.setPersistenceManagerFactoryClass( "org.jpox.PersistenceManagerFactoryImpl" ); //$NON-NLS-1$ + + jdoFactory.setDriverName( "org.hsqldb.jdbcDriver" ); //$NON-NLS-1$ + + jdoFactory.setUrl( "jdbc:hsqldb:mem:" + getName() ); //$NON-NLS-1$ + + jdoFactory.setUserName( "sa" ); //$NON-NLS-1$ + + jdoFactory.setPassword( "" ); //$NON-NLS-1$ + + jdoFactory.setProperty( "org.jpox.transactionIsolation", "READ_COMMITTED" ); //$NON-NLS-1$ //$NON-NLS-2$ + + jdoFactory.setProperty( "org.jpox.poid.transactionIsolation", "READ_COMMITTED" ); //$NON-NLS-1$ //$NON-NLS-2$ + + jdoFactory.setProperty( "org.jpox.autoCreateSchema", "true" ); //$NON-NLS-1$ //$NON-NLS-2$ + + Properties properties = jdoFactory.getProperties(); + + for ( Map.Entry<?, ?> entry : properties.entrySet() ) + { + System.setProperty( (String) entry.getKey(), (String) entry.getValue() ); + } + + PersistenceManagerFactory pmf = jdoFactory.getPersistenceManagerFactory(); + + assertNotNull( pmf ); + + /* set our own Store Manager to allow counting SQL statements */ + StoreManagerDebug.setup( (AbstractPersistenceManagerFactory) pmf ); + + SchemaTool.createSchemaTables( + new URL[]{ getClass().getResource( "/org/codehaus/plexus/redback/users/jdo/package.jdo" ) }, new URL[]{ }, + null, false, null ); //$NON-NLS-1$ + + PersistenceManager pm = pmf.getPersistenceManager(); + + pm.close(); + + setUserManager( jdoUserManager ); + + /* save the store manager to access the queries executed */ + JdoUserManager userManager = (JdoUserManager) getUserManager(); + storeManager = StoreManagerDebug.getConfiguredStoreManager( userManager.getPersistenceManager() ); + + } + + protected void assertCleanUserManager() + { + // database cleanup + ( (JdoUserManager) getUserManager()).eraseDatabase(); + + + + super.assertCleanUserManager(); + } + + +} diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/test/resources/org/codehaus/plexus/redback/config-defaults.properties b/redback-users/redback-users-providers/redback-users-jdo/src/test/resources/org/codehaus/plexus/redback/config-defaults.properties new file mode 100644 index 000000000..d0dd8d724 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/test/resources/org/codehaus/plexus/redback/config-defaults.properties @@ -0,0 +1,127 @@ +# +# Copyright 2006 The Codehaus. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. +# + +# -------------------------------------------------------------------- +# Application Configuration + +application.timestamp=EEE d MMM yyyy HH:mm:ss Z + +# -------------------------------------------------------------------- +# JDBC Setup + +jdbc.driver.name=org.hsqldb.jdbcDriver +jdbc.url=jdbc:hsqldb:mem:redback-test +jdbc.username=sa +jdbc.password= + +# -------------------------------------------------------------------- +# Email Settings + +email.jndiSessionName=java:comp/env/mail/Session +email.smtp.host=localhost +email.smtp.port=25 +email.smtp.ssl.enabled=false +email.smtp.tls.enabled=false +email.smtp.username= +email.smtp.password= + +#TODO: move description elsewhere, remove bad default +# All emails sent by the system will be from the following address +#email.from.address=${user.name}@localhost +# All emails sent by the system will be from the following user name (used in conjunction with address) +#email.from.name=Unconfigured Username + +# If all email addresses (from new user registration) require an account validation email. +email.validation.required=true +# Timeout (in minutes) for the key generated for an email validation to remain valid. +# 2880 minutes = 48 hours +email.validation.timeout=2880 +# The subject line for the email message. +email.validation.subject=Welcome + +#TODO: move description elsewhere, remove bad default +# Get the Feedback to use for any outgoing emails. +# NOTE: if feedback.path starts with a "/" it is appended to the end of the value provided in application.url +# This value can be in the format/syntax of "/feedback.action" or even "mailto:feedback@application.com" +#email.feedback.path=/feedback.action + +#Set the application base URL. The default is to derive it from the HTTP request +#application.url=http://myurl.mycompany.com + +# -------------------------------------------------------------------- +# Auto Login Settings + +security.rememberme.enabled=true +# Timeout in minutes ( 525600 minutes = 1 year ) +security.rememberme.timeout=525600 + +# Single Sign On +# Timeout in minutes +security.signon.timeout=30 + +# -------------------------------------------------------------------- +# Default Username Values +redback.default.admin=admin + +# -------------------------------------------------------------------- +# Security Policies + +#security.policy.password.encoder= +security.policy.password.previous.count=6 +security.policy.password.expiration.enabled=true +security.policy.password.expiration.days=90 +security.policy.password.expiration.notify.days=10 +security.policy.allowed.login.attempt=10 + +# turn off the perclick enforcement of various security policies, slightly +# more heavyweight since it will ensure that the User object on each click +# is up to date +security.policy.strict.enforcement.enabled=true +security.policy.strict.force.password.change.enabled=true + +# -------------------------------------------------------------------- +# Password Rules +security.policy.password.rule.alphanumeric.enabled=false +security.policy.password.rule.alphacount.enabled=true +security.policy.password.rule.alphacount.minimum=1 +security.policy.password.rule.characterlength.enabled=true +security.policy.password.rule.characterlength.minimum=1 +security.policy.password.rule.characterlength.maximum=24 +security.policy.password.rule.musthave.enabled=true +security.policy.password.rule.numericalcount.enabled=true +security.policy.password.rule.numericalcount.minimum=1 +security.policy.password.rule.reuse.enabled=true +security.policy.password.rule.nowhitespace.enabled=true + +# -------------------------------------------------------------------- +# ldap settings +# +ldap.bind.authenticator.enabled=false + +# ldap options for configuration via properties file +#ldap.config.hostname= +#ldap.config.port= +#ldap.config.base.dn= +#ldap.config.context.factory= +#ldap.config.bind.dn= +#ldap.config.password= +#ldap.config.authentication.method= + +# config parameter for the ConfigurableUserManager +user.manager.impl=cached + + + diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/test/resources/spring-context.xml b/redback-users/redback-users-providers/redback-users-jdo/src/test/resources/spring-context.xml new file mode 100644 index 000000000..8ee5839c8 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/test/resources/spring-context.xml @@ -0,0 +1,55 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + + <bean name="jdoFactory#users" class="org.codehaus.plexus.redback.common.jdo.UserConfigurableJdoFactory"> + <property name="config" ref="userConfiguration"/> + <property name="driverName" value="org.hsqldb.jdbcDriver"/> + <property name="url" value="jdbc:hsqldb:mem:redback-users-tests" /> + <property name="userName" value="sa"/> + <property name="password" value=""/> + </bean> + + <bean name="userConfiguration" class="org.codehaus.plexus.redback.configuration.UserConfiguration"> + <property name="registry" ref="test-conf"/> + </bean> + + <bean name="commons-configuration" class="org.codehaus.redback.components.registry.commons.CommonsConfigurationRegistry"> + <property name="properties"> + <value> + <![CDATA[ + <configuration> + <properties fileName="test.properties" config-name="properties"/> + </configuration> + ]]> + </value> + </property> + </bean> + + <alias name="commons-configuration" alias="test-conf"/> + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-jdo/src/test/resources/test.properties b/redback-users/redback-users-providers/redback-users-jdo/src/test/resources/test.properties new file mode 100644 index 000000000..db486ea3e --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-jdo/src/test/resources/test.properties @@ -0,0 +1 @@ +jdbc.driver.name=org.hsqldb.jdbcDriver
\ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-ldap/pom.xml b/redback-users/redback-users-providers/redback-users-ldap/pom.xml new file mode 100644 index 000000000..0bda5365c --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/pom.xml @@ -0,0 +1,95 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-providers</artifactId> + <version>1.5-SNAPSHOT</version> + </parent> + + <artifactId>redback-users-ldap</artifactId> + + <name>Redback :: Users Provider :: LDAP</name> + + <dependencies> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-policy</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-common-ldap</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback.components.cache</groupId> + <artifactId>spring-cache-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback.components.cache</groupId> + <artifactId>spring-cache-ehcache</artifactId> + </dependency> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>jsr250-api</artifactId> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.codehaus.redback.components</groupId> + <artifactId>spring-apacheds</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + + <build> + <plugins> + <plugin> + <groupId>org.sonatype.plugins</groupId> + <artifactId>port-allocator-maven-plugin</artifactId> + <version>1.1</version> + <executions> + <execution> + <id>allocate-ldap-port</id> + <phase>process-classes</phase> + <goals> + <goal>allocate-ports</goal> + </goals> + <configuration> + <ports> + <port> + <name>ldapPort</name> + <portNumber>10390</portNumber> + </port> + </ports> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <systemPropertyVariables> + <ldapPort>${ldapPort}</ldapPort> + <basedir>${basedir}</basedir> + </systemPropertyVariables> + </configuration> + </plugin> + </plugins> + </build> + + +</project> diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/LdapUserManager.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/LdapUserManager.java new file mode 100644 index 000000000..3e410a862 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/LdapUserManager.java @@ -0,0 +1,498 @@ +package org.codehaus.plexus.redback.users.ldap; +/* + * Copyright 2001-2007 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + + +import org.codehaus.plexus.redback.common.ldap.LdapUser; +import org.codehaus.plexus.redback.common.ldap.MappingException; +import org.codehaus.plexus.redback.common.ldap.UserMapper; +import org.codehaus.plexus.redback.common.ldap.connection.LdapConnection; +import org.codehaus.plexus.redback.common.ldap.connection.LdapConnectionFactory; +import org.codehaus.plexus.redback.common.ldap.connection.LdapException; +import org.codehaus.plexus.redback.users.AbstractUserManager; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.codehaus.plexus.redback.users.UserQuery; +import org.codehaus.plexus.redback.users.ldap.ctl.LdapController; +import org.codehaus.plexus.redback.users.ldap.ctl.LdapControllerException; +import org.codehaus.plexus.redback.users.ldap.service.LdapCacheService; +import org.springframework.stereotype.Service; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.naming.directory.DirContext; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author <a href="jesse@codehaus.org"> jesse + * @version "$Id$" + */ +@Service( "userManager#ldap" ) +public class LdapUserManager + extends AbstractUserManager +{ + @Inject + @Named( value = "ldapConnectionFactory#configurable" ) + private LdapConnectionFactory connectionFactory; + + @Inject + private LdapController controller; + + @Inject + @Named( value = "userMapper#ldap" ) + private UserMapper mapper; + + @Inject + private LdapCacheService ldapCacheService; + + private User guestUser; + + public boolean isReadOnly() + { + return true; + } + + public User addUser( User user ) + { + return addUser( user, true ); + } + + public void addUserUnchecked( User user ) + { + addUser( user, false ); + } + + private User addUser( User user, boolean checked ) + { + if ( user == null ) + { + return null; + } + + if ( GUEST_USERNAME.equals( user.getUsername() ) ) + { + guestUser = user; + return guestUser; + } + + LdapConnection ldapConnection = getLdapConnection(); + try + { + DirContext context = ldapConnection.getDirContext(); + controller.createUser( user, context, checked ); + } + catch ( LdapControllerException e ) + { + log.error( "Error mapping user: " + user.getPrincipal() + " to LDAP attributes.", e ); + } + catch ( MappingException e ) + { + log.error( "Error mapping user: " + user.getPrincipal() + " to LDAP attributes.", e ); + } + finally + { + closeLdapConnection( ldapConnection ); + } + return user; + } + + public User createUser( String username, String fullName, String emailAddress ) + { + return mapper.newUserInstance( username, fullName, emailAddress ); + } + + public UserQuery createUserQuery() + { + return new LdapUserQuery(); + } + + public void deleteUser( Object principal ) + throws UserNotFoundException + { + if ( principal != null ) + { + clearFromCache( principal.toString() ); + } + + LdapConnection ldapConnection = getLdapConnection(); + try + { + DirContext context = ldapConnection.getDirContext(); + controller.removeUser( principal, context ); + } + catch ( LdapControllerException e ) + { + log.error( "Failed to delete user: {}", principal, e ); + } + finally + { + closeLdapConnection( ldapConnection ); + } + } + + public void deleteUser( String username ) + throws UserNotFoundException + { + if ( username != null ) + { + clearFromCache( username ); + } + + LdapConnection ldapConnection = getLdapConnection(); + try + { + DirContext context = ldapConnection.getDirContext(); + controller.removeUser( username, context ); + } + catch ( LdapControllerException e ) + { + log.error( "Failed to delete user: " + username, e ); + } + finally + { + closeLdapConnection( ldapConnection ); + } + } + + public void eraseDatabase() + { + // TODO Implement erase! + } + + public User findUser( String username ) + throws UserNotFoundException + { + if ( username == null ) + { + throw new UserNotFoundException( "Unable to find user based on null username." ); + } + + if ( GUEST_USERNAME.equals( username ) ) + { + return getGuestUser(); + } + + // REDBACK-289/MRM-1488 + // look for the user in the cache first + LdapUser ldapUser = ldapCacheService.getUser( username ); + if ( ldapUser != null ) + { + log.debug( "User {} found in cache.", username ); + return ldapUser; + } + + LdapConnection ldapConnection = getLdapConnection(); + try + { + DirContext context = ldapConnection.getDirContext(); + User user = controller.getUser( username, context ); + if ( user == null ) + { + throw new UserNotFoundException( "user with name " + username + " not found " ); + } + + // REDBACK-289/MRM-1488 + log.debug( "Adding user {} to cache..", username ); + + ldapCacheService.addUser( (LdapUser) user ); + + return user; + } + catch ( LdapControllerException e ) + { + log.error( "Failed to find user: {}", username, e ); + return null; + } + catch ( MappingException e ) + { + log.error( "Failed to map user: {}", username, e ); + return null; + } + finally + { + closeLdapConnection( ldapConnection ); + } + } + + public User getGuestUser() + throws UserNotFoundException + { + if ( guestUser == null ) + { + throw new UserNotFoundException( "Guest user doesn't exist." ); + } + return guestUser; + } + + public User findUser( Object principal ) + throws UserNotFoundException + { + if ( principal == null ) + { + throw new UserNotFoundException( "Unable to find user based on null principal." ); + } + + if ( GUEST_USERNAME.equals( principal.toString() ) ) + { + return getGuestUser(); + } + + // REDBACK-289/MRM-1488 + // look for the user in the cache first + LdapUser ldapUser = ldapCacheService.getUser( principal.toString() ); + if ( ldapUser != null ) + { + log.debug( "User {} found in cache.", principal ); + return ldapUser; + } + + LdapConnection ldapConnection = getLdapConnection(); + try + { + DirContext context = ldapConnection.getDirContext(); + + User user = controller.getUser( principal, context ); + + // REDBACK-289/MRM-1488 + log.debug( "Adding user {} to cache..", principal ); + + ldapCacheService.addUser( (LdapUser) user ); + + return user; + } + catch ( LdapControllerException e ) + { + log.error( "Failed to find user: {}", principal, e ); + return null; + } + catch ( MappingException e ) + { + log.error( "Failed to map user: {}", principal, e ); + return null; + } + finally + { + closeLdapConnection( ldapConnection ); + } + } + + public List<User> findUsersByEmailKey( String emailKey, boolean orderAscending ) + { + LdapUserQuery query = new LdapUserQuery(); + query.setEmail( emailKey ); + query.setOrderBy( UserQuery.ORDER_BY_EMAIL ); + query.setAscending( orderAscending ); + return findUsersByQuery( query ); + } + + public List<User> findUsersByFullNameKey( String fullNameKey, boolean orderAscending ) + { + LdapUserQuery query = new LdapUserQuery(); + query.setFullName( fullNameKey ); + query.setOrderBy( UserQuery.ORDER_BY_FULLNAME ); + query.setAscending( orderAscending ); + return findUsersByQuery( query ); + } + + public List<User> findUsersByQuery( UserQuery query ) + { + if ( query == null ) + { + return Collections.emptyList(); + } + + LdapConnection ldapConnection = getLdapConnection(); + try + { + DirContext context = ldapConnection.getDirContext(); + return controller.getUsersByQuery( (LdapUserQuery) query, context ); + } + catch ( LdapControllerException e ) + { + log.error( "Failed to find user", e ); + return null; + } + catch ( MappingException e ) + { + log.error( "Failed to map user", e ); + return null; + } + finally + { + closeLdapConnection( ldapConnection ); + } + } + + /** + * @see org.codehaus.plexus.redback.users.UserManager#findUsersByUsernameKey(java.lang.String, boolean) + */ + public List<User> findUsersByUsernameKey( String usernameKey, boolean orderAscending ) + { + LdapUserQuery query = new LdapUserQuery(); + query.setUsername( usernameKey ); + query.setOrderBy( UserQuery.ORDER_BY_USERNAME ); + query.setAscending( orderAscending ); + return findUsersByQuery( query ); + } + + public String getId() + { + return "LDAP User-Manager: " + getClass().getName(); + } + + /** + * @see org.codehaus.plexus.redback.users.UserManager#getUsers() + */ + public List<User> getUsers() + { + LdapConnection ldapConnection = getLdapConnection(); + try + { + DirContext context = ldapConnection.getDirContext(); + List<User> users = new ArrayList<User>( controller.getUsers( context ) ); + //We add the guest user because it isn't in LDAP + try + { + User u = getGuestUser(); + if ( u != null ) + { + users.add( u ); + } + } + catch ( UserNotFoundException e ) + { + //Nothing to do + } + return users; + } + catch ( Exception e ) + { + log.error( e.getMessage(), e ); + } + finally + { + closeLdapConnection( ldapConnection ); + } + return Collections.emptyList(); + } + + public List<User> getUsers( boolean orderAscending ) + { + return getUsers(); + } + + public User updateUser( User user ) + throws UserNotFoundException + { + return updateUser( user, false ); + } + + public User updateUser( User user, boolean passwordChangeRequired ) + throws UserNotFoundException + { + if ( user != null ) + { + clearFromCache( user.getUsername() ); + } + + LdapConnection ldapConnection = getLdapConnection(); + try + { + DirContext context = ldapConnection.getDirContext(); + controller.updateUser( user, context ); + } + catch ( LdapControllerException e ) + { + log.error( "Failed to update user: " + user.getPrincipal(), e ); + } + catch ( MappingException e ) + { + log.error( "Failed to update user: " + user.getPrincipal(), e ); + } + finally + { + closeLdapConnection( ldapConnection ); + } + return user; + } + + public boolean userExists( Object principal ) + { + if ( principal == null ) + { + return false; + } + + // REDBACK-289/MRM-1488 + // look for the user in the cache first + LdapUser ldapUser = ldapCacheService.getUser( principal.toString() ); + if ( ldapUser != null ) + { + log.debug( "User {} found in cache.", principal ); + return true; + } + + LdapConnection ldapConnection = getLdapConnection(); + try + { + DirContext context = ldapConnection.getDirContext(); + return controller.userExists( principal, context ); + } + catch ( LdapControllerException e ) + { + log.warn( "Failed to search for user: " + principal, e ); + return false; + } + finally + { + closeLdapConnection( ldapConnection ); + } + } + + private LdapConnection getLdapConnection() + { + try + { + return connectionFactory.getConnection(); + } + catch ( LdapException e ) + { + log.warn( "failed to get a ldap connection " + e.getMessage(), e ); + throw new RuntimeException( "failed to get a ldap connection " + e.getMessage(), e ); + } + } + + private void closeLdapConnection( LdapConnection ldapConnection ) + { + if ( ldapConnection != null ) + { + ldapConnection.close(); + } + } + + // REDBACK-289/MRM-1488 + private void clearFromCache( String username ) + { + log.debug( "Removing user {} from cache..", username ); + ldapCacheService.removeUser( username ); + + log.debug( "Removing userDn for user {} from cache..", username ); + ldapCacheService.removeLdapUserDn( username ); + } + +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/LdapUserQuery.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/LdapUserQuery.java new file mode 100644 index 000000000..6c0580543 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/LdapUserQuery.java @@ -0,0 +1,60 @@ +package org.codehaus.plexus.redback.users.ldap; + +/* + * Copyright 2001-2007 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.common.ldap.UserMapper; +import org.codehaus.plexus.redback.users.AbstractUserQuery; + +public class LdapUserQuery + extends AbstractUserQuery +{ + + public void setFirstResult( int firstResult ) + { + super.setFirstResult( firstResult ); + throw new UnsupportedOperationException( "Result limiting is not yet supported for LDAP." ); + } + + public void setMaxResults( int maxResults ) + { + super.setMaxResults( maxResults ); + throw new UnsupportedOperationException( "Result limiting is not yet supported for LDAP." ); + } + + public void setOrderBy( String orderBy ) + { + super.setOrderBy( orderBy ); + throw new UnsupportedOperationException( "Free-form ordering is not yet supported for LDAP." ); + } + + public String getLdapFilter( UserMapper mapper ) + { + String filter = ""; + if (this.getEmail() != null ) + { + filter += "(" + mapper.getEmailAddressAttribute() + "=" + this.getEmail() + ")"; + } + if ( this.getFullName() != null ) + { + filter += "(" + mapper.getUserFullNameAttribute() + "=" + this.getFullName() + ")"; + } + filter += "(" + mapper.getUserIdAttribute() + "=" + ( this.getUsername() != null ? this.getUsername() : "*" ) + ")"; + + return filter; + } + +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/ctl/DefaultLdapController.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/ctl/DefaultLdapController.java new file mode 100644 index 000000000..4aeb71841 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/ctl/DefaultLdapController.java @@ -0,0 +1,298 @@ +package org.codehaus.plexus.redback.users.ldap.ctl; + +/* + * Copyright 2001-2007 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; + +import org.codehaus.plexus.redback.common.ldap.LdapUser; +import org.codehaus.plexus.redback.common.ldap.LdapUserMapper; +import org.codehaus.plexus.redback.common.ldap.MappingException; +import org.codehaus.plexus.redback.common.ldap.UserMapper; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.ldap.LdapUserQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +/** + * @author <a href="jesse@codehaus.org"> jesse + * @version "$Id$" + */ +@Service +public class DefaultLdapController + implements LdapController +{ + + private Logger log = LoggerFactory.getLogger( getClass() ); + + @Inject + @Named(value = "userMapper#ldap") + private UserMapper mapper; + + /** + * @see org.codehaus.plexus.redback.users.ldap.ctl.LdapControllerI#removeUser(java.lang.Object, javax.naming.directory.DirContext) + */ + public void removeUser( Object principal, DirContext context ) + throws LdapControllerException + { + + } + + /** + * @see org.codehaus.plexus.redback.users.ldap.ctl.LdapControllerI#updateUser(org.codehaus.plexus.redback.users.User, javax.naming.directory.DirContext) + */ + public void updateUser( User user, DirContext context ) + throws LdapControllerException, MappingException + { + + } + + /** + * @see org.codehaus.plexus.redback.users.ldap.ctl.LdapControllerI#userExists(java.lang.Object, javax.naming.directory.DirContext) + */ + public boolean userExists( Object key, DirContext context ) + throws LdapControllerException + { + NamingEnumeration<SearchResult> results = null; + try + { + results = searchUsers( key, context ); + return results.hasMoreElements(); + } + catch ( NamingException e ) + { + throw new LdapControllerException( "Error searching for the existence of user: " + key, e ); + } + finally + { + if ( results != null ) + try + { + results.close(); + } + catch ( NamingException e ) + { + log.warn( "Error closing search results", e ); + } + } + } + + protected NamingEnumeration<SearchResult> searchUsers( Object key, DirContext context ) + throws NamingException + { + LdapUserQuery query = new LdapUserQuery(); + query.setUsername( "" + key ); + return searchUsers( context, null, query ); + } + + protected NamingEnumeration<SearchResult> searchUsers( DirContext context ) + throws NamingException + { + return searchUsers( context, null, null ); + } + + protected NamingEnumeration<SearchResult> searchUsers( DirContext context, String[] returnAttributes ) + throws NamingException + { + return searchUsers( context, returnAttributes, null ); + } + + protected NamingEnumeration<SearchResult> searchUsers( DirContext context, String[] returnAttributes, LdapUserQuery query ) + throws NamingException + { + if ( query == null ) + { + query = new LdapUserQuery(); + } + SearchControls ctls = new SearchControls(); + + ctls.setDerefLinkFlag( true ); + ctls.setSearchScope( SearchControls.SUBTREE_SCOPE ); + ctls.setReturningAttributes( mapper.getReturningAttributes() ); + ctls.setCountLimit( ( ( LdapUserMapper ) mapper ).getMaxResultCount() ); + + String finalFilter = "(&(objectClass=" + mapper.getUserObjectClass() + ")" + + ( mapper.getUserFilter() != null ? mapper.getUserFilter() : "" ) + query.getLdapFilter(mapper) + ")"; + + log.info( "Searching for users with filter: \'{}\'" + " from base dn: {}",finalFilter, mapper.getUserBaseDn() ); + + return context.search( mapper.getUserBaseDn(), finalFilter, ctls ); + } + + /** + * @see org.codehaus.plexus.redback.users.ldap.ctl.LdapControllerI#getUsers(javax.naming.directory.DirContext) + */ + public Collection<User> getUsers( DirContext context ) + throws LdapControllerException, MappingException + { + NamingEnumeration<SearchResult> results = null; + try + { + results = searchUsers( context, null, null ); + Set<User> users = new LinkedHashSet<User>(); + + while ( results.hasMoreElements() ) + { + SearchResult result = results.nextElement(); + + users.add( mapper.getUser( result.getAttributes() ) ); + } + + return users; + } + catch ( NamingException e ) + { + String message = "Failed to retrieve ldap information for users."; + + throw new LdapControllerException( message, e ); + } + finally + { + if ( results != null ) + try + { + results.close(); + } + catch ( NamingException e ) + { + log.warn( "failed to close search results", e ); + } + } + } + + /** + * @see org.codehaus.plexus.redback.users.ldap.ctl.LdapControllerI#getUsersByQuery(org.codehaus.plexus.redback.users.ldap.LdapUserQuery, javax.naming.directory.DirContext) + */ + public List<User> getUsersByQuery( LdapUserQuery query, DirContext context ) + throws LdapControllerException, MappingException + { + NamingEnumeration<SearchResult> results = null; + try + { + results = searchUsers( context, null, query ); + List<User> users = new LinkedList<User>(); + + while ( results.hasMoreElements() ) + { + SearchResult result = results.nextElement(); + + users.add( mapper.getUser( result.getAttributes() ) ); + } + + return users; + } + catch ( NamingException e ) + { + String message = "Failed to retrieve ldap information for users."; + + throw new LdapControllerException( message, e ); + } + finally + { + if ( results != null ) + try + { + results.close(); + } + catch ( NamingException e ) + { + log.warn( "failed to close search results", e ); + } + } + } + + /** + * @see org.codehaus.plexus.redback.users.ldap.ctl.LdapControllerI#createUser(org.codehaus.plexus.redback.users.User, javax.naming.directory.DirContext, boolean) + */ + public void createUser( User user, DirContext context, boolean encodePasswordIfChanged ) + throws LdapControllerException, MappingException + { + if ( user == null ) + { + return; + } + if ( user.getUsername().equals( UserManager.GUEST_USERNAME ) ) + { + //We don't store guest + return; + } + + } + + /** + * @see org.codehaus.plexus.redback.users.ldap.ctl.LdapControllerI#getUser(java.lang.Object, javax.naming.directory.DirContext) + */ + public LdapUser getUser( Object key, DirContext context ) + throws LdapControllerException, MappingException + { + String username = key.toString(); + + log.info( "Searching for user: {}", username ); + LdapUserQuery query = new LdapUserQuery(); + query.setUsername( username ); + + NamingEnumeration<SearchResult> result = null; + try + { + result = searchUsers( context, null, query ); + + if ( result.hasMoreElements() ) + { + SearchResult next = result.nextElement(); + + return mapper.getUser( next.getAttributes() ); + } + else + { + return null; + } + } + catch ( NamingException e ) + { + String message = "Failed to retrieve information for user: " + username; + + throw new LdapControllerException( message, e ); + } + finally + { + if ( result != null ) + try + { + result.close(); + } + catch ( NamingException e ) + { + log.warn( "failed to close search results", e ); + } + } + } + +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/ctl/LdapController.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/ctl/LdapController.java new file mode 100644 index 000000000..d94da8179 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/ctl/LdapController.java @@ -0,0 +1,54 @@ +package org.codehaus.plexus.redback.users.ldap.ctl; + +/* + * Copyright 2001-2007 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.common.ldap.LdapUser; +import org.codehaus.plexus.redback.common.ldap.MappingException; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.ldap.LdapUserQuery; + +import javax.naming.directory.DirContext; +import java.util.Collection; +import java.util.List; + +/** + * @version $Id$ + */ +public interface LdapController +{ + + void removeUser( Object principal, DirContext context ) + throws LdapControllerException; + + void updateUser( User user, DirContext context ) + throws LdapControllerException, MappingException; + + boolean userExists( Object key, DirContext context ) + throws LdapControllerException; + + Collection<User> getUsers( DirContext context ) + throws LdapControllerException, MappingException; + + void createUser( User user, DirContext context, boolean encodePasswordIfChanged ) + throws LdapControllerException, MappingException; + + LdapUser getUser( Object key, DirContext context ) + throws LdapControllerException, MappingException; + + List<User> getUsersByQuery( LdapUserQuery query, DirContext context ) + throws LdapControllerException, MappingException; +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/ctl/LdapControllerException.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/ctl/LdapControllerException.java new file mode 100644 index 000000000..821f36681 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/ctl/LdapControllerException.java @@ -0,0 +1,33 @@ +package org.codehaus.plexus.redback.users.ldap.ctl; + +/* + * Copyright 2001-2007 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +public class LdapControllerException + extends Exception +{ + + public LdapControllerException( String message, Throwable cause ) + { + super( message, cause ); + } + + public LdapControllerException( String message ) + { + super( message ); + } + +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/service/DefaultLdapCacheService.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/service/DefaultLdapCacheService.java new file mode 100644 index 000000000..75a185372 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/service/DefaultLdapCacheService.java @@ -0,0 +1,118 @@ +package org.codehaus.plexus.redback.users.ldap.service; + +/* + * Copyright 2011 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.cache.builder.CacheBuilder; +import org.codehaus.plexus.redback.common.ldap.LdapUser; +import org.springframework.stereotype.Service; + +import javax.inject.Inject; + +/** + * DefaultLdapCacheService + * + * @author: Maria Odea Ching <oching@apache.org> + * @version + */ +@Service +public class DefaultLdapCacheService + implements LdapCacheService +{ + @Inject + private CacheBuilder cacheBuilder; + + // LDAP Users + + /** + * @see LdapCacheService#getUser(String) + */ + public LdapUser getUser( String username ) + { + return (LdapUser) cacheBuilder.getCache( "ldapUser" ).get( username ); + } + + /** + * @see LdapCacheService#removeUser(String) + */ + public boolean removeUser( String username ) + { + return ( cacheBuilder.getCache( "ldapUser" ).remove( username ) == null ? false : true ); + } + + /** + * @see LdapCacheService#removeAllUsers() + */ + public void removeAllUsers() + { + cacheBuilder.getCache( "ldapUser" ).clear(); + } + + /** + * @see LdapCacheService#addUser(org.codehaus.plexus.redback.common.ldap.LdapUser) + */ + public void addUser( LdapUser user ) + { + LdapUser existingUser = (LdapUser) cacheBuilder.getCache( "ldapUser" ).get( user.getUsername() ); + if( existingUser != null ) + { + removeUser( user.getUsername() ); + } + + cacheBuilder.getCache( "ldapUser" ).put( user.getUsername(), user ); + } + + // LDAP UserDn + + /** + * @see LdapCacheService#getLdapUserDn(String) + */ + public String getLdapUserDn( String username ) + { + return (String) cacheBuilder.getCache( "ldapUserDn" ).get( username ); + } + + /** + * @see LdapCacheService#removeLdapUserDn(String) + */ + public boolean removeLdapUserDn( String username ) + { + return ( cacheBuilder.getCache( "ldapUserDn" ).remove( username ) == null ? false : true ); + } + + /** + * @see org.codehaus.plexus.redback.users.ldap.service.LdapCacheService#removeAllLdapUserDn() + */ + public void removeAllLdapUserDn() + { + cacheBuilder.getCache( "ldapUserDn" ).clear(); + } + + /** + * @see LdapCacheService#addLdapUserDn(String, String) + */ + public void addLdapUserDn( String username, String userDn ) + { + String existingUserDn = (String) cacheBuilder.getCache( "ldapUserDn" ).get( username ); + if( existingUserDn != null ) + { + removeUser( username ); + } + + cacheBuilder.getCache( "ldapUserDn" ).put( username, userDn ); + } + +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/service/LdapCacheService.java b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/service/LdapCacheService.java new file mode 100644 index 000000000..322f56bf9 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/java/org/codehaus/plexus/redback/users/ldap/service/LdapCacheService.java @@ -0,0 +1,90 @@ +package org.codehaus.plexus.redback.users.ldap.service; + +/* + * Copyright 2011 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.common.ldap.LdapUser; + +/** + * LdapCacheService + * + * Service that manages the LDAP caches: LDAP connections and LDAP users + * + * @author: Maria Odea Ching <oching@apache.org> + * @version + */ +public interface LdapCacheService +{ + /** + * Retrieve LDAP user with the given username from the cache. + * Returns null if user is not found. + * + * @param username + * @return + */ + LdapUser getUser( String username ); + + /** + * Remove LDAP user with the given username from the cache. + * Returns the removed object if it was in the cache. Otherwise, returns null. + * + * @param username + * @return + */ + boolean removeUser( String username ); + + /** + * Remove all LDAP users in the cache. In short, it flushes the cache. + * + */ + void removeAllUsers(); + + /** + * Adds the user to the LDAP users cache. + * + * @param user + */ + void addUser( LdapUser user ); + + /** + * Retrieve the cached LDAP userDn for the given user. + * + * @param username + * @return + */ + String getLdapUserDn( String username ); + + /** + * Remove the cached LDAP userDn for the given user. + * + * @param username + * @return + */ + boolean removeLdapUserDn( String username ); + + /** + * Remove all cached LDAP userDn + */ + void removeAllLdapUserDn(); + + /** + * All the LDAP userDn for the given user to the cache + * + * @param username + * @param userDn + */ + void addLdapUserDn( String username, String userDn ); +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/main/resources/META-INF/spring-context.xml b/redback-users/redback-users-providers/redback-users-ldap/src/main/resources/META-INF/spring-context.xml new file mode 100644 index 000000000..4daa87b8d --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/main/resources/META-INF/spring-context.xml @@ -0,0 +1,61 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd" + default-lazy-init="true"> + + <context:annotation-config /> + <context:component-scan + base-package="org.codehaus.plexus.redback.users.ldap"/> + + <!-- REDBACK-289/MRM-1488 --> + <!-- Cache for LDAP users. Data is refreshed every 3 mins. --> + <bean name="cache#ldapUser" class="org.codehaus.plexus.cache.ehcache.EhcacheCache"> + <property name="diskExpiryThreadIntervalSeconds" value="180"/> + <property name="diskPersistent" value="false"/> + <property name="eternal" value="false"/> + <property name="maxElementsInMemory" value="1000"/> + <property name="memoryEvictionPolicy" value="LRU"/> + <property name="name" value="ldapUsersCache"/> + <property name="overflowToDisk" value="false"/> + <property name="timeToIdleSeconds" value="90"/> + <property name="timeToLiveSeconds" value="180"/> + </bean> + + <!-- Cache for users' LDAP userDn. Data is refreshed every 3 mins. --> + <bean name="cache#ldapUserDn" class="org.codehaus.plexus.cache.ehcache.EhcacheCache"> + <property name="diskExpiryThreadIntervalSeconds" value="180"/> + <property name="diskPersistent" value="false"/> + <property name="eternal" value="false"/> + <property name="maxElementsInMemory" value="1000"/> + <property name="memoryEvictionPolicy" value="LRU"/> + <property name="name" value="ldapUserDnCache"/> + <property name="overflowToDisk" value="false"/> + <property name="timeToIdleSeconds" value="90"/> + <property name="timeToLiveSeconds" value="180"/> + </bean> + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/codehaus/plexus/redback/users/ldap/LdapUserManagerTest.java b/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/codehaus/plexus/redback/users/ldap/LdapUserManagerTest.java new file mode 100644 index 000000000..372dff20c --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/codehaus/plexus/redback/users/ldap/LdapUserManagerTest.java @@ -0,0 +1,337 @@ +package org.codehaus.plexus.redback.users.ldap; + +/* + * Copyright 2001-2006 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import junit.framework.TestCase; +import org.codehaus.plexus.redback.common.ldap.connection.LdapConnection; +import org.codehaus.plexus.redback.common.ldap.connection.LdapConnectionFactory; +import org.codehaus.plexus.redback.policy.PasswordEncoder; +import org.codehaus.plexus.redback.policy.encoders.SHA1PasswordEncoder; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.codehaus.plexus.redback.users.ldap.service.LdapCacheService; +import org.codehaus.redback.components.apacheds.ApacheDs; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import java.util.List; + + +/** + * LdapUserManagerTest + * + * @author <a href="mailto:jesse@codehaus.org">Jesse McConnell</a> + * @version $Id$ + */ + +@RunWith( SpringJUnit4ClassRunner.class ) +@ContextConfiguration( locations = { "classpath*:/META-INF/spring-context.xml", "classpath*:/spring-context.xml" } ) +public class LdapUserManagerTest + extends TestCase +{ + + protected Logger log = LoggerFactory.getLogger( getClass() ); + + @Inject + @Named(value = "userManager#ldap") + private UserManager userManager; + + @Inject + @Named( value = "apacheDS#test" ) + private ApacheDs apacheDs; + + private String suffix; + + private PasswordEncoder passwordEncoder; + + @Inject + @Named(value = "ldapConnectionFactory#configurable") + private LdapConnectionFactory connectionFactory; + + @Inject + private LdapCacheService ldapCacheService; + + public void testFoo() + throws Exception + { + + } + + @Before + public void setUp() + throws Exception + { + super.setUp(); + + passwordEncoder = new SHA1PasswordEncoder(); + + suffix = apacheDs.addSimplePartition( "test", new String[] { "redback", "plexus", "codehaus", "org" } ) + .getSuffix(); + + log.info( "DN Suffix: " + suffix ); + + apacheDs.startServer(); + + clearManyUsers(); + + makeUsers(); + + } + + @After + public void tearDown() + throws Exception + { + // clear cache + ldapCacheService.removeAllUsers(); + + InitialDirContext context = apacheDs.getAdminContext(); + + context.unbind( createDn( "jesse" ) ); + + context.unbind( createDn( "joakim" ) ); + + apacheDs.stopServer(); + + super.tearDown(); + } + + private void makeUsers() + throws Exception + { + InitialDirContext context = apacheDs.getAdminContext(); + + String cn = "jesse"; + bindUserObject( context, cn, createDn( cn ) ); + assertExist( context, createDn( cn ), "cn", cn ); + + cn = "joakim"; + bindUserObject( context, cn, createDn( cn ) ); + assertExist( context, createDn( cn ), "cn", cn ); + + } + + @Test + public void testConnection() + throws Exception + { + assertNotNull( connectionFactory ); + + LdapConnection connection = null; + try + { + connection = connectionFactory.getConnection(); + + assertNotNull( connection ); + + DirContext context = connection.getDirContext(); + + assertNotNull( context ); + } finally { + connection.close(); + } + } + + @Test + public void testDirectUsersExistence() + throws Exception + { + LdapConnection connection = null; + try + { + connection = connectionFactory.getConnection(); + + DirContext context = connection.getDirContext(); + + assertExist( context, createDn( "jesse" ), "cn", "jesse" ); + assertExist( context, createDn( "joakim" ), "cn", "joakim" ); + } finally { + connection.close(); + } + + } + + @Test + public void testUserManager() + throws Exception + { + assertNotNull( userManager ); + + //assertNull( ldapCacheService.getUser( "jesse" ) ); + + assertTrue( userManager.userExists( "jesse" ) ); + + //assertNotNull( ldapCacheService.getUser( "jesse" ) ); + + List<User> users = userManager.getUsers(); + + assertNotNull( users ); + + assertEquals( 2, users.size() ); + + User jesse = userManager.findUser( "jesse" ); + + assertNotNull( jesse ); + + assertEquals( "jesse", jesse.getPrincipal().toString() ); + assertEquals( "jesse@apache.org", jesse.getEmail() ); + assertEquals( "foo", jesse.getFullName() ); + System.out.println( "=====>"+jesse.getEncodedPassword()); + System.out.println( "=====>"+passwordEncoder.encodePassword( "foo" )); + assertTrue( passwordEncoder.isPasswordValid( jesse.getEncodedPassword(), "foo" ) ); + + } + + @Test + public void testUserNotFoundException() + throws Exception + { + try + { + userManager.findUser( "foo bar" ); + fail( "not a UserNotFoundException with an unknown user" ); + } + catch ( UserNotFoundException e ) + { + // cool it works ! + } + } + + @Test + public void testWithManyUsers() + throws Exception + { + makeManyUsers(); + + assertNotNull( userManager ); + + assertTrue( userManager.userExists( "user10" ) ); + + List<User> users = userManager.getUsers(); + + assertNotNull( users ); + + assertEquals( 10002, users.size() ); + + User user10 = userManager.findUser( "user10" ); + + assertNotNull( user10 ); + } + + private void makeManyUsers() + throws Exception + { + InitialDirContext context = apacheDs.getAdminContext(); + + for ( int i = 0 ; i < 10000 ; i++ ) + { + String cn = "user"+i; + bindUserObject( context, cn, createDn( cn ) ); + } + + } + + private void clearManyUsers() + throws Exception + { + InitialDirContext context = apacheDs.getAdminContext(); + + for ( int i = 0 ; i < 10000 ; i++ ) + { + String cn = "user"+i; + try + { + context.unbind( createDn( cn ) ); + } + catch ( NamingException e ) + { + // OK lets try with next one + } + } + + } + + private void bindUserObject( DirContext context, String cn, String dn ) + throws Exception + { + Attributes attributes = new BasicAttributes( true ); + BasicAttribute objectClass = new BasicAttribute( "objectClass" ); + objectClass.add( "top" ); + objectClass.add( "inetOrgPerson" ); + objectClass.add( "person" ); + objectClass.add( "organizationalperson" ); + attributes.put( objectClass ); + attributes.put( "cn", cn ); + attributes.put( "sn", "foo" ); + attributes.put( "mail", cn+"@apache.org" ); + attributes.put( "userPassword", passwordEncoder.encodePassword( "foo" ) ); + attributes.put( "givenName", "foo" ); + context.createSubcontext( dn, attributes ); + } + + private String createDn( String cn ) + { + return "cn=" + cn + "," + suffix; + } + + private void assertExist( DirContext context, String dn, String attribute, String value ) + throws NamingException + { + SearchControls ctls = new SearchControls(); + + ctls.setDerefLinkFlag( true ); + ctls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); + ctls.setReturningAttributes( new String[] { "*" } ); + + BasicAttributes matchingAttributes = new BasicAttributes(); + matchingAttributes.put( attribute, value ); + BasicAttribute objectClass = new BasicAttribute( "objectClass" ); + objectClass.add( "inetOrgPerson" ); + matchingAttributes.put( objectClass ); + + NamingEnumeration<SearchResult> results = context.search( suffix, matchingAttributes ); + // NamingEnumeration<SearchResult> results = context.search( suffix, "(" + attribute + "=" + value + ")", ctls + // ); + + assertTrue( results.hasMoreElements() ); + SearchResult result = results.nextElement(); + Attributes attrs = result.getAttributes(); + Attribute testAttr = attrs.get( attribute ); + assertEquals( value, testAttr.get() ); + + } + +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/codehaus/plexus/redback/users/ldap/service/LdapCacheServiceTest.java b/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/codehaus/plexus/redback/users/ldap/service/LdapCacheServiceTest.java new file mode 100644 index 000000000..00af774d1 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/test/java/org/codehaus/plexus/redback/users/ldap/service/LdapCacheServiceTest.java @@ -0,0 +1,108 @@ +package org.codehaus.plexus.redback.users.ldap.service; + +/* + * Copyright 2011 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import junit.framework.TestCase; +import org.codehaus.plexus.redback.common.ldap.LdapUser; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.inject.Inject; + +/** + * @author: Maria Odea Ching <oching@apache.org> + * @version + */ +@RunWith( SpringJUnit4ClassRunner.class ) +@ContextConfiguration( locations = { "classpath*:/META-INF/spring-context.xml", "classpath*:/spring-context.xml" } ) +public class LdapCacheServiceTest + extends TestCase +{ + @Inject + private LdapCacheService ldapCacheService; + + private static final String USERNAME = "dummy"; + + @After + public void tearDown() + throws Exception + { + ldapCacheService.removeAllUsers(); + ldapCacheService.removeAllLdapUserDn(); + + super.tearDown(); + } + + @Test + public void testLdapUserDnCache() + throws Exception + { + ldapCacheService.addLdapUserDn( USERNAME, "userDn" ); + + assertNotNull( ldapCacheService.getLdapUserDn( USERNAME ) ); + + ldapCacheService.removeLdapUserDn( USERNAME ); + + assertNull( ldapCacheService.getLdapUserDn( USERNAME ) ); + } + + @Test + public void testClearLdapUserDnCache() + throws Exception + { + ldapCacheService.addLdapUserDn( USERNAME, "userDn" ); + + assertNotNull( ldapCacheService.getLdapUserDn( USERNAME ) ); + + ldapCacheService.removeLdapUserDn( USERNAME ); + + assertNull( ldapCacheService.getLdapUserDn( USERNAME ) ); + } + + @Test + public void testLdapUsersCache() + throws Exception + { + LdapUser ldapUser = new LdapUser( USERNAME ); + + ldapCacheService.addUser( ldapUser ); + + assertNotNull( ldapCacheService.getUser( USERNAME ) ); + + ldapCacheService.removeUser( USERNAME ); + + assertNull( ldapCacheService.getUser( USERNAME ) ); + } + + @Test + public void testClearLdapUsersCache() + throws Exception + { + LdapUser ldapUser = new LdapUser( USERNAME ); + + ldapCacheService.addUser( ldapUser ); + + assertNotNull( ldapCacheService.getUser( USERNAME ) ); + + ldapCacheService.removeAllUsers(); + + assertNull( ldapCacheService.getUser( USERNAME ) ); + } +} diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/test/resources/org/codehaus/plexus/redback/users/ldap/users.ldif b/redback-users/redback-users-providers/redback-users-ldap/src/test/resources/org/codehaus/plexus/redback/users/ldap/users.ldif new file mode 100644 index 000000000..35723a4c3 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/test/resources/org/codehaus/plexus/redback/users/ldap/users.ldif @@ -0,0 +1,21 @@ +dn: dc=redback,dc=plexus,dc=codehaus,dc=org +cn: jesse +givenName: foo +userPassword: C+7Hteo/D9vJXQ3UfzxbwnXaijM= +objectClass: top +objectClass: inetorgperson +objectClass: person +objectClass: organizationalperson +email: foo +sn: foo + +dn: dc=redback,dc=plexus,dc=codehaus,dc=org +cn: joakim +givenName: foo +userPassword: C+7Hteo/D9vJXQ3UfzxbwnXaijM= +objectClass: top +objectClass: inetorgperson +objectClass: person +objectClass: organizationalperson +email: foo +sn: foo diff --git a/redback-users/redback-users-providers/redback-users-ldap/src/test/resources/spring-context.xml b/redback-users/redback-users-providers/redback-users-ldap/src/test/resources/spring-context.xml new file mode 100644 index 000000000..9eb0fb261 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-ldap/src/test/resources/spring-context.xml @@ -0,0 +1,59 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + + <context:property-placeholder system-properties-mode="OVERRIDE"/> + + <bean name="apacheDS#test" class="org.codehaus.redback.components.apacheds.DefaultApacheDs" + scope="prototype"> + <property name="basedir" value="${basedir}/target/apacheds"/> + <property name="port" value="${ldapPort}"/> + <property name="enableNetworking" value="true"/> + <property name="password" value="secret"/> + </bean> + + <bean name="ldapConnectionFactory#configurable" class="org.codehaus.plexus.redback.common.ldap.connection.ConfigurableLdapConnectionFactory"> + <property name="hostname" value="localhost"/> + <property name="port" value="${ldapPort}"/> + <property name="baseDn" value="dc=redback,dc=plexus,dc=codehaus,dc=org"/> + <property name="contextFactory" value="com.sun.jndi.ldap.LdapCtxFactory"/> + <property name="password" value="secret"/> + <property name="bindDn" value="uid=admin,ou=system"/> + <property name="userConf" ref="userConfiguration"/> + </bean> + + <bean name="userMapper#ldap" class="org.codehaus.plexus.redback.common.ldap.LdapUserMapper"> + <property name="emailAttribute" value="mail"/> + <property name="fullNameAttribute" value="givenName"/> + <property name="passwordAttribute" value="userPassword"/> + <property name="userIdAttribute" value="cn"/> + <property name="userBaseDn" value="dc=redback,dc=plexus,dc=codehaus,dc=org"/> + <property name="userObjectClass" value="inetOrgPerson"/> + <property name="userConf" ref="userConfiguration"/> + </bean> + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-memory/pom.xml b/redback-users/redback-users-providers/redback-users-memory/pom.xml new file mode 100644 index 000000000..53d045898 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-memory/pom.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2006 The Codehaus. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-providers</artifactId> + <version>1.5-SNAPSHOT</version> + </parent> + <artifactId>redback-users-memory</artifactId> + <name>Redback :: Users Provider :: Memory</name> + + <dependencies> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-policy</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>jsr250-api</artifactId> + </dependency> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users-tests</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/MemoryUserManager.java b/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/MemoryUserManager.java new file mode 100644 index 000000000..111272166 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/MemoryUserManager.java @@ -0,0 +1,332 @@ +package org.codehaus.plexus.redback.users.memory; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.policy.UserSecurityPolicy; +import org.codehaus.plexus.redback.users.AbstractUserManager; +import org.codehaus.plexus.redback.users.PermanentUserException; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserQuery; +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.codehaus.plexus.redback.users.memory.util.UserSorter; +import org.apache.commons.lang.StringUtils; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.annotation.Resource; + +/** + * @version $Id$ + */ +@Service("userManager#memory") +public class MemoryUserManager + extends AbstractUserManager + implements UserManager +{ + @Resource + private UserSecurityPolicy userSecurityPolicy; + + public String getId() + { + Properties props = new Properties(); + URL url = this + .getClass() + .getResource( + "META-INF/maven/org/codehaus/plexus/redback/redback-users-memory/pom.properties" ); + + if ( url != null ) + { + try + { + props.load( url.openStream() ); + return "MemoryUserManager - " + props.getProperty( "version" ); + } + catch ( IOException e ) + { + // Fall thru + } + } + return "MemoryUserManager - (unknown version)"; + } + + public boolean isReadOnly() + { + return false; + } + + public UserQuery createUserQuery() + { + return new SimpleUserQuery(); + } + + public List<User> findUsersByQuery( UserQuery query ) + { + SimpleUserQuery uq = (SimpleUserQuery) query; + + List<User> list = new ArrayList<User>(); + + for ( Iterator<User> i = users.values().iterator(); i.hasNext(); ) + { + SimpleUser user = (SimpleUser) i.next(); + boolean matches = uq.matches( user ); + if ( matches ) + { + list.add( user ); + } + } + + Collections.sort( list, uq.getComparator() ); + + List<User> cutList = new ArrayList<User>(); + + for ( long i = query.getFirstResult(); + i < list.size() && ( query.getMaxResults() == -1 || i < query.getFirstResult() + uq.getMaxResults() ); + i++ ) + { + cutList.add( list.get( (int) i ) ); + } + return cutList; + } + + private Map<Object, User> users = new HashMap<Object, User>(); + + public User addUser( User user ) + { + saveUser( user ); + fireUserManagerUserAdded( user ); + + // If there exists no encoded password, then this is a new user setup + if ( StringUtils.isEmpty( user.getEncodedPassword() ) ) + { + userSecurityPolicy.extensionChangePassword( user ); + } + + return user; + } + + private void saveUser( User user ) + { + triggerInit(); + users.put( user.getPrincipal(), user ); + } + + public User updateUser( User user ) + { + return updateUser( user, false ); + } + + public User updateUser( User user, boolean passwordChangeRequired ) + { + if ( StringUtils.isNotEmpty( user.getPassword() ) ) + { + userSecurityPolicy.extensionChangePassword( user, passwordChangeRequired ); + } + + saveUser( user ); + + fireUserManagerUserUpdated( user ); + + return user; + } + + public User findUser( Object principal ) + throws UserNotFoundException + { + triggerInit(); + User user = users.get( principal ); + + if ( user == null ) + { + throw new UserNotFoundException( "Cannot find the user with the principal '" + principal + "'." ); + } + + return user; + } + + public boolean userExists( Object principal ) + { + try + { + findUser( principal ); + return true; + } + catch ( UserNotFoundException ne ) + { + return false; + } + } + + public void deleteUser( Object principal ) + throws UserNotFoundException + { + deleteUser( principal.toString() ); + } + + public User createUser( String username, String fullName, String emailAddress ) + { + User user = new SimpleUser(); + user.setUsername( username ); + user.setFullName( fullName ); + user.setEmail( emailAddress ); + + return user; + } + + public void deleteUser( String username ) + throws UserNotFoundException + { + User user = findUser( username ); + + if ( user.isPermanent() ) + { + throw new PermanentUserException( "Cannot delete permanent user." ); + } + + users.remove( user.getPrincipal() ); + + fireUserManagerUserRemoved( user ); + } + + public void addUserUnchecked( User user ) + { + addUser( user ); + } + + public void eraseDatabase() + { + users.clear(); + } + + public User findUser( String username ) + throws UserNotFoundException + { + triggerInit(); + User user = null; + + Iterator<User> it = users.values().iterator(); + while ( it.hasNext() ) + { + User u = it.next(); + if ( u.getUsername().equals( username ) ) + { + user = u; + } + } + + if ( user == null ) + { + throw new UserNotFoundException( "Unable to find user '" + username + "'" ); + } + + return user; + } + + public List<User> findUsersByUsernameKey( String usernameKey, boolean orderAscending ) + { + triggerInit(); + + List<User> userList = new ArrayList<User>(); + + Iterator<User> it = users.values().iterator(); + while ( it.hasNext() ) + { + User u = it.next(); + if ( u.getUsername().indexOf( usernameKey ) > -1 ) + { + userList.add( u ); + } + } + + Collections.sort( userList, new UserSorter( orderAscending ) ); + + return userList; + } + + public List<User> findUsersByFullNameKey( String fullNameKey, boolean orderAscending ) + { + triggerInit(); + + List<User> userList = new ArrayList<User>(); + + Iterator<User> it = users.values().iterator(); + while ( it.hasNext() ) + { + User u = it.next(); + if ( u.getFullName().indexOf( fullNameKey ) > -1 ) + { + userList.add( u ); + } + } + + Collections.sort( userList, new UserSorter( orderAscending ) ); + + return userList; + } + + public List<User> findUsersByEmailKey( String emailKey, boolean orderAscending ) + { + triggerInit(); + + List<User> userList = new ArrayList<User>(); + + Iterator<User> it = users.values().iterator(); + while ( it.hasNext() ) + { + User u = it.next(); + if ( u.getEmail().indexOf( emailKey ) > -1 ) + { + userList.add( u ); + } + } + + Collections.sort( userList, new UserSorter( orderAscending ) ); + + return userList; + } + + public List<User> getUsers() + { + triggerInit(); + return new ArrayList<User>( users.values() ); + } + + public List<User> getUsers( boolean ascendingUsername ) + { + return getUsers(); + } + + private boolean hasTriggeredInit = false; + + public void triggerInit() + { + if ( !hasTriggeredInit ) + { + fireUserManagerInit( users.isEmpty() ); + hasTriggeredInit = true; + } + } +} diff --git a/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/SimpleUser.java b/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/SimpleUser.java new file mode 100644 index 000000000..d473700ba --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/SimpleUser.java @@ -0,0 +1,220 @@ +package org.codehaus.plexus.redback.users.memory; + +/* + * Copyright 2001-2006 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.users.User; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * A Simple User record. + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + */ +public class SimpleUser + implements User, Serializable +{ + private String username; + + private String password; + + private String email; + + private String fullName; + + private String encodedPassword; + + private Date lastPasswordChange; + + private Date lastLoginDate; + + private int countFailedLoginAttempts = 0; + + private boolean locked = false; + + private boolean permanent = false; + + private boolean validated = false; + + private List<String> previousEncodedPasswords; + + private Date accountCreationDate; + + private boolean passwordChangeRequired; + + public SimpleUser() + { + // no op + } + + public void addPreviousEncodedPassword( String encodedPassword ) + { + getPreviousEncodedPasswords().add( encodedPassword ); + } + + public Date getAccountCreationDate() + { + return accountCreationDate; + } + + public int getCountFailedLoginAttempts() + { + return countFailedLoginAttempts; + } + + public String getEmail() + { + return email; + } + + public String getEncodedPassword() + { + return encodedPassword; + } + + public String getFullName() + { + return fullName; + } + + public Date getLastLoginDate() + { + return lastLoginDate; + } + + public Date getLastPasswordChange() + { + return lastPasswordChange; + } + + public String getPassword() + { + return password; + } + + public List<String> getPreviousEncodedPasswords() + { + if ( previousEncodedPasswords == null ) + { + previousEncodedPasswords = new ArrayList<String>(); + } + return previousEncodedPasswords; + } + + public Object getPrincipal() + { + return username; + } + + public String getUsername() + { + return username; + } + + public boolean isLocked() + { + return locked; + } + + public void setAccountCreationDate( Date accountCreationDate ) + { + this.accountCreationDate = accountCreationDate; + } + + public void setCountFailedLoginAttempts( int countFailedLoginAttempts ) + { + this.countFailedLoginAttempts = countFailedLoginAttempts; + } + + public void setEmail( String email ) + { + this.email = email; + } + + public void setEncodedPassword( String encodedPassword ) + { + this.encodedPassword = encodedPassword; + } + + public void setFullName( String fullName ) + { + this.fullName = fullName; + } + + public void setLastLoginDate( Date lastLoginDate ) + { + this.lastLoginDate = lastLoginDate; + } + + public void setLastPasswordChange( Date lastPasswordChange ) + { + this.lastPasswordChange = lastPasswordChange; + } + + public void setLocked( boolean locked ) + { + this.locked = locked; + } + + public void setPassword( String password ) + { + this.password = password; + } + + public void setPreviousEncodedPasswords( List<String> previousEncodedPasswords ) + { + this.previousEncodedPasswords = previousEncodedPasswords; + } + + public void setUsername( String username ) + { + this.username = username; + } + + public boolean isPasswordChangeRequired() + { + return passwordChangeRequired; + } + + public void setPasswordChangeRequired( boolean passwordChangeRequired ) + { + this.passwordChangeRequired = passwordChangeRequired; + } + + public boolean isPermanent() + { + return permanent; + } + + public void setPermanent( boolean permanent ) + { + this.permanent = permanent; + } + + public boolean isValidated() + { + return validated; + } + + public void setValidated( boolean validated ) + { + this.validated = validated; + } +} diff --git a/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/SimpleUserQuery.java b/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/SimpleUserQuery.java new file mode 100644 index 000000000..2891b858c --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/SimpleUserQuery.java @@ -0,0 +1,91 @@ +package org.codehaus.plexus.redback.users.memory; + +/* + * Copyright 2001-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.users.AbstractUserQuery; +import org.codehaus.plexus.redback.users.User; + +import java.util.Comparator; + +public class SimpleUserQuery + extends AbstractUserQuery +{ + + /** + * Returns true if this user should be considered a match of the current query + * + * @param user + * @return + */ + public boolean matches( User user ) + { + if ( getUsername() != null && user.getUsername() != null && + user.getUsername().toLowerCase().indexOf( getUsername().toLowerCase() ) == -1 ) + { + return false; + } + else if ( getFullName() != null && user.getFullName() != null && + user.getFullName().toLowerCase().indexOf( getFullName().toLowerCase() ) == -1 ) + { + return false; + } + else if ( getEmail() != null && user.getEmail() != null && + user.getEmail().toLowerCase().indexOf( getEmail().toLowerCase() ) == -1 ) + { + return false; + } + else + { + return true; + } + + } + + /** + * Returns a comparator used for sorting a collection of User objects based on the ordering set + * on this UserQuery's {@link #setOrderBy(String)} and {@link #setAscending(boolean)}. + * @return + */ + public Comparator<User> getComparator() + { + return new Comparator<User>() + { + public int compare( User user1, User user2 ) + { + return ( isAscending() ? 1 : -1 ) * compareUsers( user1, user2 ); + } + }; + } + + private int compareUsers( User user, User user1 ) + { + if ( ORDER_BY_EMAIL.equals( getOrderBy() ) ) + { + return user.getEmail() == null ? -1 + : user1.getEmail() == null ? 1 : user.getEmail().compareTo( user1.getEmail() ); + } + else if ( ORDER_BY_FULLNAME.equals( getOrderBy() ) ) + { + return user.getFullName() == null ? -1 + : user1.getFullName() == null ? 1 : user.getFullName().compareTo( user1.getFullName() ); + } + else + { + return user.getUsername().compareTo( user1.getUsername() ); + } + } +} diff --git a/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/util/UserSorter.java b/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/util/UserSorter.java new file mode 100644 index 000000000..e1183bac8 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-memory/src/main/java/org/codehaus/plexus/redback/users/memory/util/UserSorter.java @@ -0,0 +1,69 @@ +package org.codehaus.plexus.redback.users.memory.util; + +import org.codehaus.plexus.redback.users.User; + +import java.util.Comparator; + +/** + * UserSorter + */ +public class UserSorter + implements Comparator<User> +{ + private boolean ascending; + + public UserSorter() + { + this.ascending = true; + } + + public UserSorter( boolean ascending ) + { + this.ascending = ascending; + } + + public int compare( User o1, User o2 ) + { + if ( ( o1 == null ) && ( o2 == null ) ) + { + return 0; + } + + if ( ( o1 == null ) && ( o2 != null ) ) + { + return -1; + } + + if ( ( o1 != null ) && ( o2 != null ) ) + { + return 1; + } + + User u1 = null; + User u2 = null; + + if ( isAscending() ) + { + u1 = o1; + u2 = o2; + } + else + { + u1 = o2; + u2 = o1; + } + + return u1.getUsername().compareTo( u2.getUsername() ); + } + + public boolean isAscending() + { + return ascending; + } + + public void setAscending( boolean ascending ) + { + this.ascending = ascending; + } + +} diff --git a/redback-users/redback-users-providers/redback-users-memory/src/main/resources/META-INF/spring-context.xml b/redback-users/redback-users-providers/redback-users-memory/src/main/resources/META-INF/spring-context.xml new file mode 100644 index 000000000..9694a8829 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-memory/src/main/resources/META-INF/spring-context.xml @@ -0,0 +1,33 @@ +<?xml version="1.0"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd" + default-lazy-init="true"> + + <context:annotation-config /> + <context:component-scan base-package="org.codehaus.plexus.redback.users.memory"/> + +</beans>
\ No newline at end of file diff --git a/redback-users/redback-users-providers/redback-users-memory/src/test/java/org/codehaus/plexus/redback/users/MemoryUserManagerTest.java b/redback-users/redback-users-providers/redback-users-memory/src/test/java/org/codehaus/plexus/redback/users/MemoryUserManagerTest.java new file mode 100644 index 000000000..7679b8287 --- /dev/null +++ b/redback-users/redback-users-providers/redback-users-memory/src/test/java/org/codehaus/plexus/redback/users/MemoryUserManagerTest.java @@ -0,0 +1,45 @@ +package org.codehaus.plexus.redback.users; + +/* + * Copyright 2005 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.users.memory.MemoryUserManager; +import org.codehaus.plexus.redback.users.provider.test.AbstractUserManagerTestCase; +import org.junit.Before; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * {@link MemoryUserManager} test: + * + * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a> + */ +public class MemoryUserManagerTest + extends AbstractUserManagerTestCase +{ + + @Inject @Named(value = "userManager#memory") + UserManager userManager; + + @Before + public void setUp() + throws Exception + { + super.setUp(); + setUserManager( userManager ); + } +} diff --git a/redback-users/redback-users-tests/pom.xml b/redback-users/redback-users-tests/pom.xml new file mode 100644 index 000000000..4ed0ffc21 --- /dev/null +++ b/redback-users/redback-users-tests/pom.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2006 The Codehaus. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-users</artifactId> + <version>1.5-SNAPSHOT</version> + </parent> + <artifactId>redback-users-tests</artifactId> + <name>Redback :: User Tests</name> + + <dependencies> + <dependency> + <groupId>org.codehaus.redback</groupId> + <artifactId>redback-policy</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-utils</artifactId> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + </dependency> + </dependencies> + +</project> diff --git a/redback-users/redback-users-tests/src/main/java/org/codehaus/plexus/redback/users/provider/test/AbstractUserManagerTestCase.java b/redback-users/redback-users-tests/src/main/java/org/codehaus/plexus/redback/users/provider/test/AbstractUserManagerTestCase.java new file mode 100644 index 000000000..faf5c50aa --- /dev/null +++ b/redback-users/redback-users-tests/src/main/java/org/codehaus/plexus/redback/users/provider/test/AbstractUserManagerTestCase.java @@ -0,0 +1,609 @@ +package org.codehaus.plexus.redback.users.provider.test; + +/* + * Copyright 2001-2006 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import junit.framework.TestCase; +import org.codehaus.plexus.redback.policy.UserSecurityPolicy; +import org.codehaus.plexus.redback.users.PermanentUserException; +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserManager; +import org.codehaus.plexus.redback.users.UserNotFoundException; +import org.codehaus.plexus.redback.users.UserQuery; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.inject.Inject; +import java.util.List; + +/** + * AbstractUserManagerTestCase + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +@RunWith( SpringJUnit4ClassRunner.class ) +@ContextConfiguration( locations = {"classpath*:/META-INF/spring-context.xml","classpath*:/spring-context.xml"} ) +public class AbstractUserManagerTestCase + extends TestCase +{ + /** + * This value is set by the sub classes of this test case. + * They should override .setUp() and inject this value via + * the {@link #setUserManager(UserManager)} method call. + */ + private UserManager userManager; + + @Inject + private UserSecurityPolicy securityPolicy; + + private UserManagerEventTracker eventTracker; + + public UserManager getUserManager() + { + return userManager; + } + + public void setUserManager( UserManager um ) + { + this.userManager = um; + if ( this.userManager != null ) + { + this.eventTracker = new UserManagerEventTracker(); + this.userManager.addUserManagerListener( this.eventTracker ); + } + } + + public void setUp() + throws Exception + { + super.setUp(); + } + + public void tearDown() + throws Exception + { + super.tearDown(); + } + + protected void assertCleanUserManager() + { + + getUserManager().eraseDatabase(); + getEventTracker().userManagerInit( true ); + assertNotNull( getUserManager() ); + + assertEquals( "New UserManager should contain no users. " + userManager.getUsers(), 0, userManager.getUsers().size() ); + } + + @Test + public void testFindUserByNullPrincipal() + { + try + { + Object obj = null; + getUserManager().findUser( obj ); + fail( "findUser() with null Object Should have thrown a UserNotFoundException." ); + } + catch ( UserNotFoundException e ) + { + // Expected Path. + } + } + + @Test + public void testFindUserByEmptyUsername() + { + try + { + String username = null; + getUserManager().findUser( username ); + fail( "findUser() with null username Should have thrown a UserNotFoundException." ); + } + catch ( UserNotFoundException e ) + { + // Expected Path. + } + + try + { + String username = ""; + getUserManager().findUser( username ); + fail( "findUser() with empty username Should have thrown a UserNotFoundException." ); + } + catch ( UserNotFoundException e ) + { + // Expected Path. + } + + try + { + String username = " "; + getUserManager().findUser( username ); + fail( "findUser() with all whitespace username Should have thrown a UserNotFoundException." ); + } + catch ( UserNotFoundException e ) + { + // Expected Path. + } + } + + @Test + public void testAddFindUserByPrincipal() + throws UserNotFoundException + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + User smcqueen = getUserManager().createUser( "smcqueen", "Steve McQueen", "the cooler king" ); + + /* Keep a reference to the object that was added. + * Since it has the actual principal that was managed by jpox/jdo. + */ + User added = userManager.addUser( smcqueen ); + + assertEquals( 1, userManager.getUsers().size() ); + + /* Fetch user from userManager using principal returned earlier */ + User actual = userManager.findUser( added.getPrincipal() ); + assertEquals( added, actual ); + + /* Check into the event tracker. */ + assertEquals( 1, getEventTracker().countInit ); + assertNotNull( getEventTracker().lastDbFreshness ); + assertTrue( getEventTracker().lastDbFreshness.booleanValue() ); + + assertEquals( 1, getEventTracker().addedUsernames.size() ); + assertEquals( 0, getEventTracker().removedUsernames.size() ); + assertEquals( 0, getEventTracker().updatedUsernames.size() ); + } + + @Test + public void testAddFindUserLockedStatus() throws UserNotFoundException { + assertCleanUserManager(); + securityPolicy.setEnabled(false); + + User smcqueen = getUserManager().createUser("smcqueen", "Steve McQueen", "the cooler king"); + + smcqueen.setLocked( true ); + + /* + * Keep a reference to the object that was added. Since it has the + * actual principal that was managed by jpox/jdo. + */ + User added = userManager.addUser( smcqueen ); + + assertTrue( added.isLocked() ); + + assertEquals(1, userManager.getUsers().size()); + + /* Fetch user from userManager using principal returned earlier */ + User actual = userManager.findUser(added.getPrincipal()); + assertEquals(added, actual); + + assertTrue( actual.isLocked() ); + + /* Check into the event tracker. */ + assertEquals(1, getEventTracker().countInit); + assertNotNull(getEventTracker().lastDbFreshness); + assertTrue(getEventTracker().lastDbFreshness.booleanValue()); + + assertEquals(1, getEventTracker().addedUsernames.size()); + assertEquals(0, getEventTracker().removedUsernames.size()); + assertEquals(0, getEventTracker().updatedUsernames.size()); + } + + @Test + public void testAddFindUserByUsername() + throws UserNotFoundException + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + User smcqueen = getUserManager().createUser( "smcqueen", "Steve McQueen", "the cooler king" ); + + User added = userManager.addUser( smcqueen ); + + assertEquals( 1, userManager.getUsers().size() ); + + User actual = userManager.findUser( "smcqueen" ); + assertEquals( added, actual ); + + /* Check into the event tracker. */ + assertEquals( 1, getEventTracker().countInit ); + assertNotNull( getEventTracker().lastDbFreshness ); + assertTrue( getEventTracker().lastDbFreshness.booleanValue() ); + + assertEquals( 1, getEventTracker().addedUsernames.size() ); + assertEquals( 0, getEventTracker().removedUsernames.size() ); + assertEquals( 0, getEventTracker().updatedUsernames.size() ); + } + + @Test + public void testCreateUser() + throws Exception + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + UserManager um = getUserManager(); + User user = um.createUser( "admin", "Administrator", "admin@somedomain.com" ); + user.setPassword( "adminpass" ); + um.addUser( user ); + + assertEquals( 1, um.getUsers().size() ); + + /* Check into the event tracker. */ + assertEquals( 1, getEventTracker().countInit ); + assertNotNull( getEventTracker().lastDbFreshness ); + assertTrue( getEventTracker().lastDbFreshness.booleanValue() ); + + assertEquals( 1, getEventTracker().addedUsernames.size() ); + assertEquals( 0, getEventTracker().removedUsernames.size() ); + assertEquals( 0, getEventTracker().updatedUsernames.size() ); + } + + @Test + public void testAddUser() + throws Exception + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + UserManager um = getUserManager(); + assertNotNull( um.getUsers() ); + assertEquals( 0, um.getUsers().size() ); + + User user = um.createUser( "tommy123", "Tommy Traddles", "tommy.traddles@somedomain.com" ); + user.setPassword( "hillybilly" ); + um.addUser( user ); + + assertNotNull( um.getUsers() ); + assertEquals( 1, um.getUsers().size() ); + + /* Check into the event tracker. */ + assertEquals( 1, getEventTracker().countInit ); + assertNotNull( getEventTracker().lastDbFreshness ); + assertTrue( getEventTracker().lastDbFreshness.booleanValue() ); + + assertEquals( 1, getEventTracker().addedUsernames.size() ); + assertEquals( 0, getEventTracker().removedUsernames.size() ); + assertEquals( 0, getEventTracker().updatedUsernames.size() ); + } + + @Test + public void testDeleteUser() + throws Exception + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + UserManager um = getUserManager(); + User user = um.createUser( "admin", "Administrator", "admin@somedomain.com" ); + user.setPassword( "adminpass" ); + um.addUser( user ); + + assertEquals( 1, um.getUsers().size() ); + + um.deleteUser( user.getPrincipal() ); + assertEquals( 0, um.getUsers().size() ); + + // attempt finding a non-existent user + try + { + um.findUser( "admin" ); + fail( "Expected UserNotFoundException!" ); + } + catch ( UserNotFoundException e ) + { + // do nothing, expected! + } + + /* Check into the event tracker. */ + assertEquals( 1, getEventTracker().countInit ); + assertNotNull( getEventTracker().lastDbFreshness ); + assertTrue( getEventTracker().lastDbFreshness.booleanValue() ); + + assertEquals( 1, getEventTracker().addedUsernames.size() ); + assertEquals( 1, getEventTracker().removedUsernames.size() ); + assertEquals( 0, getEventTracker().updatedUsernames.size() ); + } + + @Test + public void testFindUser() + throws Exception + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + UserManager um = getUserManager(); + + // create and add a few users + User u1 = um.createUser( "admin", "Administrator", "admin@somedomain.com" ); + u1.setPassword( "adminpass" ); + um.addUser( u1 ); + + u1 = um.createUser( "administrator", "Administrator User", "administrator@somedomain.com" ); + u1.setPassword( "password" ); + um.addUser( u1 ); + + u1 = um.createUser( "root", "Root User", "root@somedomain.com" ); + u1.setPassword( "rootpass" ); + um.addUser( u1 ); + + assertEquals( 3, um.getUsers().size() ); + + // find an existing user + User user = um.findUser( "root" ); + assertNotNull( user ); + assertEquals( "root@somedomain.com", user.getEmail() ); + assertEquals( "root", user.getPrincipal() ); + assertEquals( "Root User", user.getFullName() ); + // test if the plain string password is encoded and NULL'ified + assertNull( user.getPassword() ); + // test if encoded password was as expected + assertTrue( securityPolicy.getPasswordEncoder().isPasswordValid( user.getEncodedPassword(), "rootpass" ) ); + + // attempt finding a non-existent user + try + { + um.findUser( "non-existent" ); + fail( "Expected UserNotFoundException!" ); + } + catch ( UserNotFoundException e ) + { + // do nothing, expected! + } + + /* Check into the event tracker. */ + assertEquals( 1, getEventTracker().countInit ); + assertNotNull( getEventTracker().lastDbFreshness ); + assertTrue( getEventTracker().lastDbFreshness.booleanValue() ); + + assertEquals( 3, getEventTracker().addedUsernames.size() ); + assertEquals( 0, getEventTracker().removedUsernames.size() ); + assertEquals( 0, getEventTracker().updatedUsernames.size() ); + } + + @Test + public void testFindUsersByQuery() + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + UserManager um = getUserManager(); + + // create and add a few users + User u1 = um.createUser( "admin", "Administrator", "admin@somedomain.com" ); + u1.setPassword( "adminpass" ); + um.addUser( u1 ); + + u1 = um.createUser( "administrator", "Administrator User", "administrator@somedomain.com" ); + u1.setPassword( "password" ); + um.addUser( u1 ); + + u1 = um.createUser( "root", "Root User", "root@somedomain.com" ); + u1.setPassword( "rootpass" ); + um.addUser( u1 ); + + assertEquals( 3, um.getUsers().size() ); + + // Query by username + UserQuery query = um.createUserQuery(); + query.setUsername( "Admin" ); + assertEquals( 2, um.findUsersByQuery( query ).size() ); + + // Query by full name + query = um.createUserQuery(); + query.setFullName( "Admin" ); + assertEquals( 2, um.findUsersByQuery( query ).size() ); + + query = um.createUserQuery(); + query.setFullName( "Administrator" ); + assertEquals( 2, um.findUsersByQuery( query ).size() ); + + query = um.createUserQuery(); + query.setFullName( "r User" ); + assertEquals( 1, um.findUsersByQuery( query ).size() ); + + // Query by user name + query = um.createUserQuery(); + query.setEmail( "somedomain" ); + assertEquals( 3, um.findUsersByQuery( query ).size() ); + + // Query by email + query = um.createUserQuery(); + query.setEmail( "root@" ); + assertEquals( 1, um.findUsersByQuery( query ).size() ); + + // Query by illegal property + query = um.createUserQuery(); + try + { + query.setOrderBy( "unknownString" ); + fail( "Expected IllegalArgumentException" ); + } + catch ( IllegalArgumentException e ) + { + + } + + // Query with default ordering ascending + query = um.createUserQuery(); + query.setOrderBy( UserQuery.ORDER_BY_EMAIL ); + List<User> users = um.findUsersByQuery( query ); + assertEquals( 3, users.size() ); + assertEquals( "admin@somedomain.com", ( (User) users.get( 0 ) ).getEmail() ); + + // Query with ordering ascending + query = um.createUserQuery(); + query.setOrderBy( UserQuery.ORDER_BY_EMAIL ); + query.setAscending( false ); + users = um.findUsersByQuery( query ); + assertEquals( 3, users.size() ); + assertEquals( "root@somedomain.com", ( (User) users.get( 0 ) ).getEmail() ); + + // Query with ordering descending, max 2 results + query = um.createUserQuery(); + query.setOrderBy( UserQuery.ORDER_BY_EMAIL ); + query.setAscending( false ); + query.setMaxResults( 2 ); + users = um.findUsersByQuery( query ); + assertEquals( 2, users.size() ); + assertEquals( "root@somedomain.com", ( (User) users.get( 0 ) ).getEmail() ); + + // Query with ordering ascending, max 2 results, first result = 2 so only one result + query = um.createUserQuery(); + query.setOrderBy( UserQuery.ORDER_BY_EMAIL ); + query.setAscending( false ); + query.setMaxResults( 2 ); + query.setFirstResult( 2 ); + users = um.findUsersByQuery( query ); + assertEquals( 1, users.size() ); + assertEquals( "admin@somedomain.com", ( (User) users.get( 0 ) ).getEmail() ); + + // Query on more than one field + query = um.createUserQuery(); + query.setOrderBy( UserQuery.ORDER_BY_EMAIL ); + query.setFullName( "admin" ); + query.setEmail( "admin@" ); + users = um.findUsersByQuery( query ); + assertEquals( 1, users.size() ); + assertEquals( "admin@somedomain.com", ( (User) users.get( 0 ) ).getEmail() ); + } + + @Test + public void testUserExists() + throws Exception + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + UserManager um = getUserManager(); + + // create and add a few users + User u1 = um.createUser( "admin", "Administrator", "admin@somedomain.com" ); + u1.setPassword( "adminpass" ); + um.addUser( u1 ); + + assertTrue( um.userExists( "admin" ) ); + assertFalse( um.userExists( "voodoohatrack" ) ); + + /* Check into the event tracker. */ + assertEquals( 1, getEventTracker().countInit ); + assertNotNull( getEventTracker().lastDbFreshness ); + assertTrue( getEventTracker().lastDbFreshness.booleanValue() ); + + assertEquals( 1, getEventTracker().addedUsernames.size() ); + assertEquals( 0, getEventTracker().removedUsernames.size() ); + assertEquals( 0, getEventTracker().updatedUsernames.size() ); + } + + @Test + public void testUpdateUser() + throws Exception + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + UserManager um = getUserManager(); + + // create and add a user + User u1 = um.createUser( "root", "Root User", "root@somedomain.com" ); + u1.setPassword( "rootpass" ); + u1 = um.addUser( u1 ); + + // find user + User user = um.findUser( "root" ); + assertNotNull( user ); + assertEquals( u1, user ); + + user.setEmail( "superuser@somedomain.com" ); + user.setPassword( "superpass" ); + user.setFullName( "Super User" ); + + um.updateUser( user ); + + // find updated user + user = um.findUser( "root" ); + assertNotNull( user ); + assertEquals( "superuser@somedomain.com", user.getEmail() ); + assertEquals( "Super User", user.getFullName() ); + assertTrue( securityPolicy.getPasswordEncoder().isPasswordValid( user.getEncodedPassword(), "superpass" ) ); + + /* Check into the event tracker. */ + assertEquals( 1, getEventTracker().countInit ); + assertNotNull( getEventTracker().lastDbFreshness ); + assertTrue( getEventTracker().lastDbFreshness.booleanValue() ); + + assertEquals( 1, getEventTracker().addedUsernames.size() ); + assertEquals( 0, getEventTracker().removedUsernames.size() ); + assertEquals( 1, getEventTracker().updatedUsernames.size() ); + } + + @Test + public void testDeletePermanentUser() + throws UserNotFoundException + { + assertCleanUserManager(); + securityPolicy.setEnabled( false ); + + UserManager um = getUserManager(); + User user = um.createUser( "admin", "Administrator", "admin@somedomain.com" ); + user.setPassword( "adminpass" ); + user.setPermanent( true ); + user = um.addUser( user ); + + assertEquals( 1, um.getUsers().size() ); + + try + { + um.deleteUser( user.getPrincipal() ); + fail("Deleting permanent user shold throw PermanentUserException."); + } catch( PermanentUserException e ) + { + // do nothing, expected route. + } + + assertEquals( 1, um.getUsers().size() ); + + // attempt to finding user + User admin = um.findUser( "admin" ); + assertNotNull( admin ); + assertEquals( user.getEmail(), admin.getEmail() ); + assertEquals( user.getFullName(), admin.getFullName() ); + + /* Check into the event tracker. */ + assertEquals( 1, getEventTracker().countInit ); + assertNotNull( getEventTracker().lastDbFreshness ); + assertTrue( getEventTracker().lastDbFreshness.booleanValue() ); + + assertEquals( 1, getEventTracker().addedUsernames.size() ); + assertEquals( 0, getEventTracker().removedUsernames.size() ); + assertEquals( 0, getEventTracker().updatedUsernames.size() ); } + + public UserManagerEventTracker getEventTracker() + { + return eventTracker; + } + + public void setEventTracker( UserManagerEventTracker eventTracker ) + { + this.eventTracker = eventTracker; + } +} diff --git a/redback-users/redback-users-tests/src/main/java/org/codehaus/plexus/redback/users/provider/test/UserManagerEventTracker.java b/redback-users/redback-users-tests/src/main/java/org/codehaus/plexus/redback/users/provider/test/UserManagerEventTracker.java new file mode 100644 index 000000000..66367c980 --- /dev/null +++ b/redback-users/redback-users-tests/src/main/java/org/codehaus/plexus/redback/users/provider/test/UserManagerEventTracker.java @@ -0,0 +1,72 @@ +package org.codehaus.plexus.redback.users.provider.test; + +/* + * Copyright 2001-2006 The Codehaus. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +import org.codehaus.plexus.redback.users.User; +import org.codehaus.plexus.redback.users.UserManagerListener; + +import java.util.ArrayList; +import java.util.List; + +/** + * UserManagerEventTracker + * + * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public class UserManagerEventTracker + implements UserManagerListener +{ + public long countInit = 0; + + public Boolean lastDbFreshness; + + public List<String> addedUsernames = new ArrayList<String>(); + + public List<String> removedUsernames = new ArrayList<String>(); + + public List<String> updatedUsernames = new ArrayList<String>(); + + public void userManagerInit( boolean freshDatabase ) + { + countInit++; + lastDbFreshness = Boolean.valueOf( freshDatabase ); + } + + private void addUniqueUsername( List<String> list, User user ) + { + if ( !list.contains( user.getUsername() ) ) + { + list.add( user.getUsername() ); + } + } + + public void userManagerUserAdded( User user ) + { + addUniqueUsername( addedUsernames, user ); + } + + public void userManagerUserRemoved( User user ) + { + addUniqueUsername( removedUsernames, user ); + } + + public void userManagerUserUpdated( User user ) + { + addUniqueUsername( updatedUsernames, user ); + } +} |