Skip to content

Commit

Permalink
Make the query log include username
Browse files Browse the repository at this point in the history
The query log will now include the name of the access mode (the username)
as part of the log message. This is solved by making all `QuerySession` implementors
use their provided transactional context to acquire the username and put it in
the `toString()`, which is already printed to the query log.
  • Loading branch information
Mats-SX committed Aug 26, 2016
1 parent 264fa5c commit b0bd061
Show file tree
Hide file tree
Showing 11 changed files with 393 additions and 36 deletions.
Expand Up @@ -33,6 +33,7 @@
import org.neo4j.kernel.impl.query.Neo4jTransactionalContext; import org.neo4j.kernel.impl.query.Neo4jTransactionalContext;
import org.neo4j.kernel.impl.query.QueryExecutionEngine; import org.neo4j.kernel.impl.query.QueryExecutionEngine;
import org.neo4j.kernel.impl.query.QuerySession; import org.neo4j.kernel.impl.query.QuerySession;
import org.neo4j.kernel.impl.query.TransactionalContext;


import static java.lang.String.format; import static java.lang.String.format;
import static org.neo4j.kernel.api.KernelTransaction.Type.implicit; import static org.neo4j.kernel.api.KernelTransaction.Type.implicit;
Expand All @@ -58,26 +59,28 @@ public Result run( final String querySource, final AuthSubject authSubject, fina
throws KernelException throws KernelException
{ {
InternalTransaction transaction = queryService.beginTransaction( implicit, authSubject ); InternalTransaction transaction = queryService.beginTransaction( implicit, authSubject );
Neo4jTransactionalContext transactionalContext = TransactionalContext transactionalContext =
new Neo4jTransactionalContext( queryService, transaction, txBridge.get(), locker ); new Neo4jTransactionalContext( queryService, transaction, txBridge.get(), locker );
QuerySession session = new BoltQuerySession( transactionalContext, querySource ); QuerySession session = new BoltQuerySession( transactionalContext, querySource );
return queryExecutionEngine.executeQuery( statement, params, session ); return queryExecutionEngine.executeQuery( statement, params, session );
} }


private static class BoltQuerySession extends QuerySession static class BoltQuerySession extends QuerySession
{ {
private final String querySource; private final String querySource;
private final String username;


BoltQuerySession( Neo4jTransactionalContext transactionalContext, String querySource ) BoltQuerySession( TransactionalContext transactionalContext, String querySource )
{ {
super( transactionalContext ); super( transactionalContext );
this.username = transactionalContext.accessMode().name();
this.querySource = querySource; this.querySource = querySource;
} }


@Override @Override
public String toString() public String toString()
{ {
return format( "bolt-session\t%s", querySource ); return format( "bolt-session\t%s\t%s", querySource, username );
} }
} }
} }
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.bolt.v1.runtime.cypher;

import org.junit.Test;

import org.neo4j.bolt.v1.runtime.cypher.CypherStatementRunner.BoltQuerySession;
import org.neo4j.kernel.api.security.AccessMode;
import org.neo4j.kernel.impl.query.FakeTransactionalContext;
import org.neo4j.kernel.impl.query.QuerySession;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.*;

public class BoltQuerySessionTest
{
@Test
public void shouldIncludeUsernameInToString()
{
QuerySession session = new BoltQuerySession(
new FakeTransactionalContext( AccessMode.Static.READ ), "fakeSource" );

assertThat( session.toString(), equalTo( String.format( "bolt-session\t%s\t%s", "fakeSource", "READ" ) ) );
}

}
Expand Up @@ -67,10 +67,12 @@ public static QuerySession embeddedSession( TransactionalContext transactionalCo
final Thread thread = Thread.currentThread(); final Thread thread = Thread.currentThread();
return new QuerySession( transactionalContext ) return new QuerySession( transactionalContext )
{ {
private final String username = transactionalContext.accessMode().name();

@Override @Override
public String toString() public String toString()
{ {
return format( "embedded-session\tthread\t%s", thread.getName() ); return format( "embedded-session\tthread\t%s\t%s", thread.getName(), username );
} }
}; };
} }
Expand Down
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.query;

import org.junit.Test;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import static org.neo4j.kernel.api.security.AccessMode.Static.WRITE_ONLY;

public class EmbeddedQuerySessionTest
{

@Test
public void shouldIncludeUserNameInToString()
{
TransactionalContext mock = mock( TransactionalContext.class );
when( mock.accessMode() ).thenReturn( WRITE_ONLY );
QuerySession session = QueryEngineProvider.embeddedSession( mock );

assertThat( session.toString(),
equalTo( String.format( "embedded-session\tthread\t%s\t%s",
Thread.currentThread().getName(), WRITE_ONLY.name() ) ) );
}
}
@@ -0,0 +1,123 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.query;

