Skip to content

Commit

Permalink
Extracted serialization and saving
Browse files Browse the repository at this point in the history
  • Loading branch information
OliviaYtterbrink committed Jul 5, 2016
1 parent b4a04d4 commit cb297a0
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 200 deletions.
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.server.security.auth;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.neo4j.server.security.auth.exception.FormatException;
import org.neo4j.string.UTF8;

import static java.nio.file.StandardCopyOption.ATOMIC_MOVE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

public abstract class FileRepositorySerializer<S>
{
public void saveRecordsToFile(Path recordsFile, Collection<S> records) throws IOException
{
Path directory = recordsFile.getParent();
if ( !Files.exists( directory ) )
{
Files.createDirectories( directory );
}

Path tempFile = Files.createTempFile( directory, recordsFile.getFileName().toString() + "-", ".tmp" );
try
{
Files.write( tempFile, serialize( records ) );
Files.move( tempFile, recordsFile, ATOMIC_MOVE, REPLACE_EXISTING );
}
catch ( Throwable e )
{
Files.delete( tempFile );
throw e;
}
}

public List<S> loadRecordsFromFile(Path recordsFile) throws IOException, FormatException
{
byte[] fileBytes = Files.readAllBytes( recordsFile );
return deserializeRecords( fileBytes );
}

public byte[] serialize(Collection<S> records)
{
StringBuilder sb = new StringBuilder();
for ( S record : records )
{
sb.append( serialize( record ) ).append( "\n" );
}
return UTF8.encode( sb.toString() );
}

public List<S> deserializeRecords( byte[] bytes ) throws FormatException
{
List<S> out = new ArrayList<>( );
int lineNumber = 1;
for ( String line : UTF8.decode( bytes ).split( "\n" ) )
{
if ( line.trim().length() > 0 )
{
out.add( deserializeRecord( line, lineNumber ) );
}
lineNumber++;
}
return out;
}

protected abstract String serialize( S record );

protected abstract S deserializeRecord( String line, int lineNumber ) throws FormatException;
}
Expand Up @@ -26,6 +26,7 @@

import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider;
import org.neo4j.server.security.auth.exception.FormatException;

import static java.nio.file.StandardCopyOption.ATOMIC_MOVE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
Expand Down Expand Up @@ -55,53 +56,27 @@ public void start() throws Throwable
{
if ( Files.exists( authFile ) )
{
loadUsersFromFile();
List<User> loadedUsers;
try
{
loadedUsers = serialization.loadRecordsFromFile( authFile );
} catch ( FormatException e )
{
log.error( "Failed to read authentication file \"%s\" (%s)", authFile.toAbsolutePath(), e.getMessage() );
throw new IllegalStateException( "Failed to read authentication file: " + authFile );
}

users = loadedUsers;
for ( User user : users )
{
usersByName.put( user.name(), user );
}
}
}

@Override
protected void saveUsers() throws IOException
{
saveUsersToFile();
}

private void saveUsersToFile() throws IOException
{
Path directory = authFile.getParent();
if ( !Files.exists( directory ) )
{
Files.createDirectories( directory );
}

Path tempFile = Files.createTempFile( directory, authFile.getFileName().toString() + "-", ".tmp" );
try
{
Files.write( tempFile, serialization.serialize( users ) );
Files.move( tempFile, authFile, ATOMIC_MOVE, REPLACE_EXISTING );
} catch ( Throwable e )
{
Files.delete( tempFile );
throw e;
}
}

private void loadUsersFromFile() throws IOException
{
byte[] fileBytes = Files.readAllBytes( authFile );
List<User> loadedUsers;
try
{
loadedUsers = serialization.deserializeUsers( fileBytes );
} catch ( UserSerialization.FormatException e )
{
log.error( "Failed to read authentication file \"%s\" (%s)", authFile.toAbsolutePath(), e.getMessage() );
throw new IllegalStateException( "Failed to read authentication file: " + authFile );
}

users = loadedUsers;
for ( User user : users )
{
usersByName.put( user.name(), user );
}
serialization.saveRecordsToFile(authFile, users);
}
}
Expand Up @@ -23,6 +23,7 @@
import java.util.Collection;
import java.util.List;

import org.neo4j.server.security.auth.exception.FormatException;
import org.neo4j.string.HexString;
import org.neo4j.string.UTF8;

