Skip to content

Commit

Permalink
Updated authorisation to use token and implemented deserialiser for e…
Browse files Browse the repository at this point in the history
…poch seconds (#27)

* Updated authentication implementation to use token with client_id and scope for more granular access control

* Made scope configuration mandatory without a default value

* Implemented converters for epoch second to use for auth response
  • Loading branch information
afaedda authored Jul 30, 2021
1 parent 3cc5492 commit 7e7a0fb
Show file tree
Hide file tree
Showing 20 changed files with 359 additions and 92 deletions.
2 changes: 2 additions & 0 deletions config/source-quickstart.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ tasks.max=1
venafi.base.url=https://uvo1pk54ksaae10beh2.env.cloudshare.com
venafi.username=tppadmin
venafi.password=Password123!
venafi.client_id=logs-connector-test
venafi.scope=certificate:manage

# Topic to publish VENAFI log data to.
# The default is "VENAFI-LOGS".
Expand Down
2 changes: 2 additions & 0 deletions integration/venafi-source-connector.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ tasks.max=1
venafi.base.url=http://host.docker.internal:8090
venafi.username=tppadmin
venafi.password=Password123!
venafi.client_id=kafka-connect-logs-test
venafi.scope=any

# Topic to publish VENAFI log data to.
# The default is "VENAFI-LOGS".
Expand Down
10 changes: 8 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,14 @@
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.20.0</version>
<artifactId>wiremock-jre8</artifactId>
<version>2.29.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.javacrumbs.json-unit</groupId>
<artifactId>json-unit-json-path</artifactId>
<version>2.27.0</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.opencredo.connect.venafi.tpp.log.Deserializer;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Type;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;

public class EpochSecondsDeserializer implements JsonDeserializer<ZonedDateTime> {
private static final Logger log = LoggerFactory.getLogger(EpochSecondsDeserializer.class);

private ZonedDateTime getParsedDate(String dateTimeString) {
ZonedDateTime zonedDateTime;
try {
Instant instant = Instant.ofEpochSecond(Long.parseLong(dateTimeString));
zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC.normalized());
} catch (DateTimeParseException e) {
log.debug("Failed to parse to ZonedDateTime format", e);
throw new JsonParseException("Unable to deserialize [" + dateTimeString + "] to a ZoneDateTime.", e);
}
return zonedDateTime;
}

@Override
public ZonedDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
if (jsonElement.isJsonNull() || jsonElement.getAsString().isEmpty() || !jsonElement.getAsString().chars().allMatch(Character::isDigit)) {
throw new JsonParseException("Unable to deserialize [" + jsonElement + "] to a ZoneDateTime.");
}
String json = jsonElement.getAsString();
return getParsedDate(json);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;


//TODO test DESERIALIZERS
public class ZonedDateTimeDeserializer implements JsonDeserializer<ZonedDateTime> {

private static final Logger log = LoggerFactory.getLogger(ZonedDateTimeDeserializer.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import java.util.Map;

public class TppLogSourceConfig extends AbstractConfig {

private static final Logger log = LoggerFactory.getLogger(TppLogSourceConfig.class);

public static final String BASE_URL_CONFIG = "venafi.base.url";
private static final String BASE_URL_DOC = "URL to VENAFI VEDSDK API";

Expand All @@ -18,6 +21,12 @@ public class TppLogSourceConfig extends AbstractConfig {
public static final String PASSWORD_CONFIG = "venafi.password";
private static final String PASSWORD_DOC = "The password to use with the API.";

public static final String SCOPE_CONFIG = "venafi.scope";
private static final String SCOPE_DOC = "The scope to use with the API.";

public static final String CLIENT_ID_CONFIG = "venafi.client_id";
private static final String CLIENT_ID_DOC = "The Application ID to use with the API.";

public static final String TOPIC_CONFIG = "venafi.topic";
private static final String TOPIC_DEFAULT = "VENAFI-LOGS";
private static final String TOPIC_DOC = "Topic to publish VENAFI log data to.";
Expand All @@ -29,7 +38,6 @@ public class TppLogSourceConfig extends AbstractConfig {
public static final String POLL_INTERVAL = "venafi.poll.interval";
private static final int POLL_INTERVAL_DEFAULT = 1000;
private static final String POLL_INTERVAL_DOC = "Poll interval in milliseconds.";
private static final Logger log = LoggerFactory.getLogger(TppLogSourceConfig.class);

public static final int MAX_BATCH_SIZE = 10_000;
public static final int MIN_BATCH_SIZE = 2;
Expand All @@ -39,7 +47,9 @@ public class TppLogSourceConfig extends AbstractConfig {
.define(BATCH_SIZE, ConfigDef.Type.INT, BATCH_SIZE_DEFAULT, ConfigDef.Range.between(MIN_BATCH_SIZE, MAX_BATCH_SIZE), ConfigDef.Importance.LOW, BATCH_SIZE_DOC)
.define(POLL_INTERVAL, ConfigDef.Type.INT, POLL_INTERVAL_DEFAULT, ConfigDef.Importance.LOW, POLL_INTERVAL_DOC)
.define(USERNAME_CONFIG, ConfigDef.Type.STRING, ConfigDef.Importance.HIGH, USERNAME_DOC)
.define(PASSWORD_CONFIG, ConfigDef.Type.STRING, ConfigDef.Importance.HIGH, PASSWORD_DOC);
.define(PASSWORD_CONFIG, ConfigDef.Type.STRING, ConfigDef.Importance.HIGH, PASSWORD_DOC)
.define(SCOPE_CONFIG, ConfigDef.Type.STRING, ConfigDef.Importance.MEDIUM, SCOPE_DOC)
.define(CLIENT_ID_CONFIG, ConfigDef.Type.STRING, ConfigDef.Importance.HIGH, CLIENT_ID_DOC);

public TppLogSourceConfig(Map<String, ?> props) {
super(CONFIG_DEF, props);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ private void setupTaskConfig(Map<String, String> props) {

String username = props.get(USERNAME_CONFIG);
String password = props.get(PASSWORD_CONFIG);
tokenClient = new TokenClient(username, password);
String scope = props.get(SCOPE_CONFIG);
String clientId = props.get(CLIENT_ID_CONFIG);
tokenClient = new TokenClient(username, password, scope, clientId);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public interface TppLog {
String OFFSET = "OFFSET";

@RequestLine("GET /vedsdk/Log")
@Headers({"Content-Type: application/json", "X-Venafi-Api-Key: {token}"})
@Headers({"Content-Type: application/json", "Authorization: Bearer {token}"})
LogResponse getLogs(@Param("token") String token, @QueryMap Map<String, Object> queryMap);

//If we in the future need to send a query Param with +
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package com.opencredo.connect.venafi.tpp.log.api;

import com.opencredo.connect.venafi.tpp.log.model.Credentials;
import com.opencredo.connect.venafi.tpp.log.model.TppRefreshToken;
import com.opencredo.connect.venafi.tpp.log.model.TppToken;
import feign.Headers;
import feign.RequestLine;

public interface TppPlatformAuthorization {


@RequestLine("POST /vedsdk/authorize/")
@RequestLine("POST /vedauth/authorize/")
@Headers("Content-Type: application/json")
TppToken getToken(Credentials credentials);


@RequestLine("POST /vedauth/authorize/token")
@Headers("Content-Type: application/json")
TppToken refreshToken(TppRefreshToken refreshToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import com.google.gson.FieldNamingPolicy;
import com.google.gson.GsonBuilder;
import com.opencredo.connect.venafi.tpp.log.Deserializer.DotNetDateDeserializer;
import com.opencredo.connect.venafi.tpp.log.Deserializer.EpochSecondsDeserializer;
import com.opencredo.connect.venafi.tpp.log.api.TppPlatformAuthorization;
import com.opencredo.connect.venafi.tpp.log.model.Credentials;
import com.opencredo.connect.venafi.tpp.log.model.TppRefreshToken;
import com.opencredo.connect.venafi.tpp.log.model.TppToken;
import feign.Feign;
import feign.FeignException;
Expand All @@ -19,52 +20,85 @@
public class TokenClient {

private static final org.slf4j.Logger log = LoggerFactory.getLogger(TokenClient.class);

private String tokenValue;
private ZonedDateTime tokenExpiry = ZonedDateTime.now();
private ZonedDateTime tokenRefreshUntil = ZonedDateTime.now();
private Credentials credentials;
private TppRefreshToken tppRefreshToken;
private String clientId;

public TokenClient(String username, String password) {
credentials = new Credentials(username, password);
public TokenClient(String username, String password, String scope, String clientId) {
this.credentials = new Credentials(username, password, scope, clientId);
this.clientId = clientId;
}

private static GsonDecoder customDecoder() {
return new GsonDecoder(
new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.registerTypeAdapter(ZonedDateTime.class, new DotNetDateDeserializer())
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(ZonedDateTime.class, new EpochSecondsDeserializer())
.create());
}

public String getToken(String baseUrl) {
if (isTokenInvalid()) {
try {
TppToken token = Feign
.builder()
.encoder(new GsonEncoder())
.logger(new Slf4jLogger())
.decoder(customDecoder())
.retryer(Retryer.NEVER_RETRY)
.target(TppPlatformAuthorization.class, baseUrl)
.getToken(credentials);
try {
TppToken token;
TppPlatformAuthorization tppAuth = tppAuth(baseUrl);

tokenValue = token.getAPIKey();
tokenExpiry = token.getValidUntil();
} catch (FeignException e) {
log.error("Caught following exception, ignoring to ensure connector doesn't fail", e);
tokenValue = "";
tokenExpiry = ZonedDateTime.now();
return "";
if (isTokenValid()) {
return tokenValue;
} else if (isTokenInvalid()) {
log.info("Getting new authorization token");
token = tppAuth.getToken(credentials);
} else if (isTokenExpired()) {
log.info("Refreshing authorization token");
token = tppAuth.refreshToken(tppRefreshToken);
} else {
token = tppAuth.getToken(credentials);
}

tokenValue = token.getAccessToken();
tokenExpiry = token.getExpires();
tokenRefreshUntil = token.getRefresh_until();
tppRefreshToken = new TppRefreshToken(token.getRefresh_token(), clientId);
} catch (FeignException e) {
log.error("Caught following exception, ignoring to ensure connector doesn't fail", e);
tokenValue = "";
tokenExpiry = ZonedDateTime.now();
tokenRefreshUntil = ZonedDateTime.now();
tppRefreshToken = null;
return "";
}
return tokenValue;

}

/**
* If it is the first time we are calling /authorize endpoint at connector startup
*
*/
private boolean isTokenInvalid() {
return tokenValue == null || isTokenExpired();
return tokenValue == null && tppRefreshToken == null;
}

private boolean isTokenValid() {
return tokenValue != null && tokenExpiry.isAfter(ZonedDateTime.now().minusSeconds(10L));
}

/**
* If token is expired but the refresh_token is still valid
*/
private boolean isTokenExpired() {
return tokenExpiry.isBefore(ZonedDateTime.now().minusSeconds(10L));
return tokenExpiry.isBefore(ZonedDateTime.now().minusSeconds(10L)) &&
tokenRefreshUntil.isAfter(ZonedDateTime.now().plusSeconds(10L));
}

private TppPlatformAuthorization tppAuth(String baseUrl) {
return Feign.builder()
.encoder(new GsonEncoder())
.logger(new Slf4jLogger())
.decoder(customDecoder())
.retryer(Retryer.NEVER_RETRY)
.target(TppPlatformAuthorization.class, baseUrl);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.opencredo.connect.venafi.tpp.log.model;

public class Credentials {
//These are uppercase currently to allow GSON to auto convert them to JSON without needing annotations.
private String Username;
private String Password;
//client_id to allow GSON to auto convert them to JSON without needing annotations.

public Credentials(String username, String password) {
Username = username;
Password = password;
private String username;
private String password;
private String client_id;
private String scope;

public Credentials(String username, String password, String scope, String client_id) {
this.username = username;
this.password = password;
this.client_id = client_id;
this.scope = scope;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.opencredo.connect.venafi.tpp.log.model;

public class TppRefreshToken {

private String refresh_token;
private String client_id;

public TppRefreshToken(String refresh_token, String client_id) {
this.refresh_token = refresh_token;
this.client_id = client_id;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@
import java.time.ZonedDateTime;

public class TppToken {
private String APIKey;
private ZonedDateTime validUntil;
private String access_token;
private ZonedDateTime expires;
private String refresh_token;
private ZonedDateTime refresh_until;

public String getAPIKey() {
return APIKey;
public String getAccessToken() {
return access_token;
}

public ZonedDateTime getValidUntil() {
return validUntil;
public ZonedDateTime getExpires() {
return expires;
}

public String getRefresh_token() {
return refresh_token;
}

public ZonedDateTime getRefresh_until() {
return refresh_until;
}
}
Loading

0 comments on commit 7e7a0fb

Please sign in to comment.