import org.neo4j.graphdb.Lock;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.kernel.GraphDatabaseQueryService;
import org.neo4j.kernel.api.KernelTransaction;
import org.neo4j.kernel.api.ReadOperations;
import org.neo4j.kernel.api.Statement;
import org.neo4j.kernel.api.dbms.DbmsOperations;
import org.neo4j.kernel.api.security.AccessMode;
import org.neo4j.kernel.api.txstate.TxStateHolder;

public class FakeTransactionalContext implements TransactionalContext
{
private final AccessMode mode;

public FakeTransactionalContext( AccessMode mode ) {
this.mode = mode;
}

@Override
public ReadOperations readOperations()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public DbmsOperations dbmsOperations()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public boolean isTopLevelTx()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public void close( boolean success )
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public void commitAndRestartTx()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public void cleanForReuse()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public TransactionalContext provideContext()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public boolean isOpen()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public GraphDatabaseQueryService graph()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public Statement statement()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public TxStateHolder stateView()
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public Lock acquireWriteLock( PropertyContainer p )
{
throw new UnsupportedOperationException( "fake test class" );
}

@Override
public AccessMode accessMode()
{
return mode;
}

@Override
public KernelTransaction.Revertable restrictCurrentTransaction( AccessMode accessMode )
{
throw new UnsupportedOperationException( "fake test class" );
}
}
Expand Up @@ -29,18 +29,20 @@
public class ServerQuerySession extends QuerySession public class ServerQuerySession extends QuerySession
{ {
private final HttpServletRequest request; private final HttpServletRequest request;
private final String username;


public ServerQuerySession( HttpServletRequest request, TransactionalContext transactionalContext ) public ServerQuerySession( HttpServletRequest request, TransactionalContext transactionalContext )
{ {
super( transactionalContext ); super( transactionalContext );
this.username = transactionalContext.accessMode().name();
this.request = request; this.request = request;
} }


@Override @Override
public String toString() public String toString()
{ {
return request == null ? return request == null ?
"server-session" : format("server-session\t%s", username) :
format("server-session\thttp\t%s\t%s", request.getRemoteAddr(), request.getRequestURI() ); format("server-session\thttp\t%s\t%s\t%s", request.getRemoteAddr(), request.getRequestURI(), username );
} }
} }
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.server.rest.web;

import javax.servlet.http.HttpServletRequest;

import org.junit.Test;

import org.neo4j.kernel.api.security.AccessMode;
import org.neo4j.kernel.impl.query.FakeTransactionalContext;
import org.neo4j.kernel.impl.query.QuerySession;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ServerQuerySessionTest
{

@Test
public void shouldIncludeUsernameInToString()
{
QuerySession session = new ServerQuerySession( null, new FakeTransactionalContext( AccessMode.Static.READ ) );

assertThat( session.toString(), equalTo( String.format( "server-session\t%s", "READ" ) ) );
}

@Test
public void shouldIncludeUsernameInToStringWithHttp()
{
HttpServletRequest mock = mock( HttpServletRequest.class );
when(mock.getRemoteAddr()).thenReturn( "remote-addr" );
when(mock.getRequestURI()).thenReturn( "req-uri" );

QuerySession session = new ServerQuerySession( mock, new FakeTransactionalContext( AccessMode.Static.READ ) );

assertThat( session.toString(), equalTo( String.format( "server-session\thttp\t%s\t%s\t%s", "remote-addr", "req-uri", "READ" ) ) );
}

}
Expand Up @@ -199,20 +199,22 @@ private QuerySession shellSession( Session session )
return new ShellQuerySession( session, context ); return new ShellQuerySession( session, context );
} }


private static class ShellQuerySession extends QuerySession static class ShellQuerySession extends QuerySession
{ {
private final Session session; private final Session session;
private final String username;


ShellQuerySession( Session session, TransactionalContext transactionalContext ) ShellQuerySession( Session session, TransactionalContext transactionalContext )
{ {
super( transactionalContext ); super( transactionalContext );
this.username = transactionalContext.accessMode().name();
this.session = session; this.session = session;
} }


@Override @Override
public String toString() public String toString()
{ {
return String.format( "shell-session\tshell\t%s", session.getId() ); return String.format( "shell-session\tshell\t%s\t%s", session.getId(), username );
} }
} }
} }

0 comments on commit b0bd061

Please sign in to comment.