Skip to content

Commit

Permalink
Snapshot access mode on transaction start
Browse files Browse the repository at this point in the history
A snapshot of the access mode is taken on begin transaction, which will keep
the authorization state at that point active for the life time of the transaction.

This fixes the issue that authorization can expire during long running
transactions with LDAP authorization
(with `dbms.security.realms.ldap.authorization.use_system_account=false`) or
plugin authorization using the simplified `AuthPlugin` interface.
  • Loading branch information
henriknyman committed Oct 12, 2016
1 parent 944fcba commit 65bfbf3
Show file tree
Hide file tree
Showing 14 changed files with 420 additions and 138 deletions.
Expand Up @@ -59,6 +59,12 @@ public AuthorizationViolationException onViolation( String msg )
{
return new AuthorizationViolationException( msg );
}

@Override
public AccessMode getSnapshot()
{
return NONE;
}
},

/** No reading or writing allowed because of expired credentials. */
Expand Down Expand Up @@ -104,6 +110,12 @@ public AuthorizationViolationException onViolation( String msg )
"session, and then restart your driver with the new password configured." ),
Status.Security.CredentialsExpired );
}

@Override
public AccessMode getSnapshot()
{
return CREDENTIALS_EXPIRED;
}
},

/** Allows reading data and schema, but not writing. */
Expand Down Expand Up @@ -138,6 +150,12 @@ public AuthorizationViolationException onViolation( String msg )
{
return new AuthorizationViolationException( msg );
}

@Override
public AccessMode getSnapshot()
{
return READ;
}
},

/** Allows writing data */
Expand Down Expand Up @@ -172,6 +190,12 @@ public AuthorizationViolationException onViolation( String msg )
{
return new AuthorizationViolationException( msg );
}

@Override
public AccessMode getSnapshot()
{
return WRITE_ONLY;
}
},

/** Allows reading and writing data, but not schema. */
Expand Down Expand Up @@ -206,6 +230,12 @@ public AuthorizationViolationException onViolation( String msg )
{
return new AuthorizationViolationException( msg );
}

@Override
public AccessMode getSnapshot()
{
return WRITE;
}
},

/** Allows all operations. */
Expand Down Expand Up @@ -240,6 +270,12 @@ public AuthorizationViolationException onViolation( String msg )
{
return new AuthorizationViolationException( msg );
}

@Override
public AccessMode getSnapshot()
{
return FULL;
}
},

/** Allows reading data and schema, but not writing.
Expand Down Expand Up @@ -278,6 +314,12 @@ public AuthorizationViolationException onViolation( String msg )
{
return new AuthorizationViolationException( msg );
}

@Override
public AccessMode getSnapshot()
{
return OVERRIDE_READ;
}
},

/**
Expand Down Expand Up @@ -317,6 +359,12 @@ public AuthorizationViolationException onViolation( String msg )
{
return new AuthorizationViolationException( msg );
}

@Override
public AccessMode getSnapshot()
{
return OVERRIDE_WRITE;
}
},

/**
Expand Down Expand Up @@ -356,6 +404,12 @@ public AuthorizationViolationException onViolation( String msg )
{
return new AuthorizationViolationException( msg );
}

@Override
public AccessMode getSnapshot()
{
return OVERRIDE_SCHEMA;
}
},

}
Expand All @@ -366,4 +420,6 @@ public AuthorizationViolationException onViolation( String msg )
boolean overrideOriginalMode();
AuthorizationViolationException onViolation( String msg );
String name();

AccessMode getSnapshot();
}
Expand Up @@ -75,124 +75,116 @@ default void ensureUserExistsWithName( String username ) throws InvalidArguments
throw new InvalidArgumentsException( "User '" + username + "' does not exit." );
}

/**
* Implementation to use when authentication has not yet been performed. Allows nothing.
*/
AuthSubject ANONYMOUS = new AuthSubject()
abstract class AccessModeAdapter implements AuthSubject
{
@Override
public void logout()
{
}

@Override
public AuthenticationResult getAuthenticationResult()
{
return AuthenticationResult.FAILURE;
}
private final AccessMode accessMode;

@Override
public void setPassword( String password, boolean requirePasswordChange )
throws IOException, InvalidArgumentsException
public AccessModeAdapter( AccessMode accessMode )
{
throw new AuthorizationViolationException( "Anonymous cannot change password" );
this.accessMode = accessMode;
}

@Override
public void setPasswordChangeNoLongerRequired()
public boolean allowsReads()
{
return accessMode.allowsReads();
}

@Override
public boolean hasUsername( String username )
public boolean allowsWrites()
{
return false;
return accessMode.allowsWrites();
}

@Override
public boolean allowsProcedureWith( String[] roleNames )
public boolean allowsSchemaWrites()
{
return false;
return accessMode.allowsSchemaWrites();
}

@Override
public boolean allowsReads()
public boolean overrideOriginalMode()
{
return false;
return accessMode.overrideOriginalMode();
}

@Override
public boolean allowsWrites()
public AuthorizationViolationException onViolation( String msg )
{
return false;
return accessMode.onViolation( msg );
}

@Override
public boolean allowsSchemaWrites()
public String name()
{
return false;
return accessMode.name();
}

@Override
public boolean overrideOriginalMode()
public AccessMode getSnapshot()
{
return false;
return accessMode;
}
}

