diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/procs/ProcedureSignature.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/procs/ProcedureSignature.java index 9a204e4fbd056..8bd4dfd037f59 100644 --- a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/procs/ProcedureSignature.java +++ b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/procs/ProcedureSignature.java @@ -41,6 +41,7 @@ public class ProcedureSignature private final List inputSignature; private final List outputSignature; private final Mode mode; + private final boolean admin; private final String deprecated; private final String[] allowed; private final String description; @@ -52,6 +53,7 @@ public ProcedureSignature( List inputSignature, List outputSignature, Mode mode, + boolean admin, String deprecated, String[] allowed, String description, @@ -62,6 +64,7 @@ public ProcedureSignature( this.inputSignature = unmodifiableList( inputSignature ); this.outputSignature = outputSignature == VOID ? outputSignature : unmodifiableList( outputSignature ); this.mode = mode; + this.admin = admin; this.deprecated = deprecated; this.allowed = allowed; this.description = description; @@ -79,6 +82,11 @@ public Mode mode() return mode; } + public boolean admin() + { + return admin; + } + public Optional deprecated() { return Optional.ofNullable( deprecated ); @@ -167,6 +175,7 @@ public static class Builder private String[] allowed = new String[0]; private String description; private String warning; + private boolean admin; public Builder( String[] namespace, String name ) { @@ -217,6 +226,12 @@ public Builder allowed( String[] allowed ) return this; } + public Builder admin( boolean admin ) + { + this.admin = admin; + return this; + } + public Builder warning( String warning ) { this.warning = warning; @@ -225,7 +240,7 @@ public Builder warning( String warning ) public ProcedureSignature build() { - return new ProcedureSignature( name, inputSignature, outputSignature, mode, deprecated, allowed, + return new ProcedureSignature( name, inputSignature, outputSignature, mode, admin, deprecated, allowed, description, warning, false ); } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/builtinprocs/BuiltInDbmsProcedures.java b/community/kernel/src/main/java/org/neo4j/kernel/builtinprocs/BuiltInDbmsProcedures.java index ac0cb1449e3d1..dbfdb1add47e3 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/builtinprocs/BuiltInDbmsProcedures.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/builtinprocs/BuiltInDbmsProcedures.java @@ -22,7 +22,6 @@ import java.util.Comparator; import java.util.stream.Stream; -import org.neo4j.graphdb.security.AuthorizationViolationException; import org.neo4j.internal.kernel.api.procs.ProcedureSignature; import org.neo4j.internal.kernel.api.procs.UserFunctionSignature; import org.neo4j.internal.kernel.api.security.SecurityContext; @@ -31,12 +30,12 @@ import org.neo4j.kernel.impl.query.QueryExecutionEngine; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.logging.Log; +import org.neo4j.procedure.Admin; import org.neo4j.procedure.Context; import org.neo4j.procedure.Description; import org.neo4j.procedure.Name; import org.neo4j.procedure.Procedure; -import static org.neo4j.graphdb.security.AuthorizationViolationException.PERMISSION_DENIED; import static org.neo4j.procedure.Mode.DBMS; @SuppressWarnings( "unused" ) @@ -51,15 +50,11 @@ public class BuiltInDbmsProcedures @Context public SecurityContext securityContext; + @Admin @Description( "List the currently active config of Neo4j." ) @Procedure( name = "dbms.listConfig", mode = DBMS ) public Stream listConfig( @Name( value = "searchString", defaultValue = "" ) String searchString ) { - securityContext.assertCredentialsNotExpired(); - if ( !securityContext.isAdmin() ) - { - throw new AuthorizationViolationException( PERMISSION_DENIED ); - } Config config = graph.getDependencyResolver().resolveDependency( Config.class ); return config.getConfigValues().values().stream() .filter( c -> !c.internal() ) @@ -88,16 +83,11 @@ public Stream listFunctions() .map( FunctionResult::new ); } + @Admin @Description( "Clears all query caches." ) @Procedure( name = "dbms.clearQueryCaches", mode = DBMS ) public Stream clearAllQueryCaches() { - securityContext.assertCredentialsNotExpired(); - if ( !securityContext.isAdmin() ) - { - throw new AuthorizationViolationException( PERMISSION_DENIED ); - } - QueryExecutionEngine queryExecutionEngine = graph.getDependencyResolver().resolveDependency( QueryExecutionEngine.class ); long numberOfClearedQueries = queryExecutionEngine.clearQueryCaches() - 1; // this query itself does not count diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureCompiler.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureCompiler.java index 9d28fff297361..787cd044ed209 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureCompiler.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureCompiler.java @@ -36,6 +36,7 @@ import org.neo4j.collection.RawIterator; import org.neo4j.graphdb.Resource; +import org.neo4j.graphdb.security.AuthorizationViolationException; import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.internal.kernel.api.exceptions.ProcedureException; import org.neo4j.internal.kernel.api.procs.FieldSignature; @@ -43,6 +44,7 @@ import org.neo4j.internal.kernel.api.procs.QualifiedName; import org.neo4j.internal.kernel.api.procs.UserAggregator; import org.neo4j.internal.kernel.api.procs.UserFunctionSignature; +import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.io.IOUtils; import org.neo4j.kernel.api.ResourceTracker; import org.neo4j.kernel.api.exceptions.ComponentInjectionException; @@ -57,6 +59,7 @@ import org.neo4j.kernel.api.proc.FailedLoadProcedure; import org.neo4j.kernel.impl.proc.OutputMappers.OutputMapper; import org.neo4j.logging.Log; +import org.neo4j.procedure.Admin; import org.neo4j.procedure.Description; import org.neo4j.procedure.Mode; import org.neo4j.procedure.PerformsWrites; @@ -71,6 +74,7 @@ import static java.util.Collections.emptyIterator; import static java.util.Collections.emptyList; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.procedure_unrestricted; +import static org.neo4j.graphdb.security.AuthorizationViolationException.PERMISSION_DENIED; import static org.neo4j.helpers.collection.Iterators.asRawIterator; /** @@ -271,6 +275,7 @@ private CallableProcedure compileProcedure( Class procDefinition, MethodHandl String description = description( method ); Procedure procedure = method.getAnnotation( Procedure.class ); Mode mode = procedure.mode(); + boolean admin = method.isAnnotationPresent( Admin.class ); if ( method.isAnnotationPresent( PerformsWrites.class ) ) { if ( procedure.mode() != org.neo4j.procedure.Mode.DEFAULT ) @@ -299,13 +304,13 @@ private CallableProcedure compileProcedure( Class procDefinition, MethodHandl description = describeAndLogLoadFailure( procName ); ProcedureSignature signature = new ProcedureSignature( procName, inputSignature, outputMapper.signature(), Mode.DEFAULT, - null, new String[0], description, warning, false ); + admin, null, new String[0], description, warning, false ); return new FailedLoadProcedure( signature ); } } ProcedureSignature signature = - new ProcedureSignature( procName, inputSignature, outputMapper.signature(), mode, deprecated, + new ProcedureSignature( procName, inputSignature, outputMapper.signature(), mode, admin, deprecated, config.rolesFor( procName.toString() ), description, warning, false ); return new ReflectiveProcedure( signature, constructor, method, outputMapper, setters ); } @@ -643,6 +648,17 @@ public RawIterator apply( Context ctx, Object[] inp //API injection inject( ctx, cls ); + // Admin check + if ( signature.admin() ) + { + SecurityContext securityContext = ctx.get( Context.SECURITY_CONTEXT ); + securityContext.assertCredentialsNotExpired(); + if ( !securityContext.isAdmin() ) + { + throw new AuthorizationViolationException( PERMISSION_DENIED ); + } + } + // Call the method Object rs = procedureMethod.invoke( cls, input ); diff --git a/community/procedure-api/src/main/java/org/neo4j/procedure/Admin.java b/community/procedure-api/src/main/java/org/neo4j/procedure/Admin.java new file mode 100644 index 0000000000000..79e39d2e3723b --- /dev/null +++ b/community/procedure-api/src/main/java/org/neo4j/procedure/Admin.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 "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 . + */ +package org.neo4j.procedure; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation marks a {@link Procedure} as only being executable by a user with admin permissions. + */ +@Target( ElementType.METHOD ) +@Retention( RetentionPolicy.RUNTIME ) +public @interface Admin +{ +} diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/replication/ReplicationBenchmarkProcedure.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/replication/ReplicationBenchmarkProcedure.java index 5f9abfc06a3df..e86539ecde604 100644 --- a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/replication/ReplicationBenchmarkProcedure.java +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/replication/ReplicationBenchmarkProcedure.java @@ -25,16 +25,15 @@ import java.util.stream.Stream; import org.neo4j.causalclustering.core.state.machines.dummy.DummyRequest; -import org.neo4j.graphdb.security.AuthorizationViolationException; import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.logging.Log; +import org.neo4j.procedure.Admin; import org.neo4j.procedure.Context; import org.neo4j.procedure.Description; import org.neo4j.procedure.Name; import org.neo4j.procedure.Procedure; import static java.lang.Math.toIntExact; -import static org.neo4j.graphdb.security.AuthorizationViolationException.PERMISSION_DENIED; import static org.neo4j.procedure.Mode.DBMS; @SuppressWarnings( "unused" ) @@ -52,12 +51,11 @@ public class ReplicationBenchmarkProcedure private static long startTime; private static List workers; + @Admin @Description( "Start the benchmark." ) @Procedure( name = "dbms.cluster.benchmark.start", mode = DBMS ) public synchronized void start( @Name( "nThreads" ) Long nThreads, @Name( "blockSize" ) Long blockSize ) { - checkSecurity(); - if ( workers != null ) { throw new IllegalStateException( "Already running." ); @@ -76,12 +74,11 @@ public synchronized void start( @Name( "nThreads" ) Long nThreads, @Name( "block } } + @Admin @Description( "Stop a running benchmark." ) @Procedure( name = "dbms.cluster.benchmark.stop", mode = DBMS ) public synchronized Stream stop() throws InterruptedException { - checkSecurity(); - if ( workers == null ) { throw new IllegalStateException( "Not running." ); @@ -115,15 +112,6 @@ public synchronized Stream stop() throws InterruptedException return Stream.of( new BenchmarkResult( totalRequests, totalBytes, runTime ) ); } - private void checkSecurity() throws AuthorizationViolationException - { - securityContext.assertCredentialsNotExpired(); - if ( !securityContext.isAdmin() ) - { - throw new AuthorizationViolationException( PERMISSION_DENIED ); - } - } - private class Worker implements Runnable { private final int blockSize; diff --git a/enterprise/cypher/cypher/src/test/scala/org/neo4j/cypher/CloseTransactionTest.scala b/enterprise/cypher/cypher/src/test/scala/org/neo4j/cypher/CloseTransactionTest.scala index 8af0529f223d5..7db2cdf995c0c 100644 --- a/enterprise/cypher/cypher/src/test/scala/org/neo4j/cypher/CloseTransactionTest.scala +++ b/enterprise/cypher/cypher/src/test/scala/org/neo4j/cypher/CloseTransactionTest.scala @@ -491,7 +491,7 @@ class CloseTransactionTest extends CypherFunSuite with GraphIcing { val procedureName = new QualifiedName(Array[String]("org", "neo4j", "bench"), "getAllNodes") val emptySignature: util.List[FieldSignature] = List.empty[FieldSignature].asJava val signature: ProcedureSignature = new ProcedureSignature( - procedureName, paramSignature, resultSignature, Mode.READ, null, Array.empty, + procedureName, paramSignature, resultSignature, Mode.READ, false, null, Array.empty, null, null, false) def paramSignature: util.List[FieldSignature] = List.empty[FieldSignature].asJava diff --git a/enterprise/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala b/enterprise/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala index bdc908b933c36..69a5d0ced1d56 100644 --- a/enterprise/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala +++ b/enterprise/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala @@ -71,7 +71,7 @@ class ExecutionEngineIT extends CypherFunSuite with GraphIcing { val procedureName = new QualifiedName(Array[String]("org", "neo4j", "bench"), "getAllNodes") val emptySignature: util.List[FieldSignature] = List.empty[FieldSignature].asJava val signature: ProcedureSignature = new ProcedureSignature( - procedureName, paramSignature, resultSignature, Mode.READ, null, Array.empty, + procedureName, paramSignature, resultSignature, Mode.READ, false, null, Array.empty, null, null, false) def paramSignature: util.List[FieldSignature] = List.empty[FieldSignature].asJava diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/enterprise/builtinprocs/EnterpriseBuiltInDbmsProcedures.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/enterprise/builtinprocs/EnterpriseBuiltInDbmsProcedures.java index 00fdaf4ae3736..8ebf9c9cf8e00 100644 --- a/enterprise/kernel/src/main/java/org/neo4j/kernel/enterprise/builtinprocs/EnterpriseBuiltInDbmsProcedures.java +++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/enterprise/builtinprocs/EnterpriseBuiltInDbmsProcedures.java @@ -53,6 +53,7 @@ import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; import org.neo4j.kernel.impl.proc.Procedures; import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.procedure.Admin; import org.neo4j.procedure.Context; import org.neo4j.procedure.Description; import org.neo4j.procedure.Name; @@ -136,11 +137,10 @@ public Stream terminateTransactionsForUser( @Name( return terminateTransactionsForValidUser( graph.getDependencyResolver(), username, getCurrentTx() ); } + //@Admin //@Procedure( name = "dbms.listConnections", mode = DBMS ) public Stream listConnections() { - assertAdmin(); - BoltConnectionTracker boltConnectionTracker = getBoltConnectionTracker( graph.getDependencyResolver() ); return countConnectionsByUsername( boltConnectionTracker @@ -204,10 +204,10 @@ public Stream listProcedures() @SuppressWarnings( "WeakerAccess" ) public static class ProcedureResult { + // These two procedures are admin procedures but may be executed for your own user, + // this is not documented anywhere but we cannot change the behaviour in a point release private static final List ADMIN_PROCEDURES = - Arrays.asList( "createUser", "deleteUser", "listUsers", "clearAuthCache", "changeUserPassword", - "addRoleToUser", "removeRoleFromUser", "suspendUser", "activateUser", "listRoles", - "listRolesForUser", "listUsersForRole", "createRole", "deleteRole" ); + Arrays.asList( "changeUserPassword", "listRolesForUser" ); public final String name; public final String signature; @@ -225,8 +225,7 @@ public ProcedureResult( ProcedureSignature signature ) switch ( signature.mode() ) { case DBMS: - // TODO: not enough granularity for dbms and user management, needs fix - if ( isAdminProcedure( signature.name().name() ) ) + if ( signature.admin() || isAdminProcedure( signature.name().name() ) ) { roles.add( "admin" ); } @@ -256,21 +255,16 @@ public ProcedureResult( ProcedureSignature signature ) private boolean isAdminProcedure( String procedureName ) { - return name.startsWith( "dbms.security." ) && ADMIN_PROCEDURES.contains( procedureName ) || - name.equals( "dbms.listConfig" ) || - name.equals( "dbms.setConfigValue" ) || - name.equals( "dbms.clearQueryCaches" ); + return name.startsWith( "dbms.security." ) && ADMIN_PROCEDURES.contains( procedureName ); } } + @Admin @Description( "Updates a given setting value. Passing an empty value will result in removing the configured value " + "and falling back to the default value. Changes will not persist and will be lost if the server is restarted." ) @Procedure( name = "dbms.setConfigValue", mode = DBMS ) public void setConfigValue( @Name( "setting" ) String setting, @Name( "value" ) String value ) { - securityContext.assertCredentialsNotExpired(); - assertAdmin(); - Config config = resolver.resolveDependency( Config.class ); config.updateDynamicSetting( setting, value, "dbms.setConfigValue" ); // throws if something goes wrong } @@ -532,22 +526,9 @@ private ZoneId getConfiguredTimeZone() return config.get( GraphDatabaseSettings.db_timezone ).getZoneId(); } - private boolean isAdmin() - { - return securityContext.isAdmin(); - } - - private void assertAdmin() - { - if ( !isAdmin() ) - { - throw new AuthorizationViolationException( PERMISSION_DENIED ); - } - } - private boolean isAdminOrSelf( String username ) { - return isAdmin() || securityContext.subject().hasUsername( username ); + return securityContext.isAdmin() || securityContext.subject().hasUsername( username ); } private void assertAdminOrSelf( String username ) diff --git a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/SecurityProcedures.java b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/SecurityProcedures.java index bfb92018fc784..710abb871f7e5 100644 --- a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/SecurityProcedures.java +++ b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/SecurityProcedures.java @@ -21,13 +21,12 @@ import java.util.stream.Stream; -import org.neo4j.graphdb.security.AuthorizationViolationException; import org.neo4j.kernel.enterprise.api.security.EnterpriseAuthManager; +import org.neo4j.procedure.Admin; import org.neo4j.procedure.Context; import org.neo4j.procedure.Description; import org.neo4j.procedure.Procedure; -import static org.neo4j.graphdb.security.AuthorizationViolationException.PERMISSION_DENIED; import static org.neo4j.procedure.Mode.DBMS; @SuppressWarnings( {"unused", "WeakerAccess"} ) @@ -51,15 +50,11 @@ public Stream showCurrentUser() return Stream.of( userResultForSubject() ); } + @Admin @Description( "Clears authentication and authorization cache." ) @Procedure( name = "dbms.security.clearAuthCache", mode = DBMS ) public void clearAuthenticationCache() { - securityContext.assertCredentialsNotExpired(); - if ( !securityContext.isAdmin() ) - { - throw new AuthorizationViolationException( PERMISSION_DENIED ); - } authManager.clearAuthCache(); } } diff --git a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/UserManagementProcedures.java b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/UserManagementProcedures.java index d2f51490cd6c3..e8d45e40b7be1 100644 --- a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/UserManagementProcedures.java +++ b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/UserManagementProcedures.java @@ -24,6 +24,7 @@ import java.util.stream.Stream; import org.neo4j.kernel.api.exceptions.InvalidArgumentsException; +import org.neo4j.procedure.Admin; import org.neo4j.procedure.Description; import org.neo4j.procedure.Name; import org.neo4j.procedure.Procedure; @@ -34,13 +35,13 @@ public class UserManagementProcedures extends AuthProceduresBase { + @Admin @Description( "Create a new user." ) @Procedure( name = "dbms.security.createUser", mode = DBMS ) public void createUser( @Name( "username" ) String username, @Name( "password" ) String password, @Name( value = "requirePasswordChange", defaultValue = "true" ) boolean requirePasswordChange ) throws InvalidArgumentsException, IOException { - securityContext.assertCredentialsNotExpired(); userManager.newUser( username, password, requirePasswordChange ); } @@ -72,59 +73,59 @@ public void changeUserPassword( @Name( "username" ) String username, @Name( "new setUserPassword( username, newPassword, requirePasswordChange ); } + @Admin @Description( "Assign a role to the user." ) @Procedure( name = "dbms.security.addRoleToUser", mode = DBMS ) public void addRoleToUser( @Name( "roleName" ) String roleName, @Name( "username" ) String username ) throws IOException, InvalidArgumentsException { - securityContext.assertCredentialsNotExpired(); userManager.addRoleToUser( roleName, username ); } + @Admin @Description( "Unassign a role from the user." ) @Procedure( name = "dbms.security.removeRoleFromUser", mode = DBMS ) public void removeRoleFromUser( @Name( "roleName" ) String roleName, @Name( "username" ) String username ) throws InvalidArgumentsException, IOException { - securityContext.assertCredentialsNotExpired(); userManager.removeRoleFromUser( roleName, username ); } + @Admin @Description( "Delete the specified user." ) @Procedure( name = "dbms.security.deleteUser", mode = DBMS ) public void deleteUser( @Name( "username" ) String username ) throws InvalidArgumentsException, IOException { - securityContext.assertCredentialsNotExpired(); if ( userManager.deleteUser( username ) ) { kickoutUser( username, "deletion" ); } } + @Admin @Description( "Suspend the specified user." ) @Procedure( name = "dbms.security.suspendUser", mode = DBMS ) public void suspendUser( @Name( "username" ) String username ) throws IOException, InvalidArgumentsException { - securityContext.assertCredentialsNotExpired(); userManager.suspendUser( username ); kickoutUser( username, "suspension" ); } + @Admin @Description( "Activate a suspended user." ) @Procedure( name = "dbms.security.activateUser", mode = DBMS ) public void activateUser( @Name( "username" ) String username, @Name( value = "requirePasswordChange", defaultValue = "true" ) boolean requirePasswordChange ) throws IOException, InvalidArgumentsException { - securityContext.assertCredentialsNotExpired(); userManager.activateUser( username, requirePasswordChange ); } + @Admin @Description( "List all local users." ) @Procedure( name = "dbms.security.listUsers", mode = DBMS ) public Stream listUsers() { - securityContext.assertCredentialsNotExpired(); Set users = userManager.getAllUsernames(); if ( users.isEmpty() ) { @@ -136,11 +137,11 @@ public Stream listUsers() } } + @Admin @Description( "List all available roles." ) @Procedure( name = "dbms.security.listRoles", mode = DBMS ) public Stream listRoles() { - securityContext.assertCredentialsNotExpired(); Set roles = userManager.getAllRoleNames(); return roles.stream().map( this::roleResultForName ); } @@ -154,28 +155,28 @@ public Stream listRolesForUser( @Name( "username" ) String usernam return userManager.getRoleNamesForUser( username ).stream().map( StringResult::new ); } + @Admin @Description( "List all users currently assigned the specified role." ) @Procedure( name = "dbms.security.listUsersForRole", mode = DBMS ) public Stream listUsersForRole( @Name( "roleName" ) String roleName ) throws InvalidArgumentsException { - securityContext.assertCredentialsNotExpired(); return userManager.getUsernamesForRole( roleName ).stream().map( StringResult::new ); } + @Admin @Description( "Create a new role." ) @Procedure( name = "dbms.security.createRole", mode = DBMS ) public void createRole( @Name( "roleName" ) String roleName ) throws InvalidArgumentsException, IOException { - securityContext.assertCredentialsNotExpired(); userManager.newRole( roleName ); } + @Admin @Description( "Delete the specified role. Any role assignments will be removed." ) @Procedure( name = "dbms.security.deleteRole", mode = DBMS ) public void deleteRole( @Name( "roleName" ) String roleName ) throws InvalidArgumentsException, IOException { - securityContext.assertCredentialsNotExpired(); userManager.deleteRole( roleName ); } diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/integration/bolt/EnterpriseAuthenticationTestBase.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/integration/bolt/EnterpriseAuthenticationTestBase.java index cf4e39c0f5985..a16675959723e 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/integration/bolt/EnterpriseAuthenticationTestBase.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/integration/bolt/EnterpriseAuthenticationTestBase.java @@ -194,7 +194,7 @@ protected void assertAuth( String username, String password ) throws Exception protected void assertAuthAndChangePassword( String username, String password, String newPassword ) throws Exception { assertAuth( username, password ); - String query = format( "CALL dbms.security.changeUserPassword('%s', '%s', false)", username, newPassword ); + String query = format( "CALL dbms.security.changePassword('%s')", newPassword ); client.send( util.chunk( run( query ), pullAll() ) ); assertThat( client, util.eventuallyReceives( msgSuccess(), msgSuccess() ) ); }