Skip to content

Commit

Permalink
Augmented suspendUser, deleteUser and changeUserPassword auth procs t…
Browse files Browse the repository at this point in the history
…o terminate active user sessions and transactions
  • Loading branch information
fickludd authored and henriknyman committed Jul 14, 2016
1 parent 7ee8425 commit b37ea6b
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 23 deletions.
Expand Up @@ -81,6 +81,8 @@ else if ( !enterpriseSubject.isAdmin() )
else
{
enterpriseSubject.getUserManager().setUserPassword( username, newPassword );
terminateTransactionsForValidUser( username );
terminateSessionsForValidUser( username );
}
}

Expand Down Expand Up @@ -113,6 +115,8 @@ public void deleteUser( @Name( "username" ) String username ) throws InvalidArgu
throw new InvalidArgumentsException( "Deleting yourself is not allowed!" );
}
adminSubject.getUserManager().deleteUser( username );
terminateTransactionsForValidUser( username );
terminateSessionsForValidUser( username );
}

@Procedure( name = "dbms.suspendUser", mode = DBMS )
Expand All @@ -124,6 +128,8 @@ public void suspendUser( @Name( "username" ) String username ) throws IOExceptio
throw new InvalidArgumentsException( "Suspending yourself is not allowed!" );
}
adminSubject.getUserManager().suspendUser( username );
terminateTransactionsForValidUser( username );
terminateSessionsForValidUser( username );
}

