@Test public void shouldBuildImmutableUser() { LegacyCredential abc = LegacyCredential.forPassword( "123abc" ); LegacyCredential fruit = LegacyCredential.forPassword( "fruit" ); User u1 = new User.Builder( "Steve", abc ).build(); User u2 = new User.Builder( "Steve", fruit ) .withRequiredPasswordChange( true ) .withFlag( "nice_guy" ).build(); assertThat( u1, equalTo( u1 ) ); assertThat( u1, not( equalTo( u2 ) ) ); User u1AsU2 = u1.augment().withCredentials( fruit ) .withRequiredPasswordChange( true ) .withFlag( "nice_guy" ).build(); assertThat( u1, not( equalTo( u1AsU2 ))); assertThat( u2, equalTo( u1AsU2 )); User u2AsU1 = u2.augment().withCredentials( abc ) .withRequiredPasswordChange( false ) .withoutFlag( "nice_guy" ).build(); assertThat( u2, not( equalTo( u2AsU1 ))); assertThat( u1, equalTo( u2AsU1 )); assertThat( u1, not( equalTo( u2 ) ) ); } }
@Before public void setup() throws IOException, InvalidArgumentsException { OutsideWorld mock = mock( OutsideWorld.class ); when( mock.fileSystem() ).thenReturn( fileSystem ); setDefaultAdmin = new SetDefaultAdminCommand( testDir.directory( "home" ).toPath(), testDir.directory( "conf" ).toPath(), mock ); config = setDefaultAdmin.loadNeo4jConfig(); UserRepository users = CommunitySecurityModule.getUserRepository( config, NullLogProvider.getInstance(), fileSystem ); users.create( new User.Builder( "jake", LegacyCredential.forPassword( "123" ) ) .withRequiredPasswordChange( false ) .build() ); adminIniFile = new File( CommunitySecurityModule.getUserRepositoryFile( config ).getParentFile(), "admin.ini" ); }
@Test public void shouldStoreAndRetrieveUsersByName() throws Exception { // Given FileUserRepository users = new FileUserRepository( fs, authFile, logProvider ); User user = new User.Builder( "jake", LegacyCredential.INACCESSIBLE ).withRequiredPasswordChange( true ).build(); users.create( user ); // When User result = users.getUserByName( user.name() ); // Then assertThat( result, equalTo( user ) ); }
@Override public void setUserPassword( String username, String password, boolean requirePasswordChange ) throws IOException, InvalidArgumentsException { User existingUser = getUser( username ); passwordPolicy.validatePassword( password ); if ( existingUser.credentials().matchesPassword( password ) ) { throw new InvalidArgumentsException( "Old password and new password cannot be the same." ); } try { User updatedUser = existingUser.augment() .withCredentials( Credential.forPassword( password ) ) .withRequiredPasswordChange( requirePasswordChange ) .build(); synchronized ( this ) { userRepository.update( existingUser, updatedUser ); } } catch ( ConcurrentModificationException e ) { // try again setUserPassword( username, password, requirePasswordChange ); } clearCacheForUser( username ); }
private void setPassword( String password ) throws Throwable { Config config = loadNeo4jConfig(); FileSystemAbstraction fileSystem = outsideWorld.fileSystem(); if ( realUsersExist( config ) ) { File authFile = CommunitySecurityModule.getUserRepositoryFile( config ); throw new CommandFailed( realUsersExistErrorMsg( fileSystem, authFile ) ); } else { File file = CommunitySecurityModule.getInitialUserRepositoryFile( config ); if ( fileSystem.fileExists( file ) ) { fileSystem.deleteFile( file ); } FileUserRepository userRepository = new FileUserRepository( fileSystem, file, NullLogProvider.getInstance() ); userRepository.start(); userRepository.create( new User.Builder( INITIAL_USER_NAME, LegacyCredential.forPassword( UTF8.encode( password ) ) ) .withRequiredPasswordChange( false ) .build() ); userRepository.shutdown(); outsideWorld.stdOutLine( "Changed password for user '" + INITIAL_USER_NAME + "'." ); } }
@Override public User newUser( String username, byte[] initialPassword, boolean requirePasswordChange ) throws IOException, InvalidArgumentsException { try { userRepository.assertValidUsername( username ); passwordPolicy.validatePassword( initialPassword ); User user = new User.Builder() .withName( username ) .withCredentials( LegacyCredential.forPassword( initialPassword ) ) .withRequiredPasswordChange( requirePasswordChange ) .build(); userRepository.create( user ); return user; } finally { // Clear password if ( initialPassword != null ) { Arrays.fill( initialPassword, (byte) 0 ); } } }
@Override public void activateUser( String username, boolean requirePasswordChange ) throws IOException, InvalidArgumentsException { User user = getUser( username ); if ( user.hasFlag( IS_SUSPENDED ) ) { User activatedUser = user.augment() .withoutFlag( IS_SUSPENDED ) .withRequiredPasswordChange( requirePasswordChange ) .build(); try { synchronized ( this ) { userRepository.update( user, activatedUser ); } } catch ( ConcurrentModificationException e ) { // Try again activateUser( username, requirePasswordChange ); } } clearCacheForUser( username ); }
@Override public User newUser( String username, String initialPassword, boolean requirePasswordChange ) throws IOException, InvalidArgumentsException { userRepository.assertValidUsername( username ); passwordPolicy.validatePassword( initialPassword ); User user = new User.Builder() .withName( username ) .withCredentials( Credential.forPassword( initialPassword ) ) .withRequiredPasswordChange( requirePasswordChange ) .build(); synchronized ( this ) { userRepository.create( user ); } return user; }
User user = new User.Builder( "jake", LegacyCredential.INACCESSIBLE ).withRequiredPasswordChange( true ).build();
private void setPassword( String password ) throws Throwable { Config config = loadNeo4jConfig(); FileSystemAbstraction fileSystem = outsideWorld.fileSystem(); if ( realUsersExist( config ) ) { File authFile = CommunitySecurityModule.getUserRepositoryFile( config ); throw new CommandFailed( realUsersExistErrorMsg( fileSystem, authFile ) ); } else { File file = CommunitySecurityModule.getInitialUserRepositoryFile( config ); if ( fileSystem.fileExists( file ) ) { fileSystem.deleteFile( file ); } FileUserRepository userRepository = new FileUserRepository( fileSystem, file, NullLogProvider.getInstance() ); userRepository.start(); userRepository.create( new User.Builder( INITIAL_USER_NAME, LegacyCredential.forPassword( UTF8.encode( password ) ) ) .withRequiredPasswordChange( false ) .build() ); userRepository.shutdown(); outsideWorld.stdOutLine( "Changed password for user '" + INITIAL_USER_NAME + "'." ); } }
User updatedUser = existingUser.augment().withCredentials( LegacyCredential.forPassword( password ) ).withRequiredPasswordChange( requirePasswordChange ).build(); userRepository.update( existingUser, updatedUser );
@Override public User newUser( String username, byte[] initialPassword, boolean requirePasswordChange ) throws IOException, InvalidArgumentsException { try { userRepository.assertValidUsername( username ); passwordPolicy.validatePassword( initialPassword ); User user = new User.Builder() .withName( username ) .withCredentials( LegacyCredential.forPassword( initialPassword ) ) .withRequiredPasswordChange( requirePasswordChange ) .build(); userRepository.create( user ); return user; } finally { // Clear password if ( initialPassword != null ) { Arrays.fill( initialPassword, (byte) 0 ); } } }
/** * This is a future-proofing test. If you come here because you've made changes to the serialization format, * this is your reminder to make sure to build this is in a backwards compatible way. */ @Test public void shouldReadV1SerializationFormat() throws Exception { // Given UserSerialization serialization = new UserSerialization(); byte[] salt1 = new byte[] { (byte) 0xa5, (byte) 0x43 }; byte[] hash1 = new byte[] { (byte) 0xfe, (byte) 0x00, (byte) 0x56, (byte) 0xc3, (byte) 0x7e }; byte[] salt2 = new byte[] { (byte) 0x34, (byte) 0xa4 }; byte[] hash2 = new byte[] { (byte) 0x0e, (byte) 0x1f, (byte) 0xff, (byte) 0xc2, (byte) 0x3e }; // When List<User> deserialized = serialization.deserializeRecords( UTF8.encode( "Mike:SHA-256,FE0056C37E,A543:\n" + "Steve:SHA-256,FE0056C37E,A543:nice_guy,password_change_required\n" + "Bob:SHA-256,0E1FFFC23E,34A4:password_change_required\n" ) ); // Then assertThat( deserialized, equalTo( asList( new User.Builder( "Mike", new LegacyCredential( salt1, hash1 ) ).build(), new User.Builder( "Steve", new LegacyCredential( salt1, hash1 ) ) .withRequiredPasswordChange( true ).withFlag("nice_guy").build(), new User.Builder( "Bob", new LegacyCredential( salt2, hash2 ) ) .withRequiredPasswordChange( true ) .build() ) ) ); } }
@Test public void shouldThrowIfExistingUserDoesNotMatch() throws Throwable { // Given FileUserRepository users = new FileUserRepository( fs, authFile, logProvider ); User user = new User.Builder( "jake", LegacyCredential.INACCESSIBLE ).withRequiredPasswordChange( true ).build(); users.create( user ); User modifiedUser = user.augment().withCredentials( LegacyCredential.forPassword( "foo" ) ).build(); // When User updatedUser = user.augment().withCredentials( LegacyCredential.forPassword( "bar" ) ).build(); try { users.update( modifiedUser, updatedUser ); fail( "expected exception not thrown" ); } catch ( ConcurrentModificationException e ) { // Then continue } }
@Test public void shouldLoadInitialUserIfNoneExist() throws Throwable { // Given FileUserRepository initialUserRepository = CommunitySecurityModule.getInitialUserRepository( config, NullLogProvider.getInstance(), fsRule.get() ); initialUserRepository.start(); initialUserRepository.create( new User.Builder( "neo4j", LegacyCredential.forPassword( "123" ) ) .withRequiredPasswordChange( false ) .build() ); initialUserRepository.shutdown(); // When authManager().start(); // Then final User user = users.getUserByName( "neo4j" ); assertNotNull( user ); assertTrue( user.credentials().matchesPassword( "123" ) ); assertFalse( user.passwordChangeRequired() ); }
@Test public void shouldThrowIfUpdateChangesName() throws Throwable { // Given FileUserRepository users = new FileUserRepository( fs, authFile, logProvider ); User user = new User.Builder( "jake", LegacyCredential.INACCESSIBLE ).withRequiredPasswordChange( true ).build(); users.create( user ); // When User updatedUser = new User.Builder( "john", LegacyCredential.INACCESSIBLE ).withRequiredPasswordChange( true ) .build(); try { users.update( user, updatedUser ); fail( "expected exception not thrown" ); } catch ( IllegalArgumentException e ) { // Then continue } assertThat( users.getUserByName( user.name() ), equalTo( user ) ); }
@Test public void shouldLoadInitialUserIfNoneExistEvenWithSamePassword() throws Throwable { // Given FileUserRepository initialUserRepository = CommunitySecurityModule.getInitialUserRepository( config, NullLogProvider.getInstance(), fsRule.get() ); initialUserRepository.start(); initialUserRepository.create( new User.Builder( "neo4j", LegacyCredential.forPassword( "neo4j" ) ) .withRequiredPasswordChange( false ) .build() ); initialUserRepository.shutdown(); // When authManager().start(); // Then final User user = users.getUserByName( "neo4j" ); assertNotNull( user ); assertTrue( user.credentials().matchesPassword( "neo4j" ) ); assertFalse( user.passwordChangeRequired() ); }
@Test public void shouldPersistUsers() throws Throwable { // Given FileUserRepository users = new FileUserRepository( fs, authFile, logProvider ); User user = new User.Builder( "jake", LegacyCredential.INACCESSIBLE ).withRequiredPasswordChange( true ).build(); users.create( user ); users = new FileUserRepository( fs, authFile, logProvider ); users.start(); // When User resultByName = users.getUserByName( user.name() ); // Then assertThat( resultByName, equalTo( user ) ); }
@Test public void shouldNotFindUserAfterDelete() throws Throwable { // Given FileUserRepository users = new FileUserRepository( fs, authFile, logProvider ); User user = new User.Builder( "jake", LegacyCredential.INACCESSIBLE ).withRequiredPasswordChange( true ).build(); users.create( user ); // When users.delete( user ); // Then assertThat( users.getUserByName( user.name() ), nullValue() ); }
protected User newUser( String userName, String password, boolean pwdChange ) { return new User.Builder( userName, LegacyCredential.forPassword( password ) ) .withRequiredPasswordChange( pwdChange ) .build(); } }