Skip to content

Commit

Permalink
Added a static string based auth handler
Browse files Browse the repository at this point in the history
Added tests
Updated jwt and creds for auth tests
Fixed optional auth tests to now run with nats-server
Fixes #261
  • Loading branch information
Stephen Asbury committed Aug 23, 2019
1 parent 80eee58 commit 427bb71
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 24 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@

## Version 2.6.1

* [FIXED] - #263 - Added server URLs to connect exception (not to auth exception)
* [FIXED] - #262 - Added @deprecated as needed
* [FIXED] - #260 - moved to nats-server from gnatsd for testing
* [FIXED] - #261 - Added a static credentials implementation that uses char arrays
* [FIXED] - #260 - Moved to nats-server from gnatsd for testing
* [FIXED] - #257 - Added connection method to messages that come from subscriptions, dispatchers and requests
* [FIXED] - #243 - Added check for whitespace in subjects and queue names
* [FIXED] - A couple flaky tests

## Version 2.6.0

Expand Down
31 changes: 22 additions & 9 deletions src/main/java/io/nats/client/AuthHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,35 @@
*
* <pre>
* {@code
public String getID() {
char[] nkey;
char[] jwt;
public byte[] sign(byte[] nonce) {
try {
return this.nkey.getPublicKey();
} catch (GeneralSecurityException|IOException ex) {
return null;
NKey nkey = NKey.fromSeed(this.nkey);
byte[] sig = nkey.sign(nonce);
nkey.clear();
return sig;
} catch (Exception exp) {
throw new IllegalStateException("problem signing nonce", exp);
}
}
public byte[] sign(byte[] nonce) {
public char[] getID() {
try {
return this.nkey.sign(nonce);
} catch (GeneralSecurityException|IOException ex) {
return null;
NKey nkey = NKey.fromSeed(this.nkey);
char[] pubKey = nkey.getPublicKey();
nkey.clear();
return pubKey;
} catch (Exception exp) {
throw new IllegalStateException("problem getting public key", exp);
}
}
}
public char[] getJWT() {
return this.jwt;
}
}
* </pre>
*/
public interface AuthHandler {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/io/nats/client/Nats.java
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,18 @@ public static AuthHandler credentials(String jwtFile, String nkeyFile) {
return NatsImpl.credentials(jwtFile, nkeyFile);
}

/**
* Create an auth handler from an nkey and an option JWT. This credentials object is static, and will not change
* over the course of its lifetime. Create a custom AuthHandler or use the file-based handler for dynamic credentials.
*
* @param jwt the contents of a user JWT file (optional if nkey authentication is being used)
* @param nkey an nkey seed
* @return an authhandler that will return the JWT, public key or sign a nonce appropriately
*/
public static AuthHandler staticCredentials(char[] jwt, char[] nkey) {
return NatsImpl.staticCredentials(jwt, nkey);
}

private static Connection createConnection(Options options, boolean reconnectOnConnect)
throws IOException, InterruptedException {
return NatsImpl.createConnection(options, reconnectOnConnect);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/io/nats/client/impl/NatsImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ public static AuthHandler credentials(String credsFile) {
public static AuthHandler credentials(String jwtFile, String nkeyFile) {
return new FileAuthHandler(jwtFile, nkeyFile);
}

public static AuthHandler staticCredentials(char[] jwt, char[] nkey) {
return new StringAuthHandler(jwt, nkey);
}
}
73 changes: 73 additions & 0 deletions src/main/java/io/nats/client/impl/StringAuthHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2018 The NATS Authors
// 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 io.nats.client.impl;

import io.nats.client.AuthHandler;
import io.nats.client.NKey;

class StringAuthHandler implements AuthHandler {
private char[] nkey;
private char[] jwt;

StringAuthHandler(char[] jwt, char[] nkey) {
this.jwt = jwt;
this.nkey = nkey;
}

/**
* Sign is called by the library when the server sends a nonce.
* The client's NKey should be used to sign the provided value.
*
* @param nonce the nonce to sign
* @return the signature for the nonce
*/
public byte[] sign(byte[] nonce) {
try {
NKey nkey = NKey.fromSeed(this.nkey);
byte[] sig = nkey.sign(nonce);
nkey.clear();
return sig;
} catch (Exception exp) {
throw new IllegalStateException("problem signing nonce", exp);
}
}

/**
* getID should return a public key associated with a client key known to the server.
* If the server is not in nonce-mode, this array can be empty.
*
* @return the public key as a char array
*/
public char[] getID() {
try {
NKey nkey = NKey.fromSeed(this.nkey);
char[] pubKey = nkey.getPublicKey();
nkey.clear();
return pubKey;
} catch (Exception exp) {
throw new IllegalStateException("problem getting public key", exp);
}
}

/**
* getJWT should return the user JWT associated with this connection.
* This can return null for challenge only authentication, but for account/user
* JWT-based authentication you need to return the JWT bytes here.
*
* @return the user JWT
*/
public char[] getJWT() {
return this.jwt;
}
}
65 changes: 60 additions & 5 deletions src/test/java/io/nats/client/AuthTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ public void testNKeyAuth() throws Exception {
String configFile = createNKeyConfigFile(theKey.getPublicKey());
String version = NatsTestServer.generateNatsServerVersionString();

if (!version.contains("version 2")) {
if (!version.contains("v2")) {
// Server version doesn't support this test
return;
}
Expand All @@ -494,18 +494,44 @@ public void testNKeyAuth() throws Exception {
}

@Test
public void testJWTAuthWithCredsFile() throws Exception {
public void testStaticNKeyAuth() throws Exception {
NKey theKey = NKey.createUser(null);
assertNotNull(theKey);

String configFile = createNKeyConfigFile(theKey.getPublicKey());
String version = NatsTestServer.generateNatsServerVersionString();

if (!version.contains("version 2")) {
if (!version.contains("v2")) {
// Server version doesn't support this test
return;
}

try (NatsTestServer ts = new NatsTestServer("src/test/resources/operator.conf", true)) {
try (NatsTestServer ts = new NatsTestServer(configFile, false)) {
Options options = new Options.Builder().
server(ts.getURI()).
maxReconnects(0).
authHandler(Nats.staticCredentials(null, theKey.getSeed())).
build();
Connection nc = Nats.connect(options);
try {
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
} finally {
nc.close();
assertTrue("Closed Status", Connection.Status.CLOSED == nc.getStatus());
}
}
}

@Test
public void testJWTAuthWithCredsFile() throws Exception {
String version = NatsTestServer.generateNatsServerVersionString();

if (!version.contains("v2")) {
// Server version doesn't support this test
return;
}

try (NatsTestServer ts = new NatsTestServer("src/test/resources/operator.conf", false)) {
Options options = new Options.Builder().
server(ts.getURI()).
maxReconnects(0).
Expand All @@ -521,6 +547,35 @@ public void testJWTAuthWithCredsFile() throws Exception {
}
}

@Test
public void testStaticJWTAuth() throws Exception {
String version = NatsTestServer.generateNatsServerVersionString();

if (!version.contains("v2")) {
// Server version doesn't support this test
return;
}

// from src/test/resources/jwt_nkey/user.creds
String jwt = "eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiI3UE1GTkc0R1c1WkE1NEg3N09TUUZKNkJNQURaSUQ2NTRTVk1XMkRFQVZINVIyUVU0MkhBIiwiaWF0IjoxNTY1ODg5ODk4LCJpc3MiOiJBQUhWU0k1NVlQTkJRWjVQN0Y2NzZDRkFPNFBIQlREWUZRSUVHVEtMUVRJUEVZUEZEVEpOSEhPNCIsIm5hbWUiOiJkZW1vIiwic3ViIjoiVUMzT01MSlhUWVBZN0ZUTVVZNUNaNExHRVdRSTNZUzZKVFZXU0VGRURBMk9MTEpZSVlaVFo3WTMiLCJ0eXBlIjoidXNlciIsIm5hdHMiOnsicHViIjp7fSwic3ViIjp7fX19.ROSJ7D9ETt9c8ZVHxsM4_FU2dBRLh5cNfb56MxPQth74HAxxtGMl0nn-9VVmWjXgFQn4JiIbwrGfFDBRMzxsAA";
String nkey = "SUAFYHVVQVOIDOOQ4MTOCTLGNZCJ5PZ4HPV5WAPROGTEIOF672D3R7GBY4";

try (NatsTestServer ts = new NatsTestServer("src/test/resources/operator.conf", false)) {
Options options = new Options.Builder().
server(ts.getURI()).
maxReconnects(0).
authHandler(Nats.staticCredentials(jwt.toCharArray(), nkey.toCharArray())).
build();
Connection nc = Nats.connect(options);
try {
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
} finally {
nc.close();
assertTrue("Closed Status", Connection.Status.CLOSED == nc.getStatus());
}
}
}

@Test(expected=IOException.class)
public void testBadAuthHandler() throws Exception {
NKey theKey = NKey.createUser(null);
Expand All @@ -529,7 +584,7 @@ public void testBadAuthHandler() throws Exception {
String configFile = createNKeyConfigFile(theKey.getPublicKey());
String version = NatsTestServer.generateNatsServerVersionString();

if (!version.contains("version 2")) {
if (!version.contains("v2")) {
// Server version doesn't support this test
throw new IOException();// to pass the test
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/resources/jwt_nkey/op.jwt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
-----BEGIN TEST OPERATOR JWT-----
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJXSFBHWURTQkVOS1Q3REZJWVFWSFlJMjNLWVVJTEVRN0JOSkFYTktHVVZCRlNaR0lSM1BRIiwiaWF0IjoxNTQzOTU1MjUzLCJpc3MiOiJPRFNEUFhUSjZLNUxYREoyT0FEV0laM0haMkhTN08yU0lDVE1DT05MM0ZJN0VVRklZTzdOV1JOQyIsIm5hbWUiOiJzeW5hZGlhX29wZXJhdG9yIiwic3ViIjoiT0RTRFBYVEo2SzVMWERKMk9BRFdJWjNIWjJIUzdPMlNJQ1RNQ09OTDNGSTdFVUZJWU83TldSTkMiLCJ0eXBlIjoib3BlcmF0b3IifQ.kbbUr4zZ7oSsTW3zqBTpRNsxH-DewiczmJjf2jNLth7Zxj2g4c3CKqnT9O5Cb3f8VtaJo6bpjfJ6Am-U2uMUAw
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJKV01TUzNRUFpDS0lHSE1BWko3RUpQSlVHN01DTFNQUkJaTEpSUUlRQkRVTkFaUE5MQVVBIiwiaWF0IjoxNTY1ODg5NzEyLCJpc3MiOiJPQU01VlNINDJXRlZWTkpXNFNMRTZRVkpCREpVRTJGUVNYWkxRTk1SRDdBMlBaTTIzTDIyWFlVWSIsIm5hbWUiOiJzeW5hZGlhIiwic3ViIjoiT0FNNVZTSDQyV0ZWVk5KVzRTTEU2UVZKQkRKVUUyRlFTWFpMUU5NUkQ3QTJQWk0yM0wyMlhZVVkiLCJ0eXBlIjoib3BlcmF0b3IiLCJuYXRzIjp7ImFjY291bnRfc2VydmVyX3VybCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NjA2MC9qd3QvdjEiLCJvcGVyYXRvcl9zZXJ2aWNlX3VybHMiOlsibmF0czovL2xvY2FsaG9zdDo0MTQxIl19fQ.XPvAezQj3AxwEvYLVBq-EIssP4OhjoMGLbIaripzBKv1oCtHdPNKz96YwB2vUoY-4OrN9ZOPo9TKR3jVxq0uBQ
------END TEST OPERATOR JWT------
7 changes: 2 additions & 5 deletions src/test/resources/jwt_nkey/user.creds
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
-----BEGIN NATS USER JWT-----
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJJSEczV1JJMzZVUUVHQUZNMkxVN0tWUVozWVVVTVpYWEE0VUpHVUNLUlpVTE9VUzNSTDNRIiwiaWF0IjoxNTQzOTYzNzM4LCJpc3MiOiJBQ0g0N1ZMQVdXM0dQS0tVSEhYN0hLSklEQzVQTVVRVjY2QkdBR0E0UlVLMlpHNTdKNEdRVzNQWSIsIm5hbWUiOiJ0ZXN0Iiwic3ViIjoiVUNQSE0yV0lHSERFRENHQTdNWkxYR1BFSVEzUlJPUk5PQ0xHWk1XT0RVMlVZRFNFQ0VZS1Y2NDYiLCJ0eXBlIjoidXNlciIsIm5hdHMiOnsicHViIjp7fSwic3ViIjp7fX19.IjDhjHH4_CaOpfa2G-japnyzrqy0Uo4bHDNkM0wm3-lwhOrrUy_TLGekH1BlhdBTFpJzMB3_REleRXmElswrDw
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiI3UE1GTkc0R1c1WkE1NEg3N09TUUZKNkJNQURaSUQ2NTRTVk1XMkRFQVZINVIyUVU0MkhBIiwiaWF0IjoxNTY1ODg5ODk4LCJpc3MiOiJBQUhWU0k1NVlQTkJRWjVQN0Y2NzZDRkFPNFBIQlREWUZRSUVHVEtMUVRJUEVZUEZEVEpOSEhPNCIsIm5hbWUiOiJkZW1vIiwic3ViIjoiVUMzT01MSlhUWVBZN0ZUTVVZNUNaNExHRVdRSTNZUzZKVFZXU0VGRURBMk9MTEpZSVlaVFo3WTMiLCJ0eXBlIjoidXNlciIsIm5hdHMiOnsicHViIjp7fSwic3ViIjp7fX19.ROSJ7D9ETt9c8ZVHxsM4_FU2dBRLh5cNfb56MxPQth74HAxxtGMl0nn-9VVmWjXgFQn4JiIbwrGfFDBRMzxsAA
------END NATS USER JWT------

************************* IMPORTANT *************************
NKEY Seed printed below can be used to sign and prove identity.
NKEYs are sensitive and should be treated as secrets.

-----BEGIN USER NKEY SEED-----
SUAI3GND23IWH6RFCQTJFYYUTSPKIXWV42YGKJ7WIY6ZB2KOZVXPDFRIVY
SUAFYHVVQVOIDOOQ4MTOCTLGNZCJ5PZ4HPV5WAPROGTEIOF672D3R7GBY4
------END USER NKEY SEED------

*************************************************************
6 changes: 3 additions & 3 deletions src/test/resources/operator.conf
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ port: 22222

operator = "src/test/resources/jwt_nkey/op.jwt"

system_account = "ACH47VLAWW3GPKKUHHX7HKJIDC5PMUQV66BGAGA4RUK2ZG57J4GQW3PY"
system_account = "ADRNVKNXUYQGTX5AXSPYRKAO427VMF6JG3UDE2OROYP3XRVNQ3GT3BZU"

# This is for account resolution.
# Can be MEMORY (Testing) or can be URL(url).
Expand All @@ -19,8 +19,8 @@ resolver = MEMORY

# This is a map that can preload keys:jwts into a memory resolver.
resolver_preload = {
ACH47VLAWW3GPKKUHHX7HKJIDC5PMUQV66BGAGA4RUK2ZG57J4GQW3PY : "eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiIzWlVTRTJCT0NNQUFRVFlORDNQQUtIQkFEWjVCU1M3VFBDWkdDSzRZMkxENEJTWDRWU1hRIiwiaWF0IjoxNTQzOTU1MjUzLCJpc3MiOiJPRFNEUFhUSjZLNUxYREoyT0FEV0laM0haMkhTN08yU0lDVE1DT05MM0ZJN0VVRklZTzdOV1JOQyIsIm5hbWUiOiJzeW5hZGlhX2FjY291bnQiLCJzdWIiOiJBQ0g0N1ZMQVdXM0dQS0tVSEhYN0hLSklEQzVQTVVRVjY2QkdBR0E0UlVLMlpHNTdKNEdRVzNQWSIsInR5cGUiOiJhY2NvdW50IiwibmF0cyI6eyJsaW1pdHMiOnt9fX0.cEPEApFLQtljoLv_57_xFhlSEl7AXT523Wxr_WQJef093kBz-lHInS5T9pawLbuS22p048K8EoUTjfkNLgpQBg"
ADRNVKNXUYQGTX5AXSPYRKAO427VMF6JG3UDE2OROYP3XRVNQ3GT3BZU : "eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJUTjRYUFhNV01JMjJQN1dPWVM1TE1FV0lPWDJJMkJOVUY2VlRQMklYV0RCRTVTVTJHU1dRIiwiaWF0IjoxNTY1ODg5OTk4LCJpc3MiOiJPQU01VlNINDJXRlZWTkpXNFNMRTZRVkpCREpVRTJGUVNYWkxRTk1SRDdBMlBaTTIzTDIyWFlVWSIsIm5hbWUiOiJzeXN0ZW0iLCJzdWIiOiJBRFJOVktOWFVZUUdUWDVBWFNQWVJLQU80MjdWTUY2SkczVURFMk9ST1lQM1hSVk5RM0dUM0JaVSIsInR5cGUiOiJhY2NvdW50IiwibmF0cyI6eyJsaW1pdHMiOnsic3VicyI6LTEsImNvbm4iOi0xLCJsZWFmIjotMSwiaW1wb3J0cyI6LTEsImV4cG9ydHMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsIndpbGRjYXJkcyI6dHJ1ZX19fQ.Zlz9PN5Fnw2etIFaLXF4YiWS7tA4k22oTwaGxDdgXh8fpA1RmPVKHiJGCMoQidmtHC5C5munhtjhFV7wF44vBg"

AC35RP2NS4Z3LXOJSAUBEJ6J522SCHQMCUE63YASTANNPYGV7XSBDLNJ: "eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiI2MlJPSEJDSlVQR0ZLV1dWUFlQUkZHSlFTNTRVRlY1SDdPRjVYVFc1SjZYQkpCRDI1REJBIiwiaWF0IjoxNTQzOTYzNTM2LCJpc3MiOiJPRFNEUFhUSjZLNUxYREoyT0FEV0laM0haMkhTN08yU0lDVE1DT05MM0ZJN0VVRklZTzdOV1JOQyIsIm5hbWUiOiJ0ZXN0Iiwic3ViIjoiQUMzNVJQMk5TNFozTFhPSlNBVUJFSjZKNTIyU0NIUU1DVUU2M1lBU1RBTk5QWUdWN1hTQkRMTkoiLCJ0eXBlIjoiYWNjb3VudCIsIm5hdHMiOnsibGltaXRzIjp7fX19.6sRktl3w-kf8uxV0FH9eaHFPu5iNDbDxBBrJPVK9FGjQCP39gVCH8XAN5kmlk0z9hQNWgGZcBsJRZkPl39YnBQ"
AAHVSI55YPNBQZ5P7F676CFAO4PHBTDYFQIEGTKLQTIPEYPFDTJNHHO4: "eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJJWEdFSDNGQ1NVTkxGNDZUVFUzVlBPNVZMMkhUVlNLM003TU1VS09FUk4zUExJWlkzTk5RIiwiaWF0IjoxNTY1ODg5OTEwLCJpc3MiOiJPQU01VlNINDJXRlZWTkpXNFNMRTZRVkpCREpVRTJGUVNYWkxRTk1SRDdBMlBaTTIzTDIyWFlVWSIsIm5hbWUiOiJkZW1vIiwic3ViIjoiQUFIVlNJNTVZUE5CUVo1UDdGNjc2Q0ZBTzRQSEJURFlGUUlFR1RLTFFUSVBFWVBGRFRKTkhITzQiLCJ0eXBlIjoiYWNjb3VudCIsIm5hdHMiOnsiZXhwb3J0cyI6W3sibmFtZSI6ImNyb24iLCJzdWJqZWN0IjoiY3Jvbi5cdTAwM2UiLCJ0eXBlIjoic3RyZWFtIn0seyJuYW1lIjoibGlnbyIsInN1YmplY3QiOiJsaWdvIiwidHlwZSI6InNlcnZpY2UifV0sImxpbWl0cyI6eyJzdWJzIjotMSwiY29ubiI6LTEsImxlYWYiOi0xLCJpbXBvcnRzIjotMSwiZXhwb3J0cyI6LTEsImRhdGEiOi0xLCJwYXlsb2FkIjotMSwid2lsZGNhcmRzIjp0cnVlfX19.oo6CZHPBKCRyz3NQEZK8Xi6ic_4Vb5kIw-cSoSdDwT8T97EvIIZ-ie8MupQgNVinq68zSr2SzCEfTPVkyW84AA"

}

0 comments on commit 427bb71

Please sign in to comment.