diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/commands/expressions/LabelsFunction.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/commands/expressions/LabelsFunction.scala index 34fb2a3e63e0a..a8e13881dc31c 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/commands/expressions/LabelsFunction.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/commands/expressions/LabelsFunction.scala @@ -23,6 +23,7 @@ import org.neo4j.cypher.internal.compatibility.v3_3.runtime.ExecutionContext import org.neo4j.cypher.internal.compatibility.v3_3.runtime.pipes.QueryState import org.neo4j.cypher.internal.frontend.v3_3.ParameterWrongTypeException import org.neo4j.values.AnyValue +import org.neo4j.values.storable.Values import org.neo4j.values.virtual.{NodeValue, VirtualValues} case class LabelsFunction(nodeExpr: Expression) extends NullInNullOutExpression(nodeExpr) { @@ -30,7 +31,8 @@ case class LabelsFunction(nodeExpr: Expression) extends NullInNullOutExpression( override def compute(value: AnyValue, m: ExecutionContext) (implicit state: QueryState): AnyValue = value match { case n: NodeValue => - VirtualValues.fromArray(n.labels()) + val ctx = state.query + VirtualValues.list(ctx.getLabelsForNode(n.id()).map(t => Values.stringValue(ctx.getLabelName(t))).toArray:_*) case x => throw new ParameterWrongTypeException("Expected a Node, got: " + x) } diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/executionplan/procs/ProcedureExecutionResult.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/executionplan/procs/ProcedureExecutionResult.scala index 21575d6e5ac05..1c60c70decf1d 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/executionplan/procs/ProcedureExecutionResult.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_3/runtime/executionplan/procs/ProcedureExecutionResult.scala @@ -32,10 +32,10 @@ import org.neo4j.cypher.internal.spi.v3_3.QueryContext import org.neo4j.cypher.internal.{InternalExecutionResult, QueryStatistics} import org.neo4j.graphdb.Notification import org.neo4j.graphdb.spatial.{Geometry, Point} -import org.neo4j.values.AnyValues.{asMapValue, asPathValue, asPointValue} +import org.neo4j.values.AnyValues._ import org.neo4j.values.result.QueryResult.{QueryResultVisitor, Record} import org.neo4j.values.storable.Values -import org.neo4j.values.storable.Values.{doubleValue, longValue, stringValue} +import org.neo4j.values.storable.Values.{of => _, _} import org.neo4j.values.virtual.VirtualValues.{fromNodeProxy, fromRelationshipProxy} import org.neo4j.values.{AnyValue, AnyValues} @@ -104,12 +104,14 @@ class ProcedureExecutionResult[E <: Exception](context: QueryContext, case CTPath => transform(res(i), asPathValue) case CTInteger => transform(res(i), longValue) case CTFloat => transform(res(i), doubleValue) + case CTNumber => transform(res(i), numberValue) case CTString => transform(res(i), stringValue) - case CTBoolean => transform(res(i), Values.booleanValue) + case CTBoolean => transform(res(i), booleanValue) case CTPoint => transform(res(i), (p: Point) => asPointValue(p)) case CTGeometry => transform(res(i), (g: Geometry) => asPointValue(g)) case CTMap => transform(res(i), asMapValue) - case ListType(_) => transform(res(i), AnyValues.asListValue) + case ListType(_) => transform(res(i), asListValue) + case CTAny => transform(res(i), AnyValues.of) } } visitor.visit(new Record { diff --git a/community/procedure-compiler/integration-tests/src/test/java/org/neo4j/tooling/procedure/ProcedureTest.java b/community/procedure-compiler/integration-tests/src/test/java/org/neo4j/tooling/procedure/ProcedureTest.java index 8d3d0037127d6..bb26839eb5475 100644 --- a/community/procedure-compiler/integration-tests/src/test/java/org/neo4j/tooling/procedure/ProcedureTest.java +++ b/community/procedure-compiler/integration-tests/src/test/java/org/neo4j/tooling/procedure/ProcedureTest.java @@ -86,7 +86,7 @@ public void calls_procedures_with_simple_input_type_returning_record_with_primit Session session = driver.session() ) { - assertThat( session.run( "CALL " + procedureNamespace + ".simpleInput11('string')" ).single() ).isNotNull(); + assertThat( session.run( "CALL " + procedureNamespace + ".simpleInput11('string') YIELD field04 AS p RETURN p" ).single() ).isNotNull(); assertThat( session.run( "CALL " + procedureNamespace + ".simpleInput12(42)" ).single() ).isNotNull(); assertThat( session.run( "CALL " + procedureNamespace + ".simpleInput13(42)" ).single() ).isNotNull(); assertThat( session.run( "CALL " + procedureNamespace + ".simpleInput14(4.2)" ).single() ).isNotNull(); diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthProceduresInteractionTestBase.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthProceduresInteractionTestBase.java index 7b8ee8a14655f..f91d5539db3cf 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthProceduresInteractionTestBase.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthProceduresInteractionTestBase.java @@ -90,7 +90,7 @@ public void shouldChangeOwnPassword() throws Throwable // Because RESTSubject caches an auth token that is sent with every request neo.updateAuthToken( readSubject, "readSubject", "321" ); neo.assertAuthenticated( readSubject ); - testSuccessfulRead( readSubject, 3 ); + testSuccessfulRead( readSubject, 3L ); } @Test @@ -158,13 +158,13 @@ public void shouldChangeUserPasswordIfSameUser() throws Throwable // Because RESTSubject caches an auth token that is sent with every request neo.updateAuthToken( readSubject, "readSubject", "321" ); neo.assertAuthenticated( readSubject ); - testSuccessfulRead( readSubject, 3 ); + testSuccessfulRead( readSubject, 3L ); assertEmpty( adminSubject, "CALL dbms.security.changeUserPassword( 'adminSubject', 'cba', false )" ); // Because RESTSubject caches an auth token that is sent with every request neo.updateAuthToken( adminSubject, "adminSubject", "cba" ); neo.assertAuthenticated( adminSubject ); - testSuccessfulRead( adminSubject, 3 ); + testSuccessfulRead( adminSubject, 3L ); } // Should fail nicely to change own password for non-admin or admin subject if password invalid @@ -987,7 +987,7 @@ public void shouldSetCorrectNoRolePermissions() throws Exception @Test public void shouldSetCorrectReaderPermissions() throws Exception { - testSuccessfulRead( readSubject, 3 ); + testSuccessfulRead( readSubject, 3L ); testFailWrite( readSubject ); testFailTokenWrite( readSubject, WRITE_OPS_NOT_ALLOWED ); testFailSchema( readSubject ); @@ -998,7 +998,7 @@ public void shouldSetCorrectReaderPermissions() throws Exception @Test public void shouldSetCorrectEditorPermissions() throws Exception { - testSuccessfulRead( editorSubject, 3 ); + testSuccessfulRead( editorSubject, 3L ); testSuccessfulWrite( editorSubject ); testFailTokenWrite( editorSubject ); testFailSchema( editorSubject ); @@ -1009,7 +1009,7 @@ public void shouldSetCorrectEditorPermissions() throws Exception @Test public void shouldSetCorrectPublisherPermissions() throws Exception { - testSuccessfulRead( writeSubject, 3 ); + testSuccessfulRead( writeSubject, 3L ); testSuccessfulWrite( writeSubject ); testSuccessfulTokenWrite( writeSubject ); testFailSchema( writeSubject ); @@ -1020,7 +1020,7 @@ public void shouldSetCorrectPublisherPermissions() throws Exception @Test public void shouldSetCorrectSchemaPermissions() throws Exception { - testSuccessfulRead( schemaSubject, 3 ); + testSuccessfulRead( schemaSubject, 3L ); testSuccessfulWrite( schemaSubject ); testSuccessfulTokenWrite( schemaSubject ); testSuccessfulSchema( schemaSubject ); @@ -1031,7 +1031,7 @@ public void shouldSetCorrectSchemaPermissions() throws Exception @Test public void shouldSetCorrectAdminPermissions() throws Exception { - testSuccessfulRead( adminSubject, 3 ); + testSuccessfulRead( adminSubject, 3L ); testSuccessfulWrite( adminSubject ); testSuccessfulTokenWrite( adminSubject ); testSuccessfulSchema( adminSubject ); @@ -1044,7 +1044,7 @@ public void shouldSetCorrectMultiRolePermissions() throws Exception { assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'schemaSubject')" ); - testSuccessfulRead( schemaSubject, 3 ); + testSuccessfulRead( schemaSubject, 3L ); testSuccessfulWrite( schemaSubject ); testSuccessfulSchema( schemaSubject ); testFailCreateUser( schemaSubject, PERMISSION_DENIED ); diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthScenariosInteractionTestBase.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthScenariosInteractionTestBase.java index 24d0a488f7df3..a086fef0c9a06 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthScenariosInteractionTestBase.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthScenariosInteractionTestBase.java @@ -32,6 +32,7 @@ import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.test.DoubleLatch; +import static java.util.Arrays.stream; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasItem; @@ -67,7 +68,7 @@ public void passwordChangeShouldEnableRolePermissions() throws Throwable subject = neo.login( "Henrik", "foo" ); neo.assertAuthenticated( subject ); testFailWrite( subject ); - testSuccessfulRead( subject, 3 ); + testSuccessfulRead( subject, valueOf( 3 ) ); } @Test @@ -87,7 +88,9 @@ public void shouldLogSecurityEvents() throws Exception { S mats = neo.login( "mats", "neo4j" ); // for REST, login doesn't happen until the subject does something - neo.executeQuery( mats, "UNWIND [] AS i RETURN 1", Collections.emptyMap(), r -> {} ); + neo.executeQuery( mats, "UNWIND [] AS i RETURN 1", Collections.emptyMap(), r -> + { + } ); assertEmpty( adminSubject, "CALL dbms.security.createUser('mats', 'neo4j', false)" ); assertEmpty( adminSubject, "CALL dbms.security.createRole('role1')" ); assertEmpty( adminSubject, "CALL dbms.security.deleteRole('role1')" ); @@ -113,11 +116,11 @@ public void shouldLogSecurityEvents() throws Exception log.assertHasLine( "adminSubject", "deleted role `role1`" ); log.assertHasLine( "mats", "logged in" ); log.assertHasLine( "adminSubject", "added role `reader` to user `mats`" ); - log.assertHasLine( "mats", "tried to change password for user `neo4j`: " + PERMISSION_DENIED); - log.assertHasLine( "mats", "tried to change password: A password cannot be empty."); - log.assertHasLine( "mats", "changed password"); - log.assertHasLine( "adminSubject", "removed role `reader` from user `mats`"); - log.assertHasLine( "adminSubject", "deleted user `mats`"); + log.assertHasLine( "mats", "tried to change password for user `neo4j`: " + PERMISSION_DENIED ); + log.assertHasLine( "mats", "tried to change password: A password cannot be empty." ); + log.assertHasLine( "mats", "changed password" ); + log.assertHasLine( "adminSubject", "removed role `reader` from user `mats`" ); + log.assertHasLine( "adminSubject", "deleted user `mats`" ); } /* @@ -141,7 +144,7 @@ public void userCreation2() throws Throwable testFailRead( subject, 3 ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'Henrik')" ); testFailWrite( subject ); - testSuccessfulRead( subject, 3 ); + testSuccessfulRead( subject, valueOf( 3 ) ); } /* @@ -163,7 +166,7 @@ public void userCreation3() throws Throwable testFailRead( subject, 3 ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + PUBLISHER + "', 'Henrik')" ); testSuccessfulWrite( subject ); - testSuccessfulRead( subject, 4 ); + testSuccessfulRead( subject, valueOf( 4 ) ); testFailSchema( subject ); } @@ -193,7 +196,7 @@ public void userCreation4() throws Throwable testFailCreateUser( subject, PERMISSION_DENIED ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + ARCHITECT + "', 'Henrik')" ); testSuccessfulWrite( subject ); - testSuccessfulRead( subject, 4 ); + testSuccessfulRead( subject, valueOf( 4 ) ); testSuccessfulSchema( subject ); testFailCreateUser( subject, PERMISSION_DENIED ); } @@ -306,7 +309,7 @@ public void roleManagement1() throws Throwable testFailRead( subject, 4 ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'Henrik')" ); testFailWrite( subject ); - testSuccessfulRead( subject, 4 ); + testSuccessfulRead( subject, valueOf( 4 ) ); } /* @@ -349,10 +352,10 @@ public void roleManagement3() throws Throwable neo.assertAuthenticated( subject ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'Henrik')" ); testSuccessfulWrite( subject ); - testSuccessfulRead( subject, 4 ); + testSuccessfulRead( subject, valueOf( 4 )); assertEmpty( adminSubject, "CALL dbms.security.removeRoleFromUser('" + PUBLISHER + "', 'Henrik')" ); testFailWrite( subject ); - testSuccessfulRead( subject, 4 ); + testSuccessfulRead( subject, valueOf( 4 ) ); } /* @@ -375,7 +378,7 @@ public void roleManagement4() throws Throwable neo.assertAuthenticated( subject ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'Henrik')" ); testSuccessfulWrite( subject ); - testSuccessfulRead( subject, 4 ); + testSuccessfulRead( subject, valueOf( 4 ) ); assertEmpty( adminSubject, "CALL dbms.security.removeRoleFromUser('" + READER + "', 'Henrik')" ); assertEmpty( adminSubject, "CALL dbms.security.removeRoleFromUser('" + PUBLISHER + "', 'Henrik')" ); testFailWrite( subject ); @@ -441,15 +444,15 @@ public void customRoleWithProcedureAccess() throws Exception assertEmpty( adminSubject, "CALL dbms.security.createRole('role1')" ); testFailTestProcs( mats ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('role1', 'mats')" ); - testSuccessfulTestProcs( mats ); + testSuccessfulTestProcs( mats, this::valueOf ); assertEmpty( adminSubject, "CALL dbms.security.deleteRole('role1')" ); testFailTestProcs( mats ); assertEmpty( adminSubject, "CALL dbms.security.createRole('role1')" ); testFailTestProcs( mats ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('architect', 'mats')" ); - testSuccessfulTestProcs( mats ); + testSuccessfulTestProcs( mats, this::valueOf ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('role1', 'mats')" ); - testSuccessfulTestProcs( mats ); + testSuccessfulTestProcs( mats, this::valueOf ); } //---------- User suspension ----------- @@ -489,7 +492,7 @@ public void userSuspension2() throws Throwable assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'Henrik')" ); S subject = neo.login( "Henrik", "bar" ); neo.assertAuthenticated( subject ); - testSuccessfulRead( subject, 3 ); + testSuccessfulRead( subject, valueOf( 3 ) ); assertEmpty( adminSubject, "CALL dbms.security.suspendUser('Henrik')" ); neo.assertSessionKilled( subject ); @@ -533,14 +536,14 @@ public void userActivation1() throws Throwable @Test public void userListing() throws Throwable { - testSuccessfulListUsers( adminSubject, initialUsers ); + testSuccessfulListUsers( adminSubject, stream( initialUsers ).map( this::valueOf ).toArray() ); assertEmpty( adminSubject, "CALL dbms.security.createUser('Henrik', 'bar', false)" ); - testSuccessfulListUsers( adminSubject, with( initialUsers, "Henrik" ) ); + testSuccessfulListUsers( adminSubject, stream(with( initialUsers, "Henrik" ) ).map( this::valueOf ).toArray()); S subject = neo.login( "Henrik", "bar" ); neo.assertAuthenticated( subject ); testFailListUsers( subject, 6, PERMISSION_DENIED ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + ADMIN + "', 'Henrik')" ); - testSuccessfulListUsers( subject, with( initialUsers, "Henrik" ) ); + testSuccessfulListUsers( subject, stream(with( initialUsers, "Henrik" )).map( this::valueOf ).toArray() ); } /* @@ -557,10 +560,10 @@ public void rolesListing() throws Throwable assertEmpty( adminSubject, "CALL dbms.security.createUser('Henrik', 'bar', false)" ); S subject = neo.login( "Henrik", "bar" ); neo.assertAuthenticated( subject ); - testFailListRoles( subject, PERMISSION_DENIED); - testSuccessfulListRoles( adminSubject, initialRoles ); + testFailListRoles( subject, PERMISSION_DENIED ); + testSuccessfulListRoles( adminSubject, stream( initialRoles ).map( this::valueOf ).toArray() ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + ADMIN + "', 'Henrik')" ); - testSuccessfulListRoles( subject, initialRoles ); + testSuccessfulListRoles( subject, stream( initialRoles ).map( this::valueOf ).toArray() ); } /* @@ -585,11 +588,11 @@ public void listingUserRoles() throws Throwable testFailListUserRoles( subject, "Craig", PERMISSION_DENIED ); assertSuccess( adminSubject, "CALL dbms.security.listRolesForUser('Craig') YIELD value as roles RETURN roles", - r -> assertKeyIs( r, "roles", PUBLISHER ) ); + r -> assertKeyIs( r, "roles", valueOf( PUBLISHER ) ) ); S craigSubject = neo.login( "Craig", "foo" ); assertSuccess( craigSubject, "CALL dbms.security.listRolesForUser('Craig') YIELD value as roles RETURN roles", - r -> assertKeyIs( r, "roles", PUBLISHER ) ); + r -> assertKeyIs( r, "roles", valueOf( PUBLISHER ) ) ); } /* @@ -613,7 +616,7 @@ public void listingRoleUsers() throws Throwable testFailListRoleUsers( subject, PUBLISHER, PERMISSION_DENIED ); assertSuccess( adminSubject, "CALL dbms.security.listUsersForRole('" + PUBLISHER + "') YIELD value as users RETURN users", - r -> assertKeyIs( r, "users", "Henrik", "Craig", "writeSubject" ) ); + r -> assertKeyIs( r, "users", valueOf( "Henrik" ), valueOf( "Craig" ), valueOf( "writeSubject" ) ) ); } //---------- calling procedures ----------- @@ -641,13 +644,13 @@ public void callProcedures1() throws Throwable assertEmpty( henrik, "CALL test.createNode()" ); assertSuccess( henrik, "CALL test.numNodes() YIELD count as count RETURN count", - r -> assertKeyIs( r, "count", "4" ) ); + r -> assertKeyIs( r, "count", valueOf( "4" ) ) ); assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'Henrik')" ); assertEmpty( henrik, "CALL test.createNode()" ); assertSuccess( henrik, "CALL test.numNodes() YIELD count as count RETURN count", - r -> assertKeyIs( r, "count", "5" ) ); + r -> assertKeyIs( r, "count", valueOf( "5" ) ) ); assertEmpty( adminSubject, "CALL dbms.security.removeRoleFromUser('" + PUBLISHER + "', 'Henrik')" ); @@ -677,16 +680,17 @@ public void changeUserPassword1() throws Throwable assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'Henrik')" ); S subject = neo.login( "Henrik", "abc" ); neo.assertAuthenticated( subject ); - testSuccessfulRead( subject, 3 ); + testSuccessfulRead( subject, valueOf( 3 ) ); assertEmpty( subject, "CALL dbms.security.changeUserPassword('Henrik', '123', false)" ); - neo.updateAuthToken( subject, "Henrik", "123" ); // Because RESTSubject caches an auth token that is sent with every request - testSuccessfulRead( subject, 3 ); + neo.updateAuthToken( subject, "Henrik", + "123" ); // Because RESTSubject caches an auth token that is sent with every request + testSuccessfulRead( subject, valueOf( 3) ); neo.logout( subject ); subject = neo.login( "Henrik", "abc" ); neo.assertInitFailed( subject ); subject = neo.login( "Henrik", "123" ); neo.assertAuthenticated( subject ); - testSuccessfulRead( subject, 3 ); + testSuccessfulRead( subject, valueOf( 3L ) ); } /* @@ -708,14 +712,14 @@ public void changeUserPassword2() throws Throwable assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'Henrik')" ); S subject = neo.login( "Henrik", "abc" ); neo.assertAuthenticated( subject ); - testSuccessfulRead( subject, 3 ); + testSuccessfulRead( subject, valueOf( 3 ) ); assertEmpty( adminSubject, "CALL dbms.security.changeUserPassword('Henrik', '123', false)" ); neo.logout( subject ); subject = neo.login( "Henrik", "abc" ); neo.assertInitFailed( subject ); subject = neo.login( "Henrik", "123" ); neo.assertAuthenticated( subject ); - testSuccessfulRead( subject, 3 ); + testSuccessfulRead( subject, valueOf( 3 ) ); } /* @@ -734,7 +738,7 @@ public void changeUserPassword3() throws Throwable assertEmpty( adminSubject, "CALL dbms.security.addRoleToUser('" + READER + "', 'Henrik')" ); S subject = neo.login( "Henrik", "abc" ); neo.assertAuthenticated( subject ); - testSuccessfulRead( subject, 3 ); + testSuccessfulRead( subject, valueOf( 3 ) ); assertFail( subject, "CALL dbms.security.changeUserPassword('Craig', '123')", PERMISSION_DENIED ); } @@ -746,15 +750,15 @@ public void shouldNotTryToCreateTokensWhenReading() assertEmpty( adminSubject, "CREATE (:MyNode)" ); assertSuccess( readSubject, "MATCH (n:MyNode) WHERE n.nonExistent = 'foo' RETURN toString(count(*)) AS c", - r -> assertKeyIs( r, "c", "0" ) ); + r -> assertKeyIs( r, "c", valueOf( "0" ) ) ); assertFail( readSubject, "MATCH (n:MyNode) SET n.nonExistent = 'foo' RETURN toString(count(*)) AS c", TOKEN_CREATE_OPS_NOT_ALLOWED ); assertFail( readSubject, "MATCH (n:MyNode) SET n:Foo RETURN toString(count(*)) AS c", TOKEN_CREATE_OPS_NOT_ALLOWED ); assertSuccess( schemaSubject, "MATCH (n:MyNode) SET n.nonExistent = 'foo' RETURN toString(count(*)) AS c", - r -> assertKeyIs( r, "c", "1" ) ); + r -> assertKeyIs( r, "c", valueOf( "1" ) ) ); assertSuccess( readSubject, "MATCH (n:MyNode) WHERE n.nonExistent = 'foo' RETURN toString(count(*)) AS c", - r -> assertKeyIs( r, "c", "1" ) ); + r -> assertKeyIs( r, "c", valueOf( "1" ) ) ); } private class SecurityLog @@ -766,7 +770,7 @@ void load() throws IOException File securityLog = new File( AuthScenariosInteractionTestBase.this.securityLog.getAbsolutePath() ); try ( FileSystemAbstraction fileSystem = neo.fileSystem(); BufferedReader bufferedReader = new BufferedReader( - fileSystem.openAsReader( securityLog, Charsets.UTF_8 ) ) ) + fileSystem.openAsReader( securityLog, Charsets.UTF_8 ) ) ) { lines = bufferedReader.lines().collect( java.util.stream.Collectors.toList() ); } @@ -778,4 +782,6 @@ void assertHasLine( String subject, String msg ) assertThat( lines, hasItem( containsString( "[" + subject + "]: " + msg ) ) ); } } + + protected abstract Object valueOf( Object obj ); } diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BoltAuthScenariosInteractionIT.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BoltAuthScenariosInteractionIT.java index c28cfc9caf918..5edb09482be4c 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BoltAuthScenariosInteractionIT.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BoltAuthScenariosInteractionIT.java @@ -25,6 +25,7 @@ import org.neo4j.graphdb.mockfs.UncloseableDelegatingFileSystemAbstraction; import org.neo4j.test.rule.fs.EphemeralFileSystemRule; +import org.neo4j.values.AnyValues; public class BoltAuthScenariosInteractionIT extends AuthScenariosInteractionTestBase { @@ -39,9 +40,16 @@ public BoltAuthScenariosInteractionIT() } @Override - public NeoInteractionLevel setUpNeoServer( Map config ) + public NeoInteractionLevel setUpNeoServer( Map config ) throws Throwable { - return new BoltInteraction( config, () -> new UncloseableDelegatingFileSystemAbstraction( fileSystemRule.get() ) ); + return new BoltInteraction( config, + () -> new UncloseableDelegatingFileSystemAbstraction( fileSystemRule.get() ) ); + } + + @Override + protected Object valueOf( Object obj ) + { + return AnyValues.of( obj ); } } diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BoltConfiguredAuthScenariosInteractionIT.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BoltConfiguredAuthScenariosInteractionIT.java index c749161fb77ad..bbe36ecab202f 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BoltConfiguredAuthScenariosInteractionIT.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BoltConfiguredAuthScenariosInteractionIT.java @@ -21,6 +21,8 @@ import java.util.Map; +import org.neo4j.values.AnyValues; + public class BoltConfiguredAuthScenariosInteractionIT extends ConfiguredAuthScenariosInteractionTestBase { public BoltConfiguredAuthScenariosInteractionIT() @@ -36,4 +38,11 @@ public NeoInteractionLevel setUpNeoServer( Map> maps = r.stream().collect( Collectors.toSet() ); - Matcher> thisQuery = listedQueryOfInteractionLevel( startTime, "adminSubject", listQueriesQuery ); + Matcher> thisQuery = + listedQueryOfInteractionLevel( startTime, "adminSubject", listQueriesQuery ); Matcher> matchQueryMatcher = - listedQueryWithMetaData( startTime, "writeSubject", matchQuery, map( "realUser", "MyMan") ); + listedQueryWithMetaData( startTime, "writeSubject", matchQuery, map( "realUser", "MyMan" ) ); assertThat( maps, matchesOneToOneInAnyOrder( thisQuery, matchQueryMatcher ) ); } ); @@ -176,13 +178,19 @@ public void shouldListAllQueryIncludingMetaData() throws Throwable tx.closeAndAssertSuccess(); } + private Object metadata( Map map ) + { + return AnyValues.asMapValue( map ); + } + + @SuppressWarnings( "unchecked" ) @Test public void shouldListAllQueriesWhenRunningAsAdmin() throws Throwable { DoubleLatch latch = new DoubleLatch( 3, true ); OffsetDateTime startTime = OffsetDateTime - .ofInstant( Instant.ofEpochMilli( OffsetDateTime.now().toEpochSecond() ), UTC_ZONE_ID); + .ofInstant( Instant.ofEpochMilli( OffsetDateTime.now().toEpochSecond() ), UTC_ZONE_ID ); ThreadedTransaction read1 = new ThreadedTransaction<>( neo, latch ); ThreadedTransaction read2 = new ThreadedTransaction<>( neo, latch ); @@ -214,7 +222,7 @@ public void shouldOnlyListOwnQueriesWhenNotRunningAsAdmin() throws Throwable { DoubleLatch latch = new DoubleLatch( 3, true ); OffsetDateTime startTime = OffsetDateTime - .ofInstant( Instant.ofEpochMilli( OffsetDateTime.now().toEpochSecond() ), UTC_ZONE_ID); + .ofInstant( Instant.ofEpochMilli( OffsetDateTime.now().toEpochSecond() ), UTC_ZONE_ID ); ThreadedTransaction read1 = new ThreadedTransaction<>( neo, latch ); ThreadedTransaction read2 = new ThreadedTransaction<>( neo, latch ); @@ -258,7 +266,7 @@ public void shouldListQueriesEvenIfUsingPeriodicCommit() throws Throwable int localPort = getLocalPort( server ); OffsetDateTime startTime = OffsetDateTime - .ofInstant( Instant.ofEpochMilli( OffsetDateTime.now().toEpochSecond() ), UTC_ZONE_ID); + .ofInstant( Instant.ofEpochMilli( OffsetDateTime.now().toEpochSecond() ), UTC_ZONE_ID ); // When ThreadedTransaction write = new ThreadedTransaction<>( neo, latch ); @@ -267,7 +275,7 @@ public void shouldListQueriesEvenIfUsingPeriodicCommit() throws Throwable { String writeQuery = write.executeEarly( threading, writeSubject, KernelTransaction.Type.implicit, format( "USING PERIODIC COMMIT 10 LOAD CSV FROM 'http://localhost:%d' AS line ", localPort ) + - "CREATE (n:A {id: line[0], square: line[1]}) " + "RETURN count(*)" ); + "CREATE (n:A {id: line[0], square: line[1]}) " + "RETURN count(*)" ); latch.startAndWaitForAllToStart(); // Then @@ -304,7 +312,7 @@ public void shouldListAllQueriesWithAuthDisabled() throws Throwable DoubleLatch latch = new DoubleLatch( 2, true ); OffsetDateTime startTime = OffsetDateTime - .ofInstant( Instant.ofEpochMilli( OffsetDateTime.now().toEpochSecond() ), UTC_ZONE_ID); + .ofInstant( Instant.ofEpochMilli( OffsetDateTime.now().toEpochSecond() ), UTC_ZONE_ID ); ThreadedTransaction read = new ThreadedTransaction<>( neo, latch ); @@ -467,19 +475,21 @@ private void executeTwoQueriesAndKillTheFirst( S executor1, S executor2, S kille public void shouldSelfKillQuery() throws Throwable { String result = neo.executeQuery( - readSubject, - "WITH 'Hello' AS marker CALL dbms.listQueries() YIELD queryId AS id, query " + - "WITH * WHERE query CONTAINS 'Hello' CALL dbms.killQuery(id) YIELD username " + - "RETURN count(username) AS count, username", - emptyMap(), - r -> {} + readSubject, + "WITH 'Hello' AS marker CALL dbms.listQueries() YIELD queryId AS id, query " + + "WITH * WHERE query CONTAINS 'Hello' CALL dbms.killQuery(id) YIELD username " + + "RETURN count(username) AS count, username", + emptyMap(), + r -> + { + } ); assertThat( result, containsString( "Explicitly terminated by the user." ) ); assertEmpty( - adminSubject, - "CALL dbms.listQueries() YIELD query WITH * WHERE NOT query CONTAINS 'listQueries' RETURN *" ); + adminSubject, + "CALL dbms.listQueries() YIELD query WITH * WHERE NOT query CONTAINS 'listQueries' RETURN *" ); } @Test @@ -496,9 +506,9 @@ public void shouldFailToTerminateOtherUsersQuery() throws Throwable { String id1 = extractQueryId( q1 ); assertFail( - writeSubject, - "CALL dbms.killQuery('" + id1 + "') YIELD username RETURN *", - PERMISSION_DENIED + writeSubject, + "CALL dbms.killQuery('" + id1 + "') YIELD username RETURN *", + PERMISSION_DENIED ); latch.finishAndWaitForAllToFinish(); read.closeAndAssertSuccess(); @@ -511,8 +521,8 @@ public void shouldFailToTerminateOtherUsersQuery() throws Throwable } assertEmpty( - adminSubject, - "CALL dbms.listQueries() YIELD query WITH * WHERE NOT query CONTAINS 'listQueries' RETURN *" ); + adminSubject, + "CALL dbms.listQueries() YIELD query WITH * WHERE NOT query CONTAINS 'listQueries' RETURN *" ); } @SuppressWarnings( "unchecked" ) @@ -540,7 +550,7 @@ public void shouldTerminateQueriesEvenIfUsingPeriodicCommit() throws Throwable { String writeQuery = write.executeEarly( threading, writeSubject, KernelTransaction.Type.implicit, format( "USING PERIODIC COMMIT 10 LOAD CSV FROM 'http://localhost:%d' AS line ", localPort ) + - "CREATE (n:A {id: line[0], square: line[1]}) RETURN count(*)" ); + "CREATE (n:A {id: line[0], square: line[1]}) RETURN count(*)" ); latch.startAndWaitForAllToStart(); // Then @@ -619,8 +629,8 @@ public void shouldKillMultipleUserQueries() throws Throwable write.closeAndAssertSuccess(); assertEmpty( - adminSubject, - "CALL dbms.listQueries() YIELD query WITH * WHERE NOT query CONTAINS 'listQueries' RETURN *" ); + adminSubject, + "CALL dbms.listQueries() YIELD query WITH * WHERE NOT query CONTAINS 'listQueries' RETURN *" ); } String extractQueryId( String writeQuery ) @@ -647,10 +657,12 @@ public void readUpdatedMetadataValue() throws Throwable String testValue = "testValue"; String testKey = "test"; GraphDatabaseFacade graph = neo.getLocalGraph(); - try ( InternalTransaction transaction = neo.beginLocalTransactionAsUser( writeSubject, KernelTransaction.Type.explicit ) ) + try ( InternalTransaction transaction = neo + .beginLocalTransactionAsUser( writeSubject, KernelTransaction.Type.explicit ) ) { graph.execute( "CALL dbms.setTXMetaData({" + testKey + ":'" + testValue + "'})" ); - Map metadata = (Map) graph.execute( "CALL dbms.getTXMetaData " ).next().get( "metadata" ); + Map metadata = + (Map) graph.execute( "CALL dbms.getTXMetaData " ).next().get( "metadata" ); assertEquals( testValue, metadata.get( testKey ) ); } } @@ -732,7 +744,8 @@ public void shouldHandleWriteAfterAllowedReadProcedureForWriteUser() throws Thro userManager.newRole( "role1" ); userManager.addRoleToUser( "role1", "role1Subject" ); userManager.addRoleToUser( PUBLISHER, "role1Subject" ); - assertEmpty( neo.login( "role1Subject", "abc" ), "CALL test.allowedReadProcedure() YIELD value CREATE (:NEWNODE {name:value})" ); + assertEmpty( neo.login( "role1Subject", "abc" ), + "CALL test.allowedReadProcedure() YIELD value CREATE (:NEWNODE {name:value})" ); } @Test @@ -770,9 +783,9 @@ public void shouldNotAllowNonReaderToReadAfterCallingAllowedReadProc() throws Ex userManager.addRoleToUser( "role1", "nopermission" ); // should not be able to invoke any procedure assertSuccess( neo.login( "nopermission", "abc" ), "CALL test.allowedReadProcedure()", - itr -> assertEquals( itr.stream().collect( toList() ).size(), 1 )); + itr -> assertEquals( itr.stream().collect( toList() ).size(), 1 ) ); assertFail( neo.login( "nopermission", "abc" ), - "CALL test.allowedReadProcedure() YIELD value MATCH (n:Secret) RETURN n.pass", READ_OPS_NOT_ALLOWED); + "CALL test.allowedReadProcedure() YIELD value MATCH (n:Secret) RETURN n.pass", READ_OPS_NOT_ALLOWED ); } @Test @@ -857,7 +870,7 @@ public void shouldFailNestedAllowedWriteProcedureFromNormalReadProcedure() throw userManager.newRole( "role1" ); userManager.addRoleToUser( "role1", "role1Subject" ); userManager.addRoleToUser( PredefinedRoles.PUBLISHER, "role1Subject" ); // Even if subject has WRITE permission - // the procedure should restrict to READ + // the procedure should restrict to READ assertFail( neo.login( "role1Subject", "abc" ), "CALL test.nestedReadProcedure('test.allowedWriteProcedure') YIELD value", WRITE_OPS_NOT_ALLOWED ); @@ -946,7 +959,7 @@ public void shouldTerminateOnlyGivenUsersTransaction() throws Throwable r -> assertKeyIsMap( r, "username", "transactionsTerminated", map( "schemaSubject", "1" ) ) ); assertSuccess( adminSubject, "CALL dbms.listTransactions()", - r -> assertKeyIsMap( r, "username", "activeTransactions", + r -> assertKeyIsMap( r, "username", "activeTransactions", map( "adminSubject", "1", "writeSubject", "1" ) ) ); latch.finishAndWaitForAllToFinish(); @@ -973,7 +986,7 @@ public void shouldTerminateAllTransactionsForGivenUser() throws Throwable r -> assertKeyIsMap( r, "username", "transactionsTerminated", map( "schemaSubject", "2" ) ) ); assertSuccess( adminSubject, "CALL dbms.listTransactions()", - r -> assertKeyIsMap( r, "username", "activeTransactions", map( "adminSubject", "1" ) ) ); + r -> assertKeyIsMap( r, "username", "activeTransactions", map( "adminSubject", "1" ) ) ); latch.finishAndWaitForAllToFinish(); @@ -1148,19 +1161,19 @@ private Matcher> listedQueryWithMetaData( OffsetDateTime star } @SuppressWarnings( "unchecked" ) - private Matcher> hasQuery( String query ) + private Matcher> hasQuery( String query ) { return (Matcher) hasEntry( equalTo( "query" ), equalTo( query ) ); } @SuppressWarnings( "unchecked" ) - private Matcher> hasUsername( String username ) + private Matcher> hasUsername( String username ) { return (Matcher) hasEntry( equalTo( "username" ), equalTo( username ) ); } @SuppressWarnings( "unchecked" ) - private Matcher> hasQueryId() + private Matcher> hasQueryId() { Matcher queryId = equalTo( "queryId" ); Matcher valueMatcher = @@ -1169,7 +1182,7 @@ private Matcher> hasQueryId() } @SuppressWarnings( "unchecked" ) - private Matcher> hasStartTimeAfter( OffsetDateTime startTime ) + private Matcher> hasStartTimeAfter( OffsetDateTime startTime ) { return (Matcher) hasEntry( equalTo( "startTime" ), new BaseMatcher() { @@ -1182,32 +1195,32 @@ public void describeTo( Description description ) @Override public boolean matches( Object item ) { - OffsetDateTime otherTime = OffsetDateTime.from( ISO_OFFSET_DATE_TIME.parse( item.toString() ) ); + OffsetDateTime otherTime = OffsetDateTime.from( ISO_OFFSET_DATE_TIME.parse( item.toString() ) ); return startTime.compareTo( otherTime ) <= 0; } } ); } @SuppressWarnings( "unchecked" ) - private Matcher> hasNoParameters() + private Matcher> hasNoParameters() { return (Matcher) hasEntry( equalTo( "parameters" ), equalTo( emptyMap() ) ); } @SuppressWarnings( "unchecked" ) - private Matcher> hasProtocol( String expected ) + private Matcher> hasProtocol( String expected ) { return (Matcher) hasEntry( "protocol", expected ); } @SuppressWarnings( "unchecked" ) - private Matcher> hasMetaData( Map expected ) + private Matcher> hasMetaData( Map expected ) { return (Matcher) hasEntry( equalTo( "metaData" ), allOf( expected.entrySet().stream().map( entryMapper() - ).collect( Collectors.toList() ) - ) ); + ).collect( Collectors.toList() ) + ) ); } @SuppressWarnings( {"rawtypes", "unchecked"} ) diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ConfiguredAuthScenariosInteractionTestBase.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ConfiguredAuthScenariosInteractionTestBase.java index 265182b716f62..a8d593872730a 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ConfiguredAuthScenariosInteractionTestBase.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ConfiguredAuthScenariosInteractionTestBase.java @@ -22,6 +22,7 @@ import org.junit.Test; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -32,6 +33,8 @@ import org.neo4j.kernel.impl.coreapi.InternalTransaction; import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; import org.neo4j.server.security.enterprise.configuration.SecuritySettings; +import org.neo4j.values.AnyValues; +import org.neo4j.values.virtual.MapValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -66,7 +69,8 @@ public void shouldAllowRoleCallCreateNewTokensProceduresWhenConfigured() throws public void shouldWarnWhenUsingNativeAndOtherProvider() throws Throwable { configuredSetup( stringMap( SecuritySettings.auth_providers.name(), "native ,LDAP" ) ); - assertSuccess( adminSubject, "CALL dbms.security.listUsers", r -> assertKeyIsMap( r, "username", "roles", userList ) ); + assertSuccess( adminSubject, "CALL dbms.security.listUsers", + r -> assertKeyIsMap( r, "username", "roles", valueOf( userList ) ) ); GraphDatabaseFacade localGraph = neo.getLocalGraph(); InternalTransaction transaction = localGraph .beginTransaction( KernelTransaction.Type.explicit, StandardEnterpriseSecurityContext.AUTH_DISABLED ); @@ -84,7 +88,7 @@ public void shouldNotWarnWhenOnlyUsingNativeProvider() throws Throwable { configuredSetup( stringMap( SecuritySettings.auth_provider.name(), "native" ) ); assertSuccess( adminSubject, "CALL dbms.security.listUsers", - r -> assertKeyIsMap( r, "username", "roles", userList ) ); + r -> assertKeyIsMap( r, "username", "roles", valueOf( userList ) ) ); GraphDatabaseFacade localGraph = neo.getLocalGraph(); InternalTransaction transaction = localGraph .beginTransaction( KernelTransaction.Type.explicit, StandardEnterpriseSecurityContext.AUTH_DISABLED ); @@ -97,14 +101,19 @@ public void shouldNotWarnWhenOnlyUsingNativeProvider() throws Throwable transaction.close(); } - private Map userList = map( + protected Object valueOf( Object obj ) + { + return obj; + } + + private Map userList = map( "adminSubject", listOf( ADMIN ), "readSubject", listOf( READER ), "schemaSubject", listOf( ARCHITECT ), "writeSubject", listOf( PUBLISHER ), "editorSubject", listOf( EDITOR ), - "pwdSubject", listOf( ), - "noneSubject", listOf( ), + "pwdSubject", listOf(), + "noneSubject", listOf(), "neo4j", listOf( ADMIN ) ); diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/EmbeddedAuthScenariosInteractionIT.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/EmbeddedAuthScenariosInteractionIT.java index c2e2aa617f020..f955c82a8e137 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/EmbeddedAuthScenariosInteractionIT.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/EmbeddedAuthScenariosInteractionIT.java @@ -38,4 +38,17 @@ protected NeoInteractionLevel setUpNeoServer( Map new UncloseableDelegatingFileSystemAbstraction( fileSystemRule.get() ) ); } + + @Override + protected Object valueOf( Object obj ) + { + if ( obj instanceof Integer ) + { + return ((Integer) obj).longValue(); + } + else + { + return obj; + } + } } diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ProcedureInteractionTestBase.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ProcedureInteractionTestBase.java index 6b1b392762af0..c3c412e1b1e04 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ProcedureInteractionTestBase.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ProcedureInteractionTestBase.java @@ -32,11 +32,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -45,11 +47,14 @@ import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.security.AuthorizationViolationException; +import org.neo4j.graphdb.spatial.Point; import org.neo4j.helpers.HostnamePort; import org.neo4j.kernel.api.bolt.BoltConnectionTracker; import org.neo4j.kernel.api.bolt.ManagedBoltStateMachine; @@ -66,6 +71,13 @@ import org.neo4j.server.security.enterprise.configuration.SecuritySettings; import org.neo4j.test.DoubleLatch; import org.neo4j.test.rule.concurrent.ThreadingRule; +import org.neo4j.values.AnyValue; +import org.neo4j.values.AnyValueWriter; +import org.neo4j.values.BaseToObjectValueWriter; +import org.neo4j.values.storable.TextValue; +import org.neo4j.values.storable.Values; +import org.neo4j.values.virtual.ListValue; +import org.neo4j.values.virtual.MapValue; import static java.lang.String.format; import static java.util.stream.Collectors.toList; @@ -210,13 +222,13 @@ List listOf( String... values ) //------------- Helper functions--------------- - void testSuccessfulRead( S subject, int count ) + void testSuccessfulRead( S subject, Object count ) { assertSuccess( subject, "MATCH (n) RETURN count(n) as count", r -> { List result = r.stream().map( s -> s.get( "count" ) ).collect( toList() ); assertThat( result.size(), equalTo( 1 ) ); - assertThat( String.valueOf( result.get( 0 ) ), equalTo( String.valueOf( count ) ) ); + assertThat( result.get( 0 ), equalTo( count ) ); } ); } @@ -309,7 +321,7 @@ void testFailDeleteRole( S subject, String roleName, String errMsg ) assertFail( subject, "CALL dbms.security.deleteRole('" + roleName + "')", errMsg ); } - void testSuccessfulListUsers( S subject, String[] users ) + void testSuccessfulListUsers( S subject, Object[] users ) { assertSuccess( subject, "CALL dbms.security.listUsers() YIELD username", r -> assertKeyIsArray( r, "username", users ) ); @@ -320,7 +332,7 @@ void testFailListUsers( S subject, int count, String errMsg ) assertFail( subject, "CALL dbms.security.listUsers() YIELD username", errMsg ); } - void testSuccessfulListRoles( S subject, String[] roles ) + void testSuccessfulListRoles( S subject, Object[] roles ) { assertSuccess( subject, "CALL dbms.security.listRoles() YIELD role", r -> assertKeyIsArray( r, "role", roles ) ); @@ -352,14 +364,14 @@ void testFailTestProcs( S subject ) assertFail( subject, "CALL test.allowedSchemaProcedure()", SCHEMA_OPS_NOT_ALLOWED ); } - void testSuccessfulTestProcs( S subject ) + void testSuccessfulTestProcs( S subject, Function valueOf ) { assertSuccess( subject, "CALL test.allowedReadProcedure()", - r -> assertKeyIs( r, "value", "foo" ) ); + r -> assertKeyIs( r, "value", valueOf.apply( "foo" ) ) ); assertSuccess( subject, "CALL test.allowedWriteProcedure()", - r -> assertKeyIs( r, "value", "a", "a" ) ); + r -> assertKeyIs( r, "value", valueOf.apply( "a" ), valueOf.apply( "a" ) ) ); assertSuccess( subject, "CALL test.allowedSchemaProcedure()", - r -> assertKeyIs( r, "value", "OK" ) ); + r -> assertKeyIs( r, "value", valueOf.apply( "OK" ) ) ); } void assertPasswordChangeWhenPasswordChangeRequired( S subject, String newPassword ) @@ -423,8 +435,7 @@ List> collectSuccessResult( S subject, String call ) private String assertCallEmpty( S subject, String call ) { return neo.executeQuery( subject, call, null, - result -> - { + result -> { List> collect = result.stream().collect( toList() ); assertTrue( "Expected no results but got: " + collect, collect.isEmpty() ); } ); @@ -447,18 +458,31 @@ List getObjectsAsList( ResourceIterator> r, String ke return r.stream().map( s -> s.get( key ) ).collect( toList() ); } - void assertKeyIs( ResourceIterator> r, String key, String... items ) + void assertKeyIs( ResourceIterator> r, String key, Object... items ) { assertKeyIsArray( r, key, items ); } - private void assertKeyIsArray( ResourceIterator> r, String key, String[] items ) + private void assertKeyIsArray( ResourceIterator> r, String key, Object[] items ) { List results = getObjectsAsList( r, key ); assertEquals( Arrays.asList( items ).size(), results.size() ); Assert.assertThat( results, containsInAnyOrder( items ) ); } + static void assertKeyIsMap( ResourceIterator> r, String keyKey, String valueKey, + Object expected ) + { + if (expected instanceof MapValue) + { + assertKeyIsMap( r, keyKey, valueKey, (MapValue) expected ); + } + else + { + assertKeyIsMap( r, keyKey, valueKey, (Map) expected ); + } + } + @SuppressWarnings( "unchecked" ) static void assertKeyIsMap( ResourceIterator> r, String keyKey, String valueKey, Map expected ) @@ -491,6 +515,38 @@ static void assertKeyIsMap( ResourceIterator> r, String keyKe } } + static void assertKeyIsMap( ResourceIterator> r, String keyKey, String valueKey, + MapValue expected ) + { + List> result = r.stream().collect( toList() ); + + assertEquals( "Results for should have size " + expected.size() + " but was " + result.size(), + expected.size(), result.size() ); + + for ( Map row : result ) + { + TextValue key = (TextValue) row.get( keyKey ); + assertTrue( expected.containsKey( key.stringValue() ) ); + assertThat( row, hasKey( valueKey ) ); + + Object objectValue = row.get( valueKey ); + if ( objectValue instanceof ListValue ) + { + ListValue value = (ListValue) objectValue; + ListValue expectedValues = ((ListValue) expected.get( key.stringValue() )); + assertEquals( "sizes", value.size(), expectedValues.size() ); + assertThat( Arrays.asList( value.asArray() ), containsInAnyOrder( expectedValues.asArray() ) ); + } + else + { + String value = ((TextValue) objectValue).stringValue(); + String expectedValue = ((TextValue) expected.get( key.stringValue() )).stringValue(); + assertThat( value, equalTo( expectedValue ) ); + } + } + } + + // --------------------- helpers ----------------------- void shouldTerminateTransactionsForUser( S subject, String procedure ) throws Throwable @@ -524,6 +580,18 @@ private Map countTransactionsByUsername() ).collect( Collectors.toMap( r -> r.username, r -> r.activeTransactions ) ); } + private static Map toRawMap( MapValue mapValue ) + { + BaseToObjectValueWriter writer = writer(); + HashMap raw = new HashMap<>( mapValue.size() ); + for ( Map.Entry entry : mapValue.entrySet() ) + { + entry.getValue().writeTo( writer ); + raw.put( entry.getKey(), writer.value() ); + } + return raw; + } + Map countBoltConnectionsByUsername() { BoltConnectionTracker boltConnectionTracker = EnterpriseBuiltInDbmsProcedures.getBoltConnectionTracker( @@ -537,6 +605,36 @@ Map countBoltConnectionsByUsername() ).collect( Collectors.toMap( r -> r.username, r -> r.connectionCount ) ); } + private static BaseToObjectValueWriter writer() + { + return new BaseToObjectValueWriter() + { + @Override + protected Node newNodeProxyById( long id ) + { + return null; + } + + @Override + protected Relationship newRelationshipProxyById( long id ) + { + return null; + } + + @Override + protected Point newGeographicPoint( double longitude, double latitude, String name, int code, String href ) + { + return null; + } + + @Override + protected Point newCartesianPoint( double x, double y, String name, int code, String href ) + { + return null; + } + }; + } + @SuppressWarnings( "unchecked" ) TransportConnection startBoltSession( String username, String password ) throws Exception { diff --git a/enterprise/server-enterprise/src/test/java/org/neo4j/server/rest/security/CypherRESTAuthScenariosInteractionIT.java b/enterprise/server-enterprise/src/test/java/org/neo4j/server/rest/security/CypherRESTAuthScenariosInteractionIT.java index 20ad8adda8fcf..b823e6a16685f 100644 --- a/enterprise/server-enterprise/src/test/java/org/neo4j/server/rest/security/CypherRESTAuthScenariosInteractionIT.java +++ b/enterprise/server-enterprise/src/test/java/org/neo4j/server/rest/security/CypherRESTAuthScenariosInteractionIT.java @@ -47,4 +47,17 @@ protected NeoInteractionLevel setUpNeoServer( Map co { return new CypherRESTInteraction( config ); } + + @Override + protected Object valueOf( Object obj ) + { + if (obj instanceof Long) + { + return ((Long) obj).intValue(); + } + else + { + return obj; + } + } } diff --git a/enterprise/server-enterprise/src/test/java/org/neo4j/server/rest/security/RESTAuthScenariosInteractionIT.java b/enterprise/server-enterprise/src/test/java/org/neo4j/server/rest/security/RESTAuthScenariosInteractionIT.java index 0d948ac07978d..e44f29afd2d90 100644 --- a/enterprise/server-enterprise/src/test/java/org/neo4j/server/rest/security/RESTAuthScenariosInteractionIT.java +++ b/enterprise/server-enterprise/src/test/java/org/neo4j/server/rest/security/RESTAuthScenariosInteractionIT.java @@ -47,4 +47,17 @@ protected NeoInteractionLevel setUpNeoServer( Map co { return new RESTInteraction( config ); } + + @Override + protected Object valueOf( Object obj ) + { + if ( obj instanceof Long ) + { + return ((Long) obj).intValue(); + } + else + { + return obj; + } + } }