Skip to content

Commit fad8938

Browse files
committed
Add support for OAuth
1 parent 1bb19f3 commit fad8938

File tree

7 files changed

+158
-4
lines changed

7 files changed

+158
-4
lines changed

src/main/java/io/opentelemetry/contrib/generator/telemetry/cli/CLIProcessor.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.opentelemetry.contrib.generator.telemetry.transport.auth.AuthHandler;
2424
import io.opentelemetry.contrib.generator.telemetry.transport.auth.BasicAuthHandler;
2525
import io.opentelemetry.contrib.generator.telemetry.transport.auth.NoAuthHandler;
26+
import io.opentelemetry.contrib.generator.telemetry.transport.auth.OAuthHandler;
2627
import io.opentelemetry.contrib.generator.telemetry.transport.implementations.grpc.GRPCPayloadHandler;
2728
import io.opentelemetry.contrib.generator.telemetry.transport.implementations.rest.RESTPayloadHandler;
2829
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -113,10 +114,17 @@ private static PayloadHandler getPayloadHandler(String targetEnvYAML) {
113114
throw new GeneratorException("Either restURL (for REST endpoint) or gRPCHost & gRPCPort (for gRPC endpoint) " +
114115
"must be provided in environment target YAML");
115116
}
117+
String authMode = targetEnvironmentDetails.getAuthMode() == null ? "" : targetEnvironmentDetails.getAuthMode();
118+
authMode = authMode.toUpperCase();
119+
if (authMode.isBlank() || (!authMode.equals("NONE") && !authMode.equals("BASIC") && !authMode.equals("OAUTH"))) {
120+
log.warn("authMode not provided or invalid value provided in environment target YAML. Valid values are - none/basic/oauth." +
121+
"Will use none authentication for data posting.");
122+
authMode = "NONE";
123+
}
116124
AuthHandler authHandler;
117-
if (targetEnvironmentDetails.getAuthMode().equalsIgnoreCase("NONE")) {
125+
if (authMode.equalsIgnoreCase("NONE")) {
118126
authHandler = new NoAuthHandler();
119-
} else {
127+
} else if (authMode.equalsIgnoreCase("BASIC")) {
120128
if (StringUtils.defaultString(targetEnvironmentDetails.getUsername()).isBlank()) {
121129
throw new GeneratorException("Missing username in environment target YAML");
122130
}
@@ -125,6 +133,18 @@ private static PayloadHandler getPayloadHandler(String targetEnvYAML) {
125133
}
126134
authHandler = new BasicAuthHandler(targetEnvironmentDetails.getUsername(),
127135
targetEnvironmentDetails.getPassword());
136+
} else {
137+
if (StringUtils.defaultString(targetEnvironmentDetails.getTokenURL()).isBlank()) {
138+
throw new GeneratorException("Missing tokenURL in environment target YAML");
139+
}
140+
if (StringUtils.defaultString(targetEnvironmentDetails.getClientId()).isBlank()) {
141+
throw new GeneratorException("Missing clientId in environment target YAML");
142+
}
143+
if (StringUtils.defaultString(targetEnvironmentDetails.getClientSecret()).isBlank()) {
144+
throw new GeneratorException("Missing clientSecret in environment target YAML");
145+
}
146+
authHandler = new OAuthHandler(targetEnvironmentDetails.getTokenURL(), targetEnvironmentDetails.getClientId(),
147+
targetEnvironmentDetails.getClientSecret(), targetEnvironmentDetails.getScope());
128148
}
129149
if (!nonNullRestURL.isBlank()) {
130150
return new RESTPayloadHandler(nonNullRestURL, authHandler);

src/main/java/io/opentelemetry/contrib/generator/telemetry/cli/TargetEnvironmentDetails.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ public class TargetEnvironmentDetails {
2727
private String authMode;
2828
private String username;
2929
private String password;
30+
private String tokenURL;
31+
private String clientId;
32+
private String clientSecret;
33+
private String scope;
3034
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2022 AppDynamics Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opentelemetry.contrib.generator.telemetry.transport.auth;
18+
19+
import com.fasterxml.jackson.core.JsonProcessingException;
20+
import com.fasterxml.jackson.databind.JsonNode;
21+
import com.fasterxml.jackson.databind.ObjectMapper;
22+
import io.opentelemetry.contrib.generator.telemetry.transport.implementations.HTTPClient;
23+
import lombok.extern.slf4j.Slf4j;
24+
25+
import javax.ws.rs.core.HttpHeaders;
26+
import java.util.Base64;
27+
import java.util.Optional;
28+
import java.util.concurrent.TimeUnit;
29+
30+
/**
31+
* Class to handle OAuth client credentials workflow which will generate the access token to be used with every request.
32+
* See <a href="https://www.rfc-editor.org/rfc/rfc6749#section-4.4">OAuth2 Client Credentials specification</a>
33+
* In case of a different implementation of the OAuth, please use a custom implementation.
34+
*/
35+
@Slf4j
36+
public class OAuthHandler implements AuthHandler {
37+
38+
private final String GET_TOKEN_URL;
39+
private final String CLIENT_ID;
40+
private final String CLIENT_SECRET;
41+
//Provide null for scope if not required
42+
private final String SCOPE;
43+
private final HTTPClient httpClient;
44+
private String accessToken;
45+
private long expirySeconds;
46+
47+
public OAuthHandler(String GET_TOKEN_URL, String CLIENT_ID, String CLIENT_SECRET, String SCOPE) {
48+
this.GET_TOKEN_URL = GET_TOKEN_URL;
49+
this.CLIENT_ID = CLIENT_ID;
50+
this.CLIENT_SECRET = CLIENT_SECRET;
51+
this.SCOPE = SCOPE;
52+
httpClient = new HTTPClient();
53+
accessToken = "";
54+
expirySeconds = -1;
55+
}
56+
57+
@Override
58+
public String getAuthString() {
59+
if (expirySeconds < TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())) {
60+
setAccessToken();
61+
}
62+
return "Bearer " + accessToken;
63+
}
64+
65+
private void setAccessToken() {
66+
var body = "grant_type=client_credentials";
67+
if (SCOPE != null)
68+
{
69+
body = body + "&scope=" + SCOPE;
70+
}
71+
Optional<String> responseBody = httpClient.postBytes(GET_TOKEN_URL, getHeadersToken(CLIENT_ID, CLIENT_SECRET), body.getBytes());
72+
if (responseBody.isEmpty()) {
73+
log.error("Failed to get fresh token. Post data requests may fail.");
74+
return;
75+
}
76+
extractAndSetToken(responseBody.get());
77+
}
78+
79+
private void extractAndSetToken(String tokenSecretResponse) {
80+
var responseMapper = new ObjectMapper();
81+
boolean failure = false;
82+
JsonNode responseBody = null;
83+
try {
84+
responseBody = responseMapper.readTree(tokenSecretResponse);
85+
} catch (JsonProcessingException e) {
86+
log.error("Failed to parse json out of token secret response: " + tokenSecretResponse);
87+
failure = true;
88+
}
89+
if (responseBody == null || !responseBody.has("access_token")) {
90+
log.error("access_token not found in get access token response body: " + tokenSecretResponse);
91+
failure = true;
92+
} else {
93+
accessToken = responseBody.get("access_token").asText();
94+
}
95+
if (responseBody == null || !responseBody.has("expires_in")) {
96+
log.warn("expires_in field not found in get access token response body: " + tokenSecretResponse);
97+
log.warn("Will use default expiry time of 60 minutes.");
98+
} else {
99+
expirySeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + responseBody.get("expires_in").asLong();
100+
}
101+
if (failure) {
102+
log.error("Failed to get fresh token. Post data requests may fail.");
103+
}
104+
}
105+
106+
private String[] getHeadersToken(String id, String secret) {
107+
return new String[] {HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=utf-8",
108+
HttpHeaders.AUTHORIZATION, "Basic " + getEncodedBasicAuth(id, secret)};
109+
}
110+
111+
private String getEncodedBasicAuth(String id, String secret) {
112+
String authStr = id + ":" + secret;
113+
return Base64.getEncoder().encodeToString(authStr.getBytes());
114+
}
115+
116+
}

src/main/java/io/opentelemetry/contrib/generator/telemetry/transport/implementations/grpc/GRPCPayloadHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public GRPCPayloadHandler(String host, int gRPCPort, AuthHandler authHandler) {
5252
this.HOST = host;
5353
this.gRPCPORT = gRPCPort;
5454
this.authHandler = authHandler;
55-
isAuthEnabled = authHandler instanceof NoAuthHandler;
55+
isAuthEnabled = !(authHandler instanceof NoAuthHandler);
5656
}
5757

5858
@Override

src/main/java/io/opentelemetry/contrib/generator/telemetry/transport/implementations/rest/RESTPayloadHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public RESTPayloadHandler(String endpointURL, AuthHandler authHandler) {
5555
this.ENDPOINT_URL = endpointURL;
5656
httpClient = new HTTPClient();
5757
this.authHandler = authHandler;
58-
isAuthEnabled = authHandler instanceof NoAuthHandler;
58+
isAuthEnabled = !(authHandler instanceof NoAuthHandler);
5959
}
6060

6161
@Override

src/test/java/io/opentelemetry/contrib/generator/telemetry/TestCLIProcessor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,12 @@ public void testMetricsTracesBasicAuthGRPC() throws ParseException {
5050
CLIProcessor.main(cliArgs);
5151
}
5252

53+
@Test
54+
public void testAllGeneratorsOAuthGRPC() throws ParseException {
55+
String oauthTargetYAML = Paths.get(cliResourcesPath, "target-oauth.yaml").toString();
56+
String[] cliArgs = new String[] {
57+
"-e", ENTITIES_YAML, "-m", METRICS_YAML, "-l", LOGS_YAML, "-s", TRACES_YAML, "-t", oauthTargetYAML
58+
};
59+
CLIProcessor.main(cliArgs);
60+
}
5361
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
authMode: "oauth"
2+
clientId: "clientId"
3+
clientSecret: "clientSecret"
4+
grpchost: "localhost"
5+
grpcport: "3387"
6+
tokenURL: "http://localhost:8888/dummy"

0 commit comments

Comments
 (0)