@Procedure( name = "dbms.activateUser", mode = DBMS )
Expand Down Expand Up @@ -209,16 +215,7 @@ public Stream<TransactionTerminationResult> terminateTransactionsForUser( @Name(
{
ensureSelfOrAdminAuthSubject( username );

Long killCount = 0L;
for ( KernelTransaction tx : getActiveTransactions() )
{
if ( tx.mode().name().equals( username ) && tx != this.tx )
{
tx.markForTermination( Status.Transaction.Terminated );
killCount += 1;
}
}
return Stream.of( new TransactionTerminationResult( username, killCount ) );
return terminateTransactionsForValidUser( username );
}

@Procedure( name = "dbms.listSessions", mode = DBMS )
Expand Down Expand Up @@ -246,6 +243,27 @@ public Stream<SessionResult> terminateSessionsForUser( @Name( "username" ) Strin

subject.getUserManager().getUser( username );

return terminateSessionsForValidUser( username );
}

// ----------------- helpers ---------------------

private Stream<TransactionTerminationResult> terminateTransactionsForValidUser( String username )
{
Long killCount = 0L;
for ( KernelTransaction tx : getActiveTransactions() )
{
if ( tx.mode().name().equals( username ) && tx != this.tx )
{
tx.markForTermination( Status.Transaction.Terminated );
killCount += 1;
}
}
return Stream.of( new TransactionTerminationResult( username, killCount ) );
}

private Stream<SessionResult> terminateSessionsForValidUser( String username )
{
Long killCount = 0L;
for ( HaltableUserSession session : getSessionTracker().getActiveSessions() )
{
Expand All @@ -259,8 +277,6 @@ public Stream<SessionResult> terminateSessionsForUser( @Name( "username" ) Strin
return Stream.of( new SessionResult( username, killCount ) );
}

// ----------------- helpers ---------------------

private Set<KernelTransaction> getActiveTransactions()
{
return graph.getDependencyResolver().resolveDependency( KernelTransactions.class ).activeTransactions();
Expand Down
Expand Up @@ -26,12 +26,21 @@
import java.util.concurrent.ExecutionException;

import org.neo4j.kernel.api.security.exception.InvalidArgumentsException;
import org.neo4j.bolt.v1.transport.integration.TransportTestUtil;
import org.neo4j.bolt.v1.transport.socket.client.Connection;
import org.neo4j.bolt.v1.transport.socket.client.SocketConnection;
import org.neo4j.helpers.HostnamePort;
import org.neo4j.test.rule.concurrent.ThreadingRule;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import static org.neo4j.bolt.v1.messaging.message.Messages.init;
import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess;
import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyRecieves;
import static org.neo4j.helpers.collection.MapUtil.map;
import static org.neo4j.kernel.api.security.AuthenticationResult.FAILURE;
import static org.neo4j.kernel.api.security.AuthenticationResult.PASSWORD_CHANGE_REQUIRED;
Expand All @@ -46,6 +55,7 @@ public abstract class AuthProceduresTestLogic<S> extends AuthTestBase<S>
{
@Rule
public final ThreadingRule threading = new ThreadingRule();

//---------- Change own password -----------

// Enterprise version of test in BuiltInProceduresIT.callChangePasswordWithAccessModeInDbmsMode.
Expand Down Expand Up @@ -316,6 +326,18 @@ public void shouldNotChangeUserPasswordIfSamePassword() throws Exception
"Old password and new password cannot be the same" );
}

@Test
public void shouldTerminateTransactionsOnChangeUserPassword() throws Throwable
{
shouldTerminateTransactionsForUser( writeSubject, "dbms.changeUserPassword( '%s', 'newPassword' )" );
}

@Test
public void shouldTerminateSessionsOnChangeUserPassword() throws Exception
{
shouldTerminateSessionsForUser( "writeSubject", "abc", "dbms.changeUserPassword( '%s', 'newPassword' )" );
}

//---------- create user -----------

@Test
Expand Down Expand Up @@ -415,6 +437,18 @@ public void shouldNotAllowDeletingYourself() throws Exception
testFailDeleteUser( adminSubject, "adminSubject", "Deleting yourself is not allowed" );
}

@Test
public void shouldTerminateTransactionsOnUserDeletion() throws Throwable
{
shouldTerminateTransactionsForUser( writeSubject, "dbms.deleteUser( '%s' )" );
}

@Test
public void shouldTerminateSessionsOnUserDeletion() throws Exception
{
shouldTerminateSessionsForUser( "writeSubject", "abc", "dbms.deleteUser( '%s' )" );
}

//---------- suspend user -----------

@Test
Expand Down Expand Up @@ -452,6 +486,18 @@ public void shouldFailToSuspendYourself() throws Exception
assertFail( adminSubject, "CALL dbms.suspendUser('adminSubject')", "Suspending yourself is not allowed" );
}

@Test
public void shouldTerminateTransactionsOnUserSuspension() throws Throwable
{
shouldTerminateTransactionsForUser( writeSubject, "dbms.suspendUser( '%s' )" );
}

@Test
public void shouldTerminateSessionsOnUserSuspension() throws Exception
{
shouldTerminateSessionsForUser( "writeSubject", "abc", "dbms.suspendUser( '%s' )" );
}

//---------- activate user -----------

@Test
Expand Down Expand Up @@ -875,4 +921,49 @@ public void shouldSetCorrectMultiRolePermissions() throws Exception
testFailCreateUser( schemaSubject, PERMISSION_DENIED );
assertEmpty( schemaSubject, "CALL dbms.changePassword( '321' )" );
}

// --------------------- helpers -----------------------

private void shouldTerminateTransactionsForUser( S subject, String procedure ) throws Throwable
{
ThreadedTransactionCreate<S> userThread = new ThreadedTransactionCreate<>( neo );
userThread.execute( threading, subject );
userThread.barrier.await();

assertEmpty( adminSubject, "CALL " + String.format(procedure, neo.nameOf( subject ) ) );

assertSuccess( adminSubject, "CALL dbms.listTransactions()",
r -> assertKeyIsMap( r, "username", "activeTransactions", map( "adminSubject", "1" ) ) );

userThread.closeAndAssertTransactionTermination();

assertEmpty( adminSubject, "MATCH (n:Test) RETURN n.name AS name" );
}

private void shouldTerminateSessionsForUser( String username, String password, String procedure ) throws Exception
{
Connection boltConnection = new SocketConnection();
HostnamePort boltAddress = new HostnamePort( "localhost:7687" );
authenticate( boltConnection, boltAddress, username, password );

assertEmpty( adminSubject, "CALL " + String.format(procedure, username ) );

assertEmpty( adminSubject, "CALL dbms.listSessions()" );
}

private void authenticate( Connection client, HostnamePort address, String username, String password )
throws Exception
{
Map<String, Object> authToken =
map( "principal", username, "credentials", password, "scheme", "basic" );

client.connect( address )
.send( TransportTestUtil.acceptedVersions( 1, 0, 0, 0 ) )
.send( TransportTestUtil.chunk(
init( "TestClient/1.1", authToken ) ) );

assertThat( client, eventuallyRecieves( new byte[]{0, 0, 0, 1} ) );
assertThat( client, eventuallyRecieves( msgSuccess() ) );
}

}
Expand Up @@ -52,6 +52,7 @@ abstract class AuthTestBase<S>
protected String READ_OPS_NOT_ALLOWED = "Read operations are not allowed";
protected String WRITE_OPS_NOT_ALLOWED = "Write operations are not allowed";
protected String SCHEMA_OPS_NOT_ALLOWED = "Schema operations are not allowed";

protected boolean IS_EMBEDDED = true;

protected String pwdReqErrMsg( String errMsg )
Expand Down
Expand Up @@ -19,6 +19,9 @@
*/
package org.neo4j.server.security.enterprise.auth;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.function.Consumer;

Expand Down Expand Up @@ -54,4 +57,11 @@ String executeQuery( S subject, String call, Map<String,Object> params,
String nameOf( S subject );

void tearDown() throws Throwable;

static String tempPath(String prefix, String suffix ) throws IOException
{
Path path = Files.createTempFile( prefix, suffix );
Files.delete( path );
return path.toString();
}
}
Expand Up @@ -19,25 +19,29 @@
*/
package org.neo4j.server.security.enterprise.auth;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

import org.neo4j.bolt.BoltKernelExtension;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.config.Setting;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.kernel.api.KernelTransaction;
import org.neo4j.kernel.api.security.AuthenticationResult;
import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.kernel.impl.factory.GraphDatabaseFacade;
import org.neo4j.server.security.auth.BasicPasswordPolicy;
import org.neo4j.server.security.auth.InMemoryUserRepository;
import org.neo4j.server.security.auth.RateLimitedAuthenticationStrategy;
import org.neo4j.test.TestEnterpriseGraphDatabaseFactory;

import static org.neo4j.graphdb.factory.GraphDatabaseSettings.BoltConnector.EncryptionLevel.OPTIONAL;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.boltConnector;
import static org.neo4j.server.security.auth.SecurityTestUtils.authToken;

import static java.time.Clock.systemUTC;

class NeoShallowEmbeddedInteraction implements NeoInteractionLevel<EnterpriseAuthSubject>
{
private GraphDatabaseFacade db;
Expand All @@ -46,11 +50,22 @@ class NeoShallowEmbeddedInteraction implements NeoInteractionLevel<EnterpriseAut

NeoShallowEmbeddedInteraction() throws Throwable
{
db = (GraphDatabaseFacade) new TestEnterpriseGraphDatabaseFactory().newImpermanentDatabase();
InternalFlatFileRealm internalRealm =
new InternalFlatFileRealm( new InMemoryUserRepository(), new InMemoryRoleRepository(),
new BasicPasswordPolicy(), new RateLimitedAuthenticationStrategy( systemUTC(), 3 ) );
manager = new MultiRealmAuthManager( internalRealm, Collections.singletonList( internalRealm ) );
Path IMPERMANENT_DB_ROLES_PATH = Paths.get( "target/test-data/impermanent-db/data/dbms/roles" );
if ( Files.exists( IMPERMANENT_DB_ROLES_PATH ) )
{
Files.delete( IMPERMANENT_DB_ROLES_PATH );
}

Map<Setting<?>, String> settings = new HashMap<>();
settings.put( boltConnector( "0" ).enabled, "true" );
settings.put( boltConnector( "0" ).encryption_level, OPTIONAL.name() );
settings.put( BoltKernelExtension.Settings.tls_key_file, NeoInteractionLevel.tempPath( "key", ".key" ) );
settings.put( BoltKernelExtension.Settings.tls_certificate_file, NeoInteractionLevel.tempPath( "cert", ".cert" ) );
settings.put( GraphDatabaseSettings.auth_enabled, "true" );
settings.put( GraphDatabaseSettings.auth_manager, "enterprise-auth-manager" );

db = (GraphDatabaseFacade) new TestEnterpriseGraphDatabaseFactory().newImpermanentDatabase( settings );
manager = db.getDependencyResolver().resolveDependency( MultiRealmAuthManager.class );
manager.init();
manager.start();
userManager = manager.getUserManager();
Expand Down Expand Up @@ -124,8 +139,8 @@ public String nameOf( EnterpriseAuthSubject subject )
@Override
public void tearDown() throws Throwable
{
db.shutdown();
manager.stop();
manager.shutdown();
db.shutdown();
}
}
7 changes: 7 additions & 0 deletions enterprise/server-enterprise/pom.xml
Expand Up @@ -143,6 +143,13 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-bolt</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.neo4j.app</groupId>
Expand Down
Expand Up @@ -33,6 +33,7 @@

import javax.ws.rs.core.HttpHeaders;

import org.neo4j.bolt.BoltKernelExtension;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.kernel.api.KernelTransaction;
Expand All @@ -49,6 +50,8 @@
import org.neo4j.test.server.HTTP;

import static org.junit.Assert.fail;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.BoltConnector.EncryptionLevel.OPTIONAL;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.boltConnector;
import static org.neo4j.kernel.api.security.AuthToken.newBasicAuthToken;

public class NeoFullRESTInteraction extends CommunityServerTestBase implements NeoInteractionLevel<RESTSubject>
Expand All @@ -61,6 +64,12 @@ public class NeoFullRESTInteraction extends CommunityServerTestBase implements N
public NeoFullRESTInteraction() throws IOException
{
server = EnterpriseServerBuilder.server()
.withProperty( boltConnector( "0" ).enabled.name(), "true" )
.withProperty( boltConnector( "0" ).encryption_level.name(), OPTIONAL.name() )
.withProperty( BoltKernelExtension.Settings.tls_key_file.name(),
NeoInteractionLevel.tempPath( "key", ".key" ) )
.withProperty( BoltKernelExtension.Settings.tls_certificate_file.name(),
NeoInteractionLevel.tempPath( "cert", ".cert" ) )
.withProperty( GraphDatabaseSettings.auth_enabled.name(), Boolean.toString( true ) )
.withProperty( GraphDatabaseSettings.auth_manager.name(), "enterprise-auth-manager" )
.build();
Expand Down

0 comments on commit b37ea6b

Please sign in to comment.