diff --git a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/ShiroAuthManager.java b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/ShiroAuthManager.java index 45c68c08ed0e4..29f262b9699b8 100644 --- a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/ShiroAuthManager.java +++ b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/ShiroAuthManager.java @@ -25,6 +25,8 @@ import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.subject.Subject; import java.io.IOException; @@ -130,7 +132,8 @@ public AuthSubject login( String username, String password ) { assertAuthEnabled(); - Subject subject = new Subject.Builder( securityManager ).buildSubject(); + // Start with an anonymous subject + Subject subject = buildSubject( null ); UsernamePasswordToken token = new UsernamePasswordToken( username, password ); AuthenticationResult result = AuthenticationResult.SUCCESS; @@ -148,6 +151,10 @@ public AuthSubject login( String username, String password ) catch ( ExpiredCredentialsException e ) { result = AuthenticationResult.PASSWORD_CHANGE_REQUIRED; + + // We have to build an identity with the given username to allow the user to change password + // At this point we know that the username is valid + subject = buildSubject( username ); } catch ( AuthenticationException e ) { @@ -195,4 +202,16 @@ public boolean deleteUser( String username ) throws IOException return realm.deleteUser( username ); } + private Subject buildSubject( String username ) + { + Subject.Builder subjectBuilder = new Subject.Builder( securityManager ); + + if ( username != null ) + { + PrincipalCollection identity = new SimplePrincipalCollection( username, realm.getName() ); + subjectBuilder = subjectBuilder.principals( identity ); + } + + return subjectBuilder.buildSubject(); + } } diff --git a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/ShiroAuthSubject.java b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/ShiroAuthSubject.java index f78575780e2b9..4d6e890dcf9be 100644 --- a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/ShiroAuthSubject.java +++ b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/ShiroAuthSubject.java @@ -74,7 +74,8 @@ public void setPassword( String password ) throws IOException, IllegalCredential public boolean doesUsernameMatch( String username ) { - return subject.getPrincipal().equals( username ); + Object principal = subject.getPrincipal(); + return principal != null && username.equals( principal ); } @Override diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ShiroAuthManagerTest.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ShiroAuthManagerTest.java index a843a91e50e1f..f29c0c8ee15aa 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ShiroAuthManagerTest.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ShiroAuthManagerTest.java @@ -214,6 +214,30 @@ public void shouldSetPassword() throws Throwable assertThat( users.findByName( "jake" ), equalTo( user ) ); } + @Test + public void shouldSetPasswordThroughAuthSubject() throws Throwable + { + // Given + users.create( new User( "neo", Credential.forPassword( "abc123" ), true ) ); + manager.start(); + when( authStrategy.isAuthenticationPermitted( "neo" )).thenReturn( true ); + + // When + AuthSubject authSubject = manager.login( "neo", "abc123" ); + assertThat( authSubject.getAuthenticationResult(), equalTo( AuthenticationResult.PASSWORD_CHANGE_REQUIRED ) ); + + authSubject.setPassword( "hello, world!" ); + + // Then + User user = manager.getUser( "neo" ); + assertTrue( user.credentials().matchesPassword( "hello, world!" ) ); + assertThat( users.findByName( "neo" ), equalTo( user ) ); + + authSubject.logout(); + authSubject = manager.login( "neo", "hello, world!" ); + assertThat( authSubject.getAuthenticationResult(), equalTo( AuthenticationResult.SUCCESS ) ); + } + @Test public void shouldReturnNullWhenSettingPasswordForUnknownUser() throws Throwable {