Skip to content

Commit 21c9265

Browse files
committed
solution: more flexible setup for credentials
1 parent fa1458d commit 21c9265

2 files changed

Lines changed: 147 additions & 32 deletions

File tree

emerald-api/src/main/java/io/emeraldpay/api/EmeraldConnection.java

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ public static class Builder {
7272
private Integer port;
7373
private boolean usePlaintext = false;
7474
private boolean useLoadBalancing = true;
75-
private boolean autoConnect = false;
76-
private boolean waitForAuth = false;
75+
private EmeraldCredentials.Builder credentials = EmeraldCredentials.newBuilder();
76+
private AuthInterceptor providedCredentials = null;
7777

7878
/**
7979
* Default Netty config allows messages up to 4Mb, but in practice Ethereum RPC responses may be larger. Here it allows up to 32Mb by default.
@@ -82,8 +82,6 @@ public static class Builder {
8282

8383
private Function<NettyChannelBuilder, ManagedChannelBuilder<?>> customChannel = null;
8484

85-
private String secretToken;
86-
8785
/**
8886
* Set target address as a host and port pair
8987
*
@@ -152,7 +150,10 @@ public Builder disableLoadBalancing() {
152150
* @return builder
153151
*/
154152
public Builder withAutoConnect() {
155-
this.autoConnect = true;
153+
if (providedCredentials != null) {
154+
throw new IllegalStateException("Cannot set configure credentials builder when credentials are already provided");
155+
}
156+
this.credentials = this.credentials.withAutoConnect();
156157
return this;
157158
}
158159

@@ -162,7 +163,10 @@ public Builder withAutoConnect() {
162163
* @return builder
163164
*/
164165
public Builder withWaitingForAuth() {
165-
this.waitForAuth = true;
166+
if (providedCredentials != null) {
167+
throw new IllegalStateException("Cannot set configure credentials builder when credentials are already provided");
168+
}
169+
this.credentials = this.credentials.withWaitingForAuth();
166170
return this;
167171
}
168172

@@ -184,7 +188,38 @@ public Builder maxMessageSize(Integer value) {
184188
* @return builder
185189
*/
186190
public Builder withAuthToken(String secret) {
187-
this.secretToken = secret;
191+
if (providedCredentials != null) {
192+
throw new IllegalStateException("Cannot set auth token when credentials are already provided");
193+
}
194+
this.credentials = this.credentials.withAuthToken(secret);
195+
return this;
196+
}
197+
198+
/**
199+
* Provided credentials builder.
200+
* Replaces the existing credentials builder or previously configured credentials.
201+
*
202+
* @param credentials credentials builder to use for authentication
203+
* @return builder
204+
*/
205+
public Builder withCredentials(EmeraldCredentials.Builder credentials) {
206+
if (credentials == null) {
207+
throw new IllegalArgumentException("Credentials builder cannot be null");
208+
}
209+
this.credentials = credentials;
210+
this.providedCredentials = null;
211+
return this;
212+
}
213+
214+
/**
215+
* Set an existing credentials interceptor to use for authentication, instead of building new ones from the credentials builder.
216+
* Can be set to null to use the credentials builder instead.
217+
*
218+
* @param credentials credentials interceptor to use for authentication
219+
* @return builder
220+
*/
221+
public Builder withCredentials(AuthInterceptor credentials) {
222+
this.providedCredentials = credentials;
188223
return this;
189224
}
190225

@@ -208,24 +243,6 @@ protected void initDefaults() {
208243
}
209244
}
210245

211-
/**
212-
* Setup the credentials instance
213-
*
214-
* @param channel the existing channel to use for authentication
215-
* @return credentials instance
216-
*/
217-
protected TokenCredentials getOrCreateCredentials(Channel channel) {
218-
TokenCredentials result = new TokenCredentials(secretToken, AuthGrpc.newBlockingStub(channel));
219-
if (autoConnect) {
220-
if (waitForAuth) {
221-
result.authSync();
222-
} else {
223-
result.authAsync();
224-
}
225-
}
226-
return result;
227-
}
228-
229246
/**
230247
* Build the API instance
231248
*
@@ -258,14 +275,14 @@ public EmeraldConnection build() {
258275
}
259276

260277
Channel channel = channelBuilder.build();
261-
262-
AuthInterceptor authInterceptor = null;
263-
if (secretToken != null) {
264-
if (usePlaintext) {
265-
System.err.println("WARNING: Authentication with a secret token over an unsecure plaintext connection.");
278+
AuthInterceptor authInterceptor = providedCredentials;
279+
if (authInterceptor == null) {
280+
if (credentials.hasToken()) {
281+
if (usePlaintext) {
282+
System.err.println("WARNING: Authentication with a secret token over an unsecure plaintext connection.");
283+
}
266284
}
267-
AuthHolder holder = new AuthHolder(getOrCreateCredentials(channel));
268-
authInterceptor = new AuthInterceptor(holder);
285+
authInterceptor = credentials.build(channel);
269286
}
270287

271288
return new EmeraldConnection(channel, authInterceptor);
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package io.emeraldpay.api;
2+
3+
import io.emeraldpay.api.proto.AuthGrpc;
4+
import io.emeraldpay.impl.AuthHolder;
5+
import io.emeraldpay.impl.AuthInterceptor;
6+
import io.emeraldpay.impl.TokenCredentials;
7+
import io.grpc.Channel;
8+
9+
/**
10+
* Credentials configuration for Emerald API authentication
11+
*/
12+
public class EmeraldCredentials {
13+
14+
public static EmeraldCredentials.Builder newBuilder() {
15+
return new Builder();
16+
}
17+
18+
public static class Builder {
19+
private boolean autoConnect = false;
20+
private boolean waitForAuth = false;
21+
private String secretToken;
22+
23+
/**
24+
* Authenticate on Emerald API using the provided secret token
25+
*
26+
* @param secret a token like `emrld_y40SYbbZclSZPX4r6nL9hNKUGaknAwqyv2qslI`
27+
* @return builder
28+
*/
29+
public EmeraldCredentials.Builder withAuthToken(String secret) {
30+
this.secretToken = secret;
31+
return this;
32+
}
33+
34+
/**
35+
* Automatically authenticate on Emerald API when the connection is built.
36+
* By default, it authenticates on the first request, which may cause delays.
37+
*
38+
* @return builder
39+
*/
40+
public EmeraldCredentials.Builder withAutoConnect() {
41+
this.autoConnect = true;
42+
return this;
43+
}
44+
45+
/**
46+
* When auto-connect is enabled, wait for authentication to complete before returning the EmeraldConnection instance.
47+
*
48+
* @return builder
49+
*/
50+
public EmeraldCredentials.Builder withWaitingForAuth() {
51+
this.waitForAuth = true;
52+
return this;
53+
}
54+
55+
/**
56+
* Setup the credentials instance
57+
*
58+
* @param channel the existing channel to use for authentication
59+
* @return credentials instance
60+
*/
61+
protected TokenCredentials getOrCreateCredentials(Channel channel) {
62+
TokenCredentials result = new TokenCredentials(secretToken, AuthGrpc.newBlockingStub(channel));
63+
if (autoConnect) {
64+
if (waitForAuth) {
65+
result.authSync();
66+
} else {
67+
result.authAsync();
68+
}
69+
}
70+
return result;
71+
}
72+
73+
/**
74+
*
75+
* @return true if the credentials are configured with a token, false otherwise
76+
*/
77+
public boolean hasToken() {
78+
return secretToken != null;
79+
}
80+
81+
/**
82+
* Build the authentication interceptor. It needs the Channel to setup the authentication. It can be the same Channel as the one used for actual requests.
83+
*
84+
* @param channel the [existing] channel to use for authentication
85+
* @return an instance of the authentication interceptor, or null if no authentication is configured
86+
*/
87+
public AuthInterceptor build(Channel channel) {
88+
AuthInterceptor authInterceptor = null;
89+
if (secretToken != null) {
90+
AuthHolder holder = new AuthHolder(getOrCreateCredentials(channel));
91+
authInterceptor = new AuthInterceptor(holder);
92+
}
93+
return authInterceptor;
94+
}
95+
}
96+
97+
98+
}

0 commit comments

Comments
 (0)