Skip to content

Commit

Permalink
Support for custom Authenticator
Browse files Browse the repository at this point in the history
implement: #132

Change-Id: Ib31a99ebc6d9c2ec3169d066ff179860b9cc6e25
  • Loading branch information
javeme authored and zhoney committed Nov 21, 2018
1 parent 94dc821 commit 7860796
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@
import javax.ws.rs.ext.Provider;
import javax.xml.bind.DatatypeConverter;

import org.apache.tinkerpop.gremlin.server.auth.AuthenticationException;
import org.glassfish.grizzly.utils.Charsets;

import com.baidu.hugegraph.auth.HugeAuthenticator.User;
import com.baidu.hugegraph.auth.StandardAuthenticator;
import com.baidu.hugegraph.core.GraphManager;
import com.baidu.hugegraph.util.E;
import com.google.common.collect.ImmutableMap;

@Provider
@PreMatching
Expand All @@ -64,16 +67,15 @@ protected User authenticate(ContainerRequestContext context) {

if (!manager.requireAuthentication()) {
// Return anonymous user with admin role if disable authentication
final String username = StandardAuthenticator.USER_ANONYMOUS;
final String role = StandardAuthenticator.ROLE_ADMIN;
return new User(username, role);
return User.ANONYMOUS;
}

// Extract authentication credentials
String auth = context.getHeaderString(HttpHeaders.AUTHORIZATION);
if (auth == null) {
throw new NotAuthorizedException(
"Authentication credentials are required");
"Authentication credentials are required",
"Missing authentication credentials");
}
if (!auth.startsWith("Basic ")) {
throw new BadRequestException(
Expand All @@ -94,12 +96,15 @@ protected User authenticate(ContainerRequestContext context) {
assert username != null && password != null;

// Validate the extracted credentials
final String role = manager.authenticate(username, password);
if (!StandardAuthenticator.verifyRole(role)) {
throw new NotAuthorizedException("Invalid username or password");
try {
return manager.authenticate(ImmutableMap.of(
StandardAuthenticator.KEY_USERNAME, username,
StandardAuthenticator.KEY_PASSWORD, password));
} catch (AuthenticationException e) {
String msg = String.format("Authentication failed for user '%s'",
username);
throw new NotAuthorizedException(msg, e.getMessage());
}

return new User(username, role);
}

public static class Authorizer implements SecurityContext {
Expand All @@ -117,11 +122,11 @@ public Authorizer(final User user, final UriInfo uri) {
}

public String username() {
return this.user.username;
return this.user.username();
}

public String userrole() {
return this.user.role;
public String role() {
return this.user.role();
}

@Override
Expand All @@ -140,7 +145,7 @@ public boolean isUserInRole(String role) {
E.checkState(owner.length == 2, "Bad role format: '%s'", role);
role = this.getPathParameter(owner[1]);
}
return role.equals(this.user.role);
return role.equals(this.user.role());
}

@Override
Expand All @@ -164,7 +169,7 @@ private final class UserPrincipal implements Principal {

@Override
public String getName() {
return Authorizer.this.user.role;
return Authorizer.this.user.role();
}

@Override
Expand All @@ -183,37 +188,4 @@ public boolean equals(Object obj) {
}
}
}

protected static class User {

private String username;
private String role;

public User(String username, String role) {
this.username = username;
this.role = role;
}

@Override
public int hashCode() {
return this.username.hashCode() ^ this.role.hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof User)) {
return false;
}

User other = (User) obj;
return this.username.equals(other.username) &&
this.role.equals(other.role);
}

@Override
public String toString() {
return String.format("User{username=%s,role=%s}",
this.username, this.role);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
Expand Down Expand Up @@ -114,17 +115,18 @@ public static class WebApplicationExceptionMapper

@Override
public Response toResponse(WebApplicationException exception) {

Response response = exception.getResponse();
if (response.hasEntity()) {
return response;
}

MultivaluedMap<String, Object> headers = response.getHeaders();
boolean trace = this.trace(response.getStatus());
return Response.status(response.getStatus())
.type(MediaType.APPLICATION_JSON)
.entity(formatException(exception, trace))
.build();
response = Response.status(response.getStatus())
.type(MediaType.APPLICATION_JSON)
.entity(formatException(exception, trace))
.build();
response.getHeaders().putAll(headers);
return response;
}

private boolean trace(int status) {
Expand All @@ -150,15 +152,16 @@ public static String formatException(Exception exception) {
}

public static String formatException(Exception exception, boolean trace) {
String clazz = exception.getClass().toString();
String msg = exception.getMessage() != null ?
exception.getMessage() : "";
String cause = exception.getCause() != null ?
exception.getCause().toString() : "";

JsonObjectBuilder json = Json.createObjectBuilder()
.add("exception", exception.getClass().toString())
.add("message", msg)
.add("cause", cause);
.add("exception", clazz)
.add("message", msg)
.add("cause", cause);

if (trace) {
JsonArrayBuilder traces = Json.createArrayBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2017 HugeGraph Authors
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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 com.baidu.hugegraph.auth;

import java.util.Map;

import org.apache.tinkerpop.gremlin.server.auth.AuthenticatedUser;
import org.apache.tinkerpop.gremlin.server.auth.AuthenticationException;
import org.apache.tinkerpop.gremlin.server.auth.Authenticator;

import com.baidu.hugegraph.HugeException;
import com.baidu.hugegraph.config.HugeConfig;
import com.baidu.hugegraph.config.ServerOptions;

public interface HugeAuthenticator extends Authenticator {

public static final String ROLE_NONE = "";
public static final String ROLE_ADMIN = "admin";
public static final String ROLE_USER = "user";
public static final String ROLE_OWNER = "$owner";
public static final String ROLE_DYNAMIC = "$dynamic";

public void setup(HugeConfig config);

@Override
public User authenticate(final Map<String, String> credentials)
throws AuthenticationException;

@Override
public default boolean requireAuthentication() {
return true;
}

public static class User extends AuthenticatedUser {

protected static final String USER_ADMIN = ROLE_ADMIN;
protected static final String USER_ANONY = ANONYMOUS_USERNAME;

public static final User ADMIN = new User(USER_ADMIN, ROLE_ADMIN);
public static final User ANONYMOUS = new User(USER_ANONY, ROLE_ADMIN);

private final String role;

public User(String username, String role) {
super(username);
this.role = role;
}

public String username() {
return this.getName();
}

public String role() {
return this.role;
}

@Override
public boolean isAnonymous() {
return this == ANONYMOUS || this == ANONYMOUS_USER;
}

@Override
public int hashCode() {
return this.username().hashCode() ^ this.role().hashCode();
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof User)) {
return false;
}

User other = (User) obj;
return this.username().equals(other.username()) &&
this.role().equals(other.role());
}

@Override
public String toString() {
return String.format("User{username=%s,role=%s}",
this.username(), this.role());
}
}

public static HugeAuthenticator loadAuthenticator(HugeConfig conf) {
String authenticatorClass = conf.get(ServerOptions.AUTHENTICATOR);
ClassLoader cl = conf.getClass().getClassLoader();

HugeAuthenticator authenticator;
try {
authenticator = (HugeAuthenticator) cl.loadClass(authenticatorClass)
.newInstance();
} catch (Exception e) {
throw new HugeException("Failed to load authenticator: '%s'",
authenticatorClass, e);
}

authenticator.setup(conf);

return authenticator;
}
}
Loading

0 comments on commit 7860796

Please sign in to comment.