Skip to content

Commit 4ba3020

Browse files
Add AuthorizationProvider to ClientOptions and WorkflowService [2] (#632)
1 parent 8c61213 commit 4ba3020

File tree

6 files changed

+181
-1
lines changed

6 files changed

+181
-1
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ dependencies {
6262
compile group: 'com.cronutils', name: 'cron-utils', version: '9.0.0'
6363
compile group: 'io.micrometer', name: 'micrometer-core', version: '1.1.2'
6464
compile group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
65+
compile group: 'com.auth0', name: 'java-jwt', version:'3.10.2'
6566

6667
testCompile group: 'junit', name: 'junit', version: '4.12'
6768
testCompile group: 'com.googlecode.junit-toolbox', name: 'junit-toolbox', version: '2.4'
@@ -155,6 +156,7 @@ def ossrhPassword = hasProperty('ossrhPassword') ? property('ossrhPassword') : '
155156
publishing {
156157

157158
publications {
159+
// Uncomment below if you want to run "publishMavenLocal"
158160
maven(MavenPublication) {
159161
pom.withXml {
160162
asNode().with {

src/main/java/com/uber/cadence/serviceclient/ClientOptions.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.common.collect.ImmutableMap;
2222
import com.uber.cadence.FeatureFlags;
2323
import com.uber.cadence.internal.metrics.NoopScope;
24+
import com.uber.cadence.serviceclient.auth.IAuthorizationProvider;
2425
import com.uber.m3.tally.Scope;
2526
import java.util.Map;
2627

@@ -75,6 +76,9 @@ public class ClientOptions {
7576
/** Optional TChannel headers */
7677
private final Map<String, String> headers;
7778

79+
/** Optional authorization provider */
80+
private final IAuthorizationProvider authProvider;
81+
7882
private static final ClientOptions DEFAULT_INSTANCE;
7983

8084
/** Optional Feature flags to turn on/off some Cadence features */
@@ -134,6 +138,7 @@ private ClientOptions(Builder builder) {
134138
} else {
135139
this.headers = ImmutableMap.of();
136140
}
141+
this.authProvider = builder.authProvider;
137142
}
138143

139144
public String getHost() {
@@ -185,6 +190,10 @@ public Map<String, String> getHeaders() {
185190
return headers;
186191
}
187192

193+
public IAuthorizationProvider getAuthProvider() {
194+
return authProvider;
195+
}
196+
188197
public FeatureFlags getFeatureFlags() {
189198
return this.featureFlags;
190199
}
@@ -207,6 +216,7 @@ public static class Builder {
207216
private Scope metricsScope;
208217
private Map<String, String> transportHeaders;
209218
private Map<String, String> headers;
219+
private IAuthorizationProvider authProvider;
210220
private FeatureFlags featureFlags;
211221

212222
private Builder() {}
@@ -216,6 +226,11 @@ public Builder setHost(String host) {
216226
return this;
217227
}
218228

229+
public Builder setAuthorizationProvider(IAuthorizationProvider provider) {
230+
this.authProvider = provider;
231+
return this;
232+
}
233+
219234
public Builder setPort(int port) {
220235
this.port = port;
221236
return this;

src/main/java/com/uber/cadence/serviceclient/WorkflowServiceTChannel.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
import java.net.InetAddress;
113113
import java.net.InetSocketAddress;
114114
import java.net.UnknownHostException;
115+
import java.nio.charset.StandardCharsets;
115116
import java.util.ArrayList;
116117
import java.util.HashMap;
117118
import java.util.Map;
@@ -275,7 +276,14 @@ private <T> ThriftRequest<T> buildThriftRequest(String apiName, T body, Long rpc
275276
new ThriftRequest.Builder<>(options.getServiceName(), endpoint);
276277
// Create a mutable hashmap for headers, as tchannel.tracing.PrefixedHeadersCarrier assumes
277278
// that it can call put directly to add new stuffs (e.g. traces).
278-
builder.setHeaders(new HashMap<>(thriftHeaders));
279+
final HashMap<String, String> headers = new HashMap<>(thriftHeaders);
280+
if (this.options.getAuthProvider() != null) {
281+
headers.put(
282+
"cadence-authorization",
283+
new String(options.getAuthProvider().getAuthToken(), StandardCharsets.UTF_8));
284+
}
285+
builder.setHeaders(headers);
286+
279287
if (rpcTimeoutOverride != null) {
280288
builder.setTimeout(rpcTimeoutOverride);
281289
} else {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Modifications Copyright (c) 2017-2021 Uber Technologies Inc.
3+
* Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc.
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
7+
* use this file except in compliance with the License. A copy of the License is
8+
* located at
9+
*
10+
* http://aws.amazon.com/apache2.0
11+
*
12+
* or in the "license" file accompanying this file. This file is distributed on
13+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14+
* express or implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*/
17+
18+
package com.uber.cadence.serviceclient.auth;
19+
20+
import com.auth0.jwt.JWT;
21+
import com.auth0.jwt.JWTCreator;
22+
import com.auth0.jwt.algorithms.Algorithm;
23+
import java.nio.charset.StandardCharsets;
24+
import java.security.interfaces.RSAPrivateKey;
25+
import java.security.interfaces.RSAPublicKey;
26+
import java.sql.Date;
27+
import java.time.Clock;
28+
29+
public class AdminJwtAuthorizationProvider implements IAuthorizationProvider {
30+
31+
private final RSAPrivateKey rsaPrivateKey;
32+
private final RSAPublicKey rsaPublicKey;
33+
34+
public AdminJwtAuthorizationProvider(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
35+
this.rsaPrivateKey = privateKey;
36+
this.rsaPublicKey = publicKey;
37+
}
38+
39+
@Override
40+
public byte[] getAuthToken() {
41+
final JWTCreator.Builder jwtBuilder = JWT.create();
42+
jwtBuilder.withClaim("admin", true);
43+
jwtBuilder.withClaim("ttl", 60 * 10);
44+
jwtBuilder.withIssuedAt(Date.from(Clock.systemUTC().instant()));
45+
return jwtBuilder
46+
.sign(Algorithm.RSA256(this.rsaPublicKey, this.rsaPrivateKey))
47+
.getBytes(StandardCharsets.UTF_8);
48+
}
49+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Modifications Copyright (c) 2017-2021 Uber Technologies Inc.
3+
* Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc.
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
7+
* use this file except in compliance with the License. A copy of the License is
8+
* located at
9+
*
10+
* http://aws.amazon.com/apache2.0
11+
*
12+
* or in the "license" file accompanying this file. This file is distributed on
13+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14+
* express or implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*/
17+
18+
package com.uber.cadence.serviceclient.auth;
19+
20+
public interface IAuthorizationProvider {
21+
// getAuthToken provides the OAuth authorization token
22+
// It's called before every request to Cadence server, and sets the token in the request header.
23+
byte[] getAuthToken();
24+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.uber.cadence.serviceclient.auth;
2+
3+
import static org.junit.Assert.*;
4+
5+
import com.auth0.jwt.JWT;
6+
import com.auth0.jwt.interfaces.Claim;
7+
import com.auth0.jwt.interfaces.DecodedJWT;
8+
import java.nio.charset.StandardCharsets;
9+
import java.security.KeyFactory;
10+
import java.security.NoSuchAlgorithmException;
11+
import java.security.interfaces.RSAPrivateKey;
12+
import java.security.interfaces.RSAPublicKey;
13+
import java.security.spec.InvalidKeySpecException;
14+
import java.security.spec.PKCS8EncodedKeySpec;
15+
import java.security.spec.X509EncodedKeySpec;
16+
import org.apache.commons.codec.binary.Base64;
17+
import org.junit.Test;
18+
19+
public class AdminJwtAuthorizationProviderTest {
20+
@Test
21+
public void testCreateAuthToken() throws NoSuchAlgorithmException, InvalidKeySpecException {
22+
23+
Base64 b64 = new Base64();
24+
byte[] decodedPub = b64.decode(testPublicKey.getBytes(StandardCharsets.UTF_8));
25+
byte[] decodedPri = b64.decode(testPrivateKey.getBytes(StandardCharsets.UTF_8));
26+
27+
KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
28+
29+
final RSAPublicKey rsaPublicKey =
30+
(RSAPublicKey) rsaKeyFactory.generatePublic(new X509EncodedKeySpec(decodedPub));
31+
32+
final RSAPrivateKey rsaPrivateKey =
33+
(RSAPrivateKey) rsaKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decodedPri));
34+
35+
final AdminJwtAuthorizationProvider authProvider =
36+
new AdminJwtAuthorizationProvider(rsaPublicKey, rsaPrivateKey);
37+
final String jwt = new String(authProvider.getAuthToken(), StandardCharsets.UTF_8);
38+
39+
final DecodedJWT decodedJwt = JWT.decode(jwt);
40+
final Claim adminClaim = decodedJwt.getClaim("admin");
41+
assertTrue(adminClaim.asBoolean());
42+
final Claim ttlClaim = decodedJwt.getClaim("ttl");
43+
assertEquals((int) (60 * 10), (int) ttlClaim.asInt());
44+
}
45+
46+
private static String testPublicKey =
47+
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAscukltHilaq+o5gIVE4P\n"
48+
+ "GwWl+esvJ2EaEpWw6ogr98Un11YJ4oKkwIkLw4iIo0tveCINA3cZmxaW1RejRWKE\n"
49+
+ "qYFtQ1rYd6BsnFAHXWh2R3A1FtpG6ANUEGkE7OAJe2/L42E/ImJ+GQxRvartInDM\n"
50+
+ "yfiRfB7+L2n3wG+Ni+hBNMtAaX4Wwbj2hup21Jjuo96TuhcGImBFBATGWaYR2wqe\n"
51+
+ "/6by9wJexPHlY/1uDp3SnzF1dCLjp76SGCfyYqOGC/PxhQi7mDxeH9/tIC+lt/Sz\n"
52+
+ "wc1n8gZLtlRlZHinvYa8lhWXqVYw6WD8h4LTgALq9iY+beD1PFQSY1GkQtt0RhRw\n"
53+
+ "eQIDAQAB";
54+
55+
private static String testPrivateKey =
56+
"MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCxy6SW0eKVqr6j\n"
57+
+ "mAhUTg8bBaX56y8nYRoSlbDqiCv3xSfXVgnigqTAiQvDiIijS294Ig0DdxmbFpbV\n"
58+
+ "F6NFYoSpgW1DWth3oGycUAddaHZHcDUW2kboA1QQaQTs4Al7b8vjYT8iYn4ZDFG9\n"
59+
+ "qu0icMzJ+JF8Hv4vaffAb42L6EE0y0BpfhbBuPaG6nbUmO6j3pO6FwYiYEUEBMZZ\n"
60+
+ "phHbCp7/pvL3Al7E8eVj/W4OndKfMXV0IuOnvpIYJ/Jio4YL8/GFCLuYPF4f3+0g\n"
61+
+ "L6W39LPBzWfyBku2VGVkeKe9hryWFZepVjDpYPyHgtOAAur2Jj5t4PU8VBJjUaRC\n"
62+
+ "23RGFHB5AgMBAAECggEABj1T9Orf0W9nskDQ2QQ7cuVdZEJjpMrbTK1Aw1L8/Qc9\n"
63+
+ "TSkINDEayaV9mn1RXe61APcBSdP4ER7nXfTZiQ21LhLcWWg9T3cbh1b70oRqyI9z\n"
64+
+ "Pi6HSBeWz4kfUBX9izMQFBZKzjYn6qaJp1b8bGXKRWkcvPRZqLhmsRPmeH3xrOHe\n"
65+
+ "qsIDhYXMjRoOgEUxLbk8iPLP6nx0icPJl/tHK2l76R+1Ko6TBE69Md2krUIuh0u4\n"
66+
+ "nm9n+Az+0GuvkFsLw5KMGhSBeqB+ez5qtFa8T8CUCn98IjiUDOwgZdFrNldFLcZf\n"
67+
+ "putw7O2qCA9LT+mFBQ6CVsVu/9tKeXQ9sJ7p3lxhwQKBgQDjt7HNIabLncdXPMu0\n"
68+
+ "ByRyNVme0+Y1vbj9Q7iodk77hvlzWpD1p5Oyvq7cN+Cb4c1iO/ZQXMyUw+9hLgmf\n"
69+
+ "LNquH2d4hK1Jerzc/ciwu6dUBsCW8+0VJd4M2UNN15rJMPvbZGmqMq9Np1iCTCjE\n"
70+
+ "dvHo7xjPcJhsbhMbHq+PaUU7OQKBgQDH4KuaHBFTGUPkRaQGAZNRB8dDvSExV6ID\n"
71+
+ "Pblzr80g9kKHUnQCQfIDLjHVgDbTaSCdRw7+EXRyRmLy5mfPWEbUFfIemEpEcEcb\n"
72+
+ "3geWeVDx4Z/FwprWFuVifRopRSQ/FAbMXLIui7OHXWLEtzBvLkR/uS2VIVPm10PV\n"
73+
+ "pbh2EXifQQKBgQDbcOLbjelBYLt/euvGgfeCQ50orIS1Fy5UidVCKjh0tR5gJk95\n"
74+
+ "G1L+tjilqQc+0LtuReBYkwTm+2YMXSQSi1P05fh9MEYZgDjOMZYbkcpu887V6Rx3\n"
75+
+ "+7Te5uOv+OyFozmhs0MMK6m5iGGHtsK2iPUYBoj/Jj8MhorM4KZH6ic4KQKBgQCl\n"
76+
+ "3zIpg09xSc9Iue5juZz6qtzXvzWzkAj4bZnggq1VxGfzix6Q3Q8tSoG6r1tQWLbj\n"
77+
+ "Lpwnhm6/guAMud6+eIDW8ptqfnFrmE26t6hOXMEq6lXANT5vmrKj6DP0uddZrZHy\n"
78+
+ "uJ55+B91n68elvPP4HKiGBfW4cCSGmTGAXAyM0+JwQKBgQCz2cNiFrr+oEnlHDLg\n"
79+
+ "EqsiEufppT4FSZPy9/MtuWuMgEOBu34cckYaai+nahQLQvH62KskTK0EUjE1ywub\n"
80+
+ "NPORuXcugxIBMHWyseOS7lrtrlSBxU9gntS7jHdM3IMrrUy9YZBvPvFGP0wLdpKM\n"
81+
+ "nvt3vT46hs3n28XZpb18uRkSDw==";
82+
}

0 commit comments

Comments
 (0)