mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Refactor auth infrastructure naming
This commit is contained in:
parent
3fe7848c4e
commit
e84123c20a
@ -10,23 +10,25 @@ import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
||||
import static io.grpc.Status.UNAUTHENTICATED;
|
||||
|
||||
/**
|
||||
* Simple credentials implementation for sending cleartext username:password token via rpc call headers.
|
||||
* Sets the {@value AUTH_HEADER_KEY} rpc call header to a given value.
|
||||
*/
|
||||
public class BisqCallCredentials extends CallCredentials {
|
||||
public class AuthHeaderCallCredentials extends CallCredentials {
|
||||
|
||||
private final String apiToken;
|
||||
public static final String AUTH_HEADER_KEY = "authorization";
|
||||
|
||||
public BisqCallCredentials(String apiToken) {
|
||||
this.apiToken = apiToken;
|
||||
private final String authHeaderValue;
|
||||
|
||||
public AuthHeaderCallCredentials(String authHeaderValue) {
|
||||
this.authHeaderValue = authHeaderValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier metadataApplier) {
|
||||
appExecutor.execute(() -> {
|
||||
try {
|
||||
Metadata headers = new Metadata();
|
||||
Key<String> apiTokenKey = Key.of("bisq-api-token", ASCII_STRING_MARSHALLER);
|
||||
headers.put(apiTokenKey, apiToken);
|
||||
var headers = new Metadata();
|
||||
var authorizationKey = Key.of(AUTH_HEADER_KEY, ASCII_STRING_MARSHALLER);
|
||||
headers.put(authorizationKey, authHeaderValue);
|
||||
metadataApplier.apply(headers);
|
||||
} catch (Throwable ex) {
|
||||
metadataApplier.fail(UNAUTHENTICATED.withCause(ex));
|
@ -61,12 +61,12 @@ public class BisqCliMain {
|
||||
.withRequiredArg()
|
||||
.defaultsTo("localhost");
|
||||
|
||||
var portOpt = parser.accepts("port", "Bisq node RPC port")
|
||||
var portOpt = parser.accepts("port", "Bisq node rpc port")
|
||||
.withRequiredArg()
|
||||
.ofType(Integer.class)
|
||||
.defaultsTo(9998);
|
||||
|
||||
var authOpt = parser.accepts("auth", "Bisq node RPC authentication token")
|
||||
var passwordOpt = parser.accepts("password", "Bisq node rpc server password")
|
||||
.withRequiredArg();
|
||||
|
||||
var options = parser.parse(args);
|
||||
@ -89,9 +89,9 @@ public class BisqCliMain {
|
||||
var host = options.valueOf(hostOpt);
|
||||
var port = options.valueOf(portOpt);
|
||||
|
||||
var authToken = options.valueOf(authOpt);
|
||||
if (authToken == null) {
|
||||
err.println("error: rpc authentication token must not be null");
|
||||
var password = options.valueOf(passwordOpt);
|
||||
if (password == null) {
|
||||
err.println("error: rpc password must not be null");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -103,7 +103,7 @@ public class BisqCliMain {
|
||||
}
|
||||
|
||||
var channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
|
||||
var credentials = new BisqCallCredentials(authToken);
|
||||
var credentials = new AuthHeaderCallCredentials(password);
|
||||
|
||||
var command = nonOptionArgs.get(0);
|
||||
|
||||
|
@ -116,7 +116,7 @@ public class Config {
|
||||
public static final String DAO_ACTIVATED = "daoActivated";
|
||||
public static final String DUMP_DELAYED_PAYOUT_TXS = "dumpDelayedPayoutTxs";
|
||||
public static final String ALLOW_FAULTY_DELAYED_TXS = "allowFaultyDelayedTxs";
|
||||
public static final String API_TOKEN = "apiToken";
|
||||
public static final String API_PASSWORD = "apiPassword";
|
||||
|
||||
// Default values for certain options
|
||||
public static final int UNSPECIFIED_PORT = -1;
|
||||
@ -200,7 +200,7 @@ public class Config {
|
||||
public final long genesisTotalSupply;
|
||||
public final boolean dumpDelayedPayoutTxs;
|
||||
public final boolean allowFaultyDelayedTxs;
|
||||
public final String apiToken;
|
||||
public final String apiPassword;
|
||||
|
||||
// Properties derived from options but not exposed as options themselves
|
||||
public final File torDir;
|
||||
@ -617,8 +617,8 @@ public class Config {
|
||||
.ofType(boolean.class)
|
||||
.defaultsTo(false);
|
||||
|
||||
ArgumentAcceptingOptionSpec<String> apiTokenOpt =
|
||||
parser.accepts(API_TOKEN, "Bisq gRPC API authentication token")
|
||||
ArgumentAcceptingOptionSpec<String> apiPasswordOpt =
|
||||
parser.accepts(API_PASSWORD, "gRPC API password")
|
||||
.withRequiredArg()
|
||||
.defaultsTo("");
|
||||
|
||||
@ -734,7 +734,7 @@ public class Config {
|
||||
this.daoActivated = options.valueOf(daoActivatedOpt);
|
||||
this.dumpDelayedPayoutTxs = options.valueOf(dumpDelayedPayoutTxsOpt);
|
||||
this.allowFaultyDelayedTxs = options.valueOf(allowFaultyDelayedTxsOpt);
|
||||
this.apiToken = options.valueOf(apiTokenOpt);
|
||||
this.apiPassword = options.valueOf(apiPasswordOpt);
|
||||
} catch (OptionException ex) {
|
||||
throw new ConfigException("problem parsing option '%s': %s",
|
||||
ex.options().get(0),
|
||||
|
@ -0,0 +1,48 @@
|
||||
package bisq.core.grpc;
|
||||
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.ServerCall;
|
||||
import io.grpc.ServerCallHandler;
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
||||
import static io.grpc.Metadata.Key;
|
||||
import static io.grpc.Status.UNAUTHENTICATED;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Authorizes rpc server calls by comparing the value of the caller's
|
||||
* {@value AUTH_HEADER_KEY} header to an expected value set at server startup time.
|
||||
*
|
||||
* @see bisq.common.config.Config#apiPassword
|
||||
*/
|
||||
@Slf4j
|
||||
public class AuthorizationInterceptor implements ServerInterceptor {
|
||||
|
||||
public static final String AUTH_HEADER_KEY = "authorization";
|
||||
|
||||
private final String expectedAuthHeaderValue;
|
||||
|
||||
public AuthorizationInterceptor(String expectedAuthHeaderValue) {
|
||||
this.expectedAuthHeaderValue = expectedAuthHeaderValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata headers,
|
||||
ServerCallHandler<ReqT, RespT> serverCallHandler) {
|
||||
var actualAuthHeaderValue = headers.get(Key.of(AUTH_HEADER_KEY, ASCII_STRING_MARSHALLER));
|
||||
|
||||
if (actualAuthHeaderValue == null)
|
||||
throw new StatusRuntimeException(UNAUTHENTICATED.withDescription(
|
||||
format("missing '%s' rpc header value", AUTH_HEADER_KEY)));
|
||||
|
||||
if (!actualAuthHeaderValue.equals(expectedAuthHeaderValue))
|
||||
throw new StatusRuntimeException(UNAUTHENTICATED.withDescription(
|
||||
format("incorrect '%s' rpc header value", AUTH_HEADER_KEY)));
|
||||
|
||||
return serverCallHandler.startCall(serverCall, headers);
|
||||
}
|
||||
}
|
@ -215,7 +215,7 @@ public class BisqGrpcServer {
|
||||
.addService(new GetPaymentAccountsImpl())
|
||||
.addService(new PlaceOfferImpl())
|
||||
.addService(new StopServerImpl())
|
||||
.intercept(new TokenAuthInterceptor(config.apiToken))
|
||||
.intercept(new AuthorizationInterceptor(config.apiPassword))
|
||||
.build()
|
||||
.start();
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
package bisq.core.grpc;
|
||||
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.ServerCall;
|
||||
import io.grpc.ServerCallHandler;
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
||||
import static io.grpc.Metadata.Key;
|
||||
import static io.grpc.Status.UNAUTHENTICATED;
|
||||
|
||||
/**
|
||||
* Simple authentication interceptor to validate a cleartext token in username:password format.
|
||||
*/
|
||||
@Slf4j
|
||||
public class TokenAuthInterceptor implements ServerInterceptor {
|
||||
|
||||
private final String apiToken;
|
||||
|
||||
public TokenAuthInterceptor(String apiToken) {
|
||||
this.apiToken = apiToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata,
|
||||
ServerCallHandler<ReqT, RespT> serverCallHandler) {
|
||||
authenticate(metadata.get(Key.of("bisq-api-token", ASCII_STRING_MARSHALLER)));
|
||||
return serverCallHandler.startCall(serverCall, metadata);
|
||||
}
|
||||
|
||||
private void authenticate(String authToken) {
|
||||
if (authToken == null)
|
||||
throw new StatusRuntimeException(UNAUTHENTICATED.withDescription("API token is missing"));
|
||||
|
||||
if (!authToken.equals(apiToken))
|
||||
throw new StatusRuntimeException(UNAUTHENTICATED.withDescription("Invalid API token"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user