Skip to content

Add bearer authentication support #1000

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions driver/src/main/java/org/neo4j/driver/AuthTokens.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
import org.neo4j.driver.internal.security.InternalAuthToken;

import static java.util.Collections.singletonMap;
import static org.neo4j.driver.Values.value;
import static org.neo4j.driver.internal.security.InternalAuthToken.CREDENTIALS_KEY;
import static org.neo4j.driver.internal.security.InternalAuthToken.PARAMETERS_KEY;
import static org.neo4j.driver.internal.security.InternalAuthToken.PRINCIPAL_KEY;
import static org.neo4j.driver.internal.security.InternalAuthToken.REALM_KEY;
import static org.neo4j.driver.internal.security.InternalAuthToken.SCHEME_KEY;
import static org.neo4j.driver.internal.util.Iterables.newHashMapWithSize;
import static org.neo4j.driver.Values.value;

/**
* This is a listing of the various methods of authentication supported by this
Expand Down Expand Up @@ -79,13 +79,32 @@ public static AuthToken basic( String username, String password, String realm )
return new InternalAuthToken( map );
}

/**
* The bearer authentication scheme, using a base64 encoded token.
*
* @param token base64 encoded token
* @return an authentication token that can be used to connect to Neo4j
* @throws NullPointerException when token is {@code null}
* @see GraphDatabase#driver(String, AuthToken)
*/
public static AuthToken bearer( String token )
{
Objects.requireNonNull( token, "Token can't be null" );

Map<String,Value> map = newHashMapWithSize( 2 );
map.put( SCHEME_KEY, value( "bearer" ) );
map.put( CREDENTIALS_KEY, value( token ) );
return new InternalAuthToken( map );
}

/**
* The kerberos authentication scheme, using a base64 encoded ticket
*
* @param base64EncodedTicket a base64 encoded service ticket
* @return an authentication token that can be used to connect to Neo4j
* @throws NullPointerException when ticket is {@code null}
* @see GraphDatabase#driver(String, AuthToken)
* @since 1.3
* @throws NullPointerException when ticket is {@code null}
*/
public static AuthToken kerberos( String base64EncodedTicket )
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.neo4j.driver.exceptions;

/**
* The provided token has expired.
* <p>
* The current driver instance is considered invalid. It should not be used anymore. The client must create a new driver instance with a valid token.
* <p>
* Error code: Neo.ClientError.Security.TokenExpired
*/
public class TokenExpiredException extends SecurityException
{
public TokenExpiredException( String code, String message )
{
super( code, message );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.neo4j.driver.exceptions.Neo4jException;
import org.neo4j.driver.exceptions.ResultConsumedException;
import org.neo4j.driver.exceptions.ServiceUnavailableException;
import org.neo4j.driver.exceptions.TokenExpiredException;
import org.neo4j.driver.exceptions.TransientException;

public final class ErrorUtil
Expand Down Expand Up @@ -80,6 +81,10 @@ else if ( code.equalsIgnoreCase( "Neo.ClientError.Security.AuthorizationExpired"
{
return new AuthorizationExpiredException( code, message );
}
else if ( code.equalsIgnoreCase( "Neo.ClientError.Security.TokenExpired" ) )
{
return new TokenExpiredException( code, message );
}
else
{
return new ClientException( code, message );
Expand Down
26 changes: 23 additions & 3 deletions driver/src/test/java/org/neo4j/driver/AuthTokensTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
import java.util.HashMap;
import java.util.Map;

import org.neo4j.driver.AuthToken;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Value;
import org.neo4j.driver.internal.security.InternalAuthToken;
import org.neo4j.driver.internal.value.ListValue;
import org.neo4j.driver.internal.value.MapValue;
Expand Down Expand Up @@ -103,6 +100,22 @@ void customAuthParameters()
assertThat( map.get( "parameters" ), equalTo( (Value) new MapValue( expectedParameters ) ) );
}

@Test
void shouldSupportBearerAuth()
{
// GIVEN
String tokenStr = "token";

// WHEN
InternalAuthToken token = (InternalAuthToken) AuthTokens.bearer( tokenStr );

// THEN
Map<String,Value> map = token.toMap();
assertThat( map.size(), equalTo( 2 ) );
assertThat( map.get( "scheme" ), equalTo( new StringValue( "bearer" ) ) );
assertThat( map.get( "credentials" ), equalTo( new StringValue( tokenStr ) ) );
}

@Test
void basicKerberosAuthWithRealm()
{
Expand Down Expand Up @@ -141,6 +154,13 @@ void shouldAllowBasicAuthTokenWithNullRealm()
assertEquals( "password", map.get( "credentials" ).asString() );
}

@Test
void shouldNotAllowBearerAuthTokenWithNullToken()
{
NullPointerException e = assertThrows( NullPointerException.class, () -> AuthTokens.bearer( null ) );
assertEquals( "Token can't be null", e.getMessage() );
}

@Test
void shouldNotAllowKerberosAuthTokenWithNullTicket()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.neo4j.driver.exceptions.DatabaseException;
import org.neo4j.driver.exceptions.Neo4jException;
import org.neo4j.driver.exceptions.ServiceUnavailableException;
import org.neo4j.driver.exceptions.TokenExpiredException;
import org.neo4j.driver.exceptions.TransientException;

import static org.hamcrest.Matchers.containsString;
Expand Down Expand Up @@ -175,4 +176,17 @@ void shouldCreateAuthorizationExpiredException()
assertEquals( code, error.code() );
assertEquals( message, error.getMessage() );
}

@Test
void shouldCreateTokenExpiredException()
{
String code = "Neo.ClientError.Security.TokenExpired";
String message = "message";

Neo4jException error = newNeo4jError( code, message );

assertThat( error, instanceOf( TokenExpiredException.class ) );
assertEquals( code, error.code() );
assertEquals( message, error.getMessage() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public class GetFeatures implements TestkitRequest
"AuthorizationExpiredTreatment",
"ConfHint:connection.recv_timeout_seconds",
"Temporary:DriverFetchSize",
"Temporary:DriverMaxTxRetryTime"
"Temporary:DriverMaxTxRetryTime",
"Feature:Auth:Bearer"
) );

private static final Set<String> SYNC_FEATURES = new HashSet<>( Arrays.asList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ public TestkitResponse process( TestkitState testkitState )
data.authorizationToken.getTokens().get( "credentials" ),
data.authorizationToken.getTokens().get( "realm" ) );
break;
case "bearer":
authToken = AuthTokens.bearer( data.authorizationToken.getTokens().get( "credentials" ) );
break;
default:
return BackendError.builder()
.data( BackendError
Expand Down