Expand All @@ -31,45 +32,13 @@
/**
* Serializes user authorization and authentication data to a format similar to unix passwd files.
*/
public class UserSerialization
public class UserSerialization extends FileRepositorySerializer<User>
{
public class FormatException extends Exception
{
FormatException( String message )
{
super( message );
}
}

private static final String userSeparator = ":";
private static final String credentialSeparator = ",";

public byte[] serialize(Collection<User> users)
{
StringBuilder sb = new StringBuilder();
for ( User user : users )
{
sb.append( serialize(user) ).append( "\n" );
}
return UTF8.encode( sb.toString() );
}

public List<User> deserializeUsers( byte[] bytes ) throws FormatException
{
List<User> out = new ArrayList<>();
int lineNumber = 1;
for ( String line : UTF8.decode( bytes ).split( "\n" ) )
{
if (line.trim().length() > 0)
{
out.add( deserializeUser( line, lineNumber ) );
}
lineNumber++;
}
return out;
}

private String serialize( User user )
@Override
protected String serialize( User user )
{
return String.join( userSeparator,
user.name(),
Expand All @@ -78,7 +47,8 @@ private String serialize( User user )
);
}

private User deserializeUser( String line, int lineNumber ) throws FormatException
@Override
protected User deserializeRecord( String line, int lineNumber ) throws FormatException
{
String[] parts = line.split( userSeparator, -1 );
if ( parts.length != 3 )
Expand Down
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.server.security.auth.exception;

public class FormatException extends Exception
{
public FormatException( String message )
{
super( message );
}
}
Expand Up @@ -47,7 +47,7 @@ public void shouldSerializeAndDeserialize() throws Exception
byte[] serialized = serialization.serialize( users );

// Then
assertThat( serialization.deserializeUsers( serialized ), equalTo( users ) );
assertThat( serialization.deserializeRecords( serialized ), equalTo( users ) );
}

/**
Expand All @@ -65,7 +65,7 @@ public void shouldReadV1SerializationFormat() throws Exception
byte[] hash2 = new byte[] { (byte) 0x0e, (byte) 0x1f, (byte) 0xff, (byte) 0xc2, (byte) 0x3e };

// When
List<User> deserialized = serialization.deserializeUsers( UTF8.encode(
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") ) );
Expand Down
Expand Up @@ -27,6 +27,7 @@

import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider;
import org.neo4j.server.security.auth.exception.FormatException;

import static java.nio.file.StandardCopyOption.ATOMIC_MOVE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
Expand All @@ -37,8 +38,6 @@
*/
public class FileRoleRepository extends AbstractRoleRepository
{
// TODO: Extract shared code with FileUserRepository

private final Path roleFile;

private final Log log;
Expand All @@ -56,57 +55,30 @@ public void start() throws Throwable
{
if ( Files.exists( roleFile ) )
{
loadRolesFromFile();
}
}

@Override
protected void saveRoles() throws IOException
{
saveRolesToFile();
}
List<RoleRecord> loadedRoles;
try
{
loadedRoles = serialization.loadRecordsFromFile( roleFile );
}
catch ( FormatException e )
{
log.error( "Failed to read role file \"%s\" (%s)", roleFile.toAbsolutePath(), e.getMessage() );
throw new IllegalStateException( "Failed to read role file: " + roleFile );
}

private void saveRolesToFile() throws IOException
{
Path directory = roleFile.getParent();
if ( !Files.exists( directory ) )
{
Files.createDirectories( directory );
}
roles = loadedRoles;
for ( RoleRecord role : roles )
{
rolesByName.put( role.name(), role );

Path tempFile = Files.createTempFile( directory, roleFile.getFileName().toString() + "-", ".tmp" );
try
{
Files.write( tempFile, serialization.serialize( roles ) );
Files.move( tempFile, roleFile, ATOMIC_MOVE, REPLACE_EXISTING );
}
catch ( Throwable e )
{
Files.delete( tempFile );
throw e;
populateUserMap( role );
}
}
}

private void loadRolesFromFile() throws IOException
@Override
protected void saveRoles() throws IOException
{
byte[] fileBytes = Files.readAllBytes( roleFile );
List<RoleRecord> loadedRoles;
try
{
loadedRoles = serialization.deserializeRoles( fileBytes );
}
catch ( RoleSerialization.FormatException e )
{
log.error( "Failed to read role file \"%s\" (%s)", roleFile.toAbsolutePath(), e.getMessage() );
throw new IllegalStateException( "Failed to read role file: " + roleFile );
}

roles = loadedRoles;
for ( RoleRecord role : roles )
{
rolesByName.put( role.name(), role );

populateUserMap( role );
}
serialization.saveRecordsToFile( roleFile, roles );
}
}

0 comments on commit cb297a0

Please sign in to comment.