/**
* Implementation to use when authentication has not yet been performed. Allows nothing.
*/
AuthSubject ANONYMOUS = new AuthSubject.AccessModeAdapter( Static.NONE )
{
@Override
public AuthorizationViolationException onViolation( String msg )
public void logout()
{
return new AuthorizationViolationException( msg );
}

@Override
public String name()
public AuthenticationResult getAuthenticationResult()
{
return "<anonymous>";
return AuthenticationResult.FAILURE;
}

@Override
public String username()
public void setPassword( String password, boolean requirePasswordChange )
throws IOException, InvalidArgumentsException
{
return ""; // Should never clash with a valid username
throw new AuthorizationViolationException( "Anonymous cannot change password" );
}
};

/**
* Implementation to use when authentication is disabled. Allows everything.
*/
AuthSubject AUTH_DISABLED = new AuthSubject()
{
@Override
public boolean allowsReads()
public void setPasswordChangeNoLongerRequired()
{
return true;
}

@Override
public boolean allowsWrites()
public boolean hasUsername( String username )
{
return true;
return false;
}

@Override
public boolean allowsSchemaWrites()
public boolean allowsProcedureWith( String[] roleNames )
{
return true;
return false;
}

@Override
public boolean overrideOriginalMode()
public String name()
{
return false;
return "<anonymous>";
}

@Override
public AuthorizationViolationException onViolation( String msg )
public String username()
{
return new AuthorizationViolationException( msg );
return ""; // Should never clash with a valid username
}
};

/**
* Implementation to use when authentication is disabled. Allows everything.
*/
AuthSubject AUTH_DISABLED = new AuthSubject.AccessModeAdapter( Static.FULL )
{
@Override
public String name()
{
Expand Down
Expand Up @@ -230,7 +230,7 @@ public KernelTransactionImplementation initialize(
this.lastTransactionTimestampWhenStarted = lastTimeStamp;
this.transactionEvent = tracer.beginTransaction();
assert transactionEvent != null : "transactionEvent was null!";
this.accessMode = accessMode;
this.accessMode = accessMode.getSnapshot();
this.transactionId = NOT_COMMITTED_TRANSACTION_ID;
this.commitTime = NOT_COMMITTED_TRANSACTION_COMMIT_TIME;
this.currentTransactionOperations = timeoutMillis > 0 ? operationContainer.guardedParts() : operationContainer.nonGuarderParts();
Expand Down
Expand Up @@ -27,6 +27,7 @@
import org.neo4j.kernel.api.proc.QualifiedName;
import org.neo4j.kernel.api.security.AccessMode;
import org.neo4j.kernel.api.security.AuthSubject;
import org.neo4j.kernel.impl.api.security.AccessModeSnapshot;
import org.neo4j.kernel.impl.proc.Procedures;

public class NonTransactionalDbmsOperations implements DbmsOperations
Expand All @@ -47,9 +48,13 @@ public RawIterator<Object[],ProcedureException> procedureCallDbms(
) throws ProcedureException
{
BasicContext ctx = new BasicContext();
if ( mode instanceof AuthSubject )
AccessMode originalMode = (mode instanceof AccessModeSnapshot) ?
((AccessModeSnapshot) mode).getOriginalAccessMode() :
mode;

if ( originalMode instanceof AuthSubject )
{
ctx.put( Context.AUTH_SUBJECT, (AuthSubject) mode );
ctx.put( Context.AUTH_SUBJECT, (AuthSubject) originalMode );
}
return procedures.callProcedure( ctx, name, input );
}
Expand Down

0 comments on commit 65bfbf3

Please sign in to comment.