From 16c2b6e16087c7493c452d703e588c02ee61ade9 Mon Sep 17 00:00:00 2001 From: angelmoo Date: Thu, 9 Jul 2020 18:08:52 -0500 Subject: [PATCH 1/3] adding support for getting access token on tpp server --- .gitignore | 6 +- .../com/venafi/vcert/sdk/VCertClient.java | 28 +++++- .../vcert/sdk/connectors/Connector.java | 35 ++++++++ .../connectors/tpp/AuthorizeResponseV2.java | 31 +++++++ .../connectors/tpp/ResfreshTokenResponse.java | 27 ++++++ .../vcert/sdk/connectors/tpp/TokenInfo.java | 18 ++++ .../venafi/vcert/sdk/connectors/tpp/Tpp.java | 12 +++ .../sdk/connectors/tpp/TppConnector.java | 90 +++++++++++++++++-- .../vcert/sdk/endpoint/Authentication.java | 50 ++++++++--- 9 files changed, 278 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeResponseV2.java create mode 100644 src/main/java/com/venafi/vcert/sdk/connectors/tpp/ResfreshTokenResponse.java create mode 100644 src/main/java/com/venafi/vcert/sdk/connectors/tpp/TokenInfo.java diff --git a/.gitignore b/.gitignore index 22975af..66350ec 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,8 @@ hs_err_pid* .idea/ *.iml -target/ \ No newline at end of file +target/ +/.settings/ +/.classpath +/.factorypath +/.project diff --git a/src/main/java/com/venafi/vcert/sdk/VCertClient.java b/src/main/java/com/venafi/vcert/sdk/VCertClient.java index ae58650..019066d 100644 --- a/src/main/java/com/venafi/vcert/sdk/VCertClient.java +++ b/src/main/java/com/venafi/vcert/sdk/VCertClient.java @@ -1,9 +1,12 @@ package com.venafi.vcert.sdk; import static org.apache.commons.lang3.StringUtils.isBlank; + import java.security.Security; + +import javax.naming.OperationNotSupportedException; + import com.google.common.annotations.VisibleForTesting; -import feign.FeignException; import com.venafi.vcert.sdk.certificate.CertificateRequest; import com.venafi.vcert.sdk.certificate.ImportRequest; import com.venafi.vcert.sdk.certificate.ImportResponse; @@ -15,11 +18,14 @@ import com.venafi.vcert.sdk.connectors.ZoneConfiguration; import com.venafi.vcert.sdk.connectors.cloud.Cloud; import com.venafi.vcert.sdk.connectors.cloud.CloudConnector; +import com.venafi.vcert.sdk.connectors.tpp.TokenInfo; import com.venafi.vcert.sdk.connectors.tpp.Tpp; import com.venafi.vcert.sdk.connectors.tpp.TppConnector; import com.venafi.vcert.sdk.endpoint.Authentication; import com.venafi.vcert.sdk.endpoint.ConnectorType; +import feign.FeignException; + public class VCertClient implements Connector { private Connector connector; @@ -245,5 +251,25 @@ public Policy readPolicyConfiguration(String zone) throws VCertException { } return null; } + + @Override + public TokenInfo getAccessToken( Authentication auth ) throws OperationNotSupportedException, VCertException{ + + return connector.getAccessToken(auth); + + } + + @Override + public TokenInfo refreshToken( String resfreshToken, String applicationId ) throws OperationNotSupportedException{ + + return connector.refreshToken(resfreshToken, applicationId); + + } + + @Override + public int revokeAccessToken(String accessToken) throws OperationNotSupportedException { + return connector.revokeAccessToken(accessToken); + } + } diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java b/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java index b379185..22d4cc5 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java @@ -1,5 +1,7 @@ package com.venafi.vcert.sdk.connectors; +import javax.naming.OperationNotSupportedException; + import com.venafi.vcert.sdk.VCertException; import com.venafi.vcert.sdk.certificate.CertificateRequest; import com.venafi.vcert.sdk.certificate.ImportRequest; @@ -7,6 +9,7 @@ import com.venafi.vcert.sdk.certificate.PEMCollection; import com.venafi.vcert.sdk.certificate.RenewalRequest; import com.venafi.vcert.sdk.certificate.RevocationRequest; +import com.venafi.vcert.sdk.connectors.tpp.TokenInfo; import com.venafi.vcert.sdk.endpoint.Authentication; import com.venafi.vcert.sdk.endpoint.ConnectorType; @@ -146,4 +149,36 @@ String requestCertificate(CertificateRequest request, String zone) * @throws VCertException */ Policy readPolicyConfiguration(String zone) throws VCertException; + + /** + * + * @return 1 if the access token was revoked and 0 if not. + * @throws OperationNotSupportedException for some types of applications this is not valid. + */ + default int revokeAccessToken( String accessToken ) throws OperationNotSupportedException { + throw new OperationNotSupportedException(); + } + + /** + * returns a new access token. + * @param auth authentication info + * @return the new token. + * @throws OperationNotSupportedException for some types of applications this is not valid. + * @throws VCertException throws this exception when authentication info is null. + */ + default TokenInfo getAccessToken ( Authentication auth ) throws OperationNotSupportedException, VCertException { + throw new OperationNotSupportedException(); + } + + /** + * this is for refreshing a token. + * @param resfreshToken the refresh token. + * @param applicationId the application id. + * @return a complete info about the new access token, refresh token, expires. + * @throws javax.naming.OperationNotSupportedException + */ + default TokenInfo refreshToken( String resfreshToken, String applicationId ) throws OperationNotSupportedException{ + throw new OperationNotSupportedException(); + } + } diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeResponseV2.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeResponseV2.java new file mode 100644 index 0000000..72b4913 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeResponseV2.java @@ -0,0 +1,31 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import com.google.gson.annotations.SerializedName; + +import lombok.Data; + +@Data +public class AuthorizeResponseV2 { + + @SerializedName("access_token") + private String accessToken; + + @SerializedName("refresh_token") + private String refreshToken; + + @SerializedName("expires") + private long expire; + + @SerializedName("token_type") + private String tokenType; + + @SerializedName("scope") + private String scope; + + @SerializedName("identity") + private String identity; + + @SerializedName("refresh_until") + private long refreshUntil; + +} diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/ResfreshTokenResponse.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/ResfreshTokenResponse.java new file mode 100644 index 0000000..87ffd25 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/ResfreshTokenResponse.java @@ -0,0 +1,27 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import com.google.gson.annotations.SerializedName; + +import lombok.Data; + +@Data +public class ResfreshTokenResponse { + + @SerializedName("access_token") + private String accessToken; + + @SerializedName("refresh_token") + private String refreshToken; + + @SerializedName("expires") + private long expire; + + @SerializedName("token_type") + private String tokenType; + + @SerializedName("scope") + private String scope; + + @SerializedName("refresh_until") + private long refreshUntil; +} diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TokenInfo.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TokenInfo.java new file mode 100644 index 0000000..4db22e8 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TokenInfo.java @@ -0,0 +1,18 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class TokenInfo { + + private String accessToken; + private String refreshToken; + private long expires; + private String tokenType; + private String scope; + private String identity; + private long refreshUntil; + +} diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java index b934302..c0f8590 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java @@ -21,6 +21,18 @@ public interface Tpp { @RequestLine("POST authorize/") @Headers("Content-Type: application/json") AuthorizeResponse authorize(TppConnector.AuthorizeRequest authorizeRequest); + + @RequestLine("POST vedauth/authorize/OAuth") + @Headers("Content-Type: application/json") + AuthorizeResponseV2 authorize(TppConnector.AuthorizeRequestV2 authorizeRequest); + + @RequestLine("POST vedauth/authorize/token") + @Headers("Content-Type: application/json") + ResfreshTokenResponse refreshToken(TppConnector.RefreshTokenRequest request); + + @RequestLine("GET vedauth/revoke/token") + @Headers("Authorization: {token}") + Response revokeToken(@Param("token") String token); @RequestLine("POST certificates/checkpolicy") @Headers({"Content-Type: application/json", "x-venafi-api-key: {apiKey}"}) diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java index 8262d1d..1965991 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java @@ -22,12 +22,11 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; + +import javax.naming.OperationNotSupportedException; + import com.google.common.annotations.VisibleForTesting; import com.google.gson.annotations.SerializedName; -import feign.Response; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.Getter; import com.venafi.vcert.sdk.VCertException; import com.venafi.vcert.sdk.certificate.CertificateRequest; import com.venafi.vcert.sdk.certificate.ChainOption; @@ -47,6 +46,10 @@ import com.venafi.vcert.sdk.endpoint.ConnectorType; import com.venafi.vcert.sdk.utils.Is; +import feign.Response; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; public class TppConnector implements Connector { @@ -64,6 +67,7 @@ public class TppConnector implements Connector { private String vendorAndProductName; private static final String tppAttributeManagementType = "Management Type"; private static final String tppAttributeManualCSR = "Manual Csr"; + private static final String MISSING_CREDENTIALS_MESSAGE = "failed to authenticate: missing credentials"; // TODO can be enum @SuppressWarnings("serial") @@ -122,11 +126,39 @@ private Response doPing() { } public void authenticate(Authentication auth) throws VCertException { - VCertException.throwIfNull(auth, "failed to authenticate: missing credentials"); + VCertException.throwIfNull(auth, MISSING_CREDENTIALS_MESSAGE); AuthorizeResponse response = tpp.authorize(new AuthorizeRequest(auth.user(), auth.password())); apiKey = response.apiKey(); bestBeforeEnd = response.validUntil(); } + + + @Override + public TokenInfo getAccessToken(Authentication auth) throws OperationNotSupportedException, VCertException { + + VCertException.throwIfNull( auth, MISSING_CREDENTIALS_MESSAGE ); + + AuthorizeRequestV2 info = new AuthorizeRequestV2( auth.user(), auth.password(), auth.clientId(), auth.scope(), auth.state(), auth.redirectUri() ); + + AuthorizeResponseV2 response = tpp.authorize( info ); + + TokenInfo accessTokenInfo = new TokenInfo(response.accessToken(), response.refreshToken(), response.expire(), response.tokenType(), response.scope(), response.identity(), response.refreshUntil()); + + return accessTokenInfo; + } + + @Override + public TokenInfo refreshToken(String refreshToken, String clientId ) throws OperationNotSupportedException{ + + RefreshTokenRequest request = new RefreshTokenRequest(refreshToken, clientId); + ResfreshTokenResponse response = tpp.refreshToken( request ); + + TokenInfo tokenInfo = new TokenInfo( response.accessToken(), response.refreshToken(), response.expire(), + response.tokenType(), response.scope(), "", + response.refreshUntil()); + + return tokenInfo; + } @Override public ZoneConfiguration readZoneConfiguration(String zone) throws VCertException { @@ -453,11 +485,57 @@ String getPolicyDN(final String zone) { return result; } - @Data + @Override + public int revokeAccessToken( String accessToken ) throws OperationNotSupportedException { + + String requestHeader = "Bearer "+accessToken; + + Response response = tpp.revokeToken( requestHeader ); + + return response.status() == 200 ? 1 : 0; + + } + + +@Data @AllArgsConstructor static class AuthorizeRequest { private String username; private String password; + + } + + @Data + @AllArgsConstructor + static class AuthorizeRequestV2{ + + @SerializedName("username") + private String username; + + @SerializedName("password") + private String password; + + @SerializedName("client_id") + private String client_id; + + @SerializedName("scope") + private String scope; + + @SerializedName("state") + private String state; + + @SerializedName("redirect_uri") + private String redirect_uri; + + } + + @Data + @AllArgsConstructor + static class RefreshTokenRequest{ + @SerializedName("refresh_token") + private String refresh_token; + @SerializedName("client_id") + private String client_id; } @Data diff --git a/src/main/java/com/venafi/vcert/sdk/endpoint/Authentication.java b/src/main/java/com/venafi/vcert/sdk/endpoint/Authentication.java index 2e9deb3..0d2480f 100644 --- a/src/main/java/com/venafi/vcert/sdk/endpoint/Authentication.java +++ b/src/main/java/com/venafi/vcert/sdk/endpoint/Authentication.java @@ -1,21 +1,49 @@ package com.venafi.vcert.sdk.endpoint; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @Data -@AllArgsConstructor @Builder public class Authentication { - private String user; - private String password; // todo: char[] ? - private String apiKey; + private String user; + private String password; // todo: char[] ? + private String apiKey; + @Builder.Default + private String clientId = "vcert-sdk"; + @Builder.Default + private String scope = "certificate:manage, revoke"; + @Builder.Default + private String state = ""; + @Builder.Default + private String redirectUri =""; + + public Authentication() {} - @Override - public String toString() { - return Authentication.class.getSimpleName() + "(user=" + user + ", apiKey=" + apiKey - + ", password=" + (!password.isEmpty() ? "****" : "not set") + ")"; - } -} + public Authentication(String user, String password, String apiKey) { + super(); + this.user = user; + this.password = password; + this.apiKey = apiKey; + } + + public Authentication(String user, String password, String apiKey, String clientId, String scope, String state, + String redirectUri) { + super(); + this.user = user; + this.password = password; + this.apiKey = apiKey; + this.clientId = clientId; + this.scope = scope; + this.state = state; + this.redirectUri = redirectUri; + } + + @Override + public String toString() { + return Authentication.class.getSimpleName() + "(user=" + user + ", apiKey=" + apiKey + + ", password=" + (!password.isEmpty() ? "****" : "not set") + ")"; + } + +} \ No newline at end of file From 7769fe4e36b07caa681feb9edeb0fd6bbab37d67 Mon Sep 17 00:00:00 2001 From: Russel Vela Date: Thu, 9 Jul 2020 19:38:29 -0500 Subject: [PATCH 2/3] VEN-60642 Provide support for Token Authentication on implemented operations. --- .../venafi/vcert/sdk/example/TppClient.java | 4 +- .../com/venafi/vcert/sdk/VCertClient.java | 27 +- .../vcert/sdk/connectors/Connector.java | 204 ++++++++- .../connectors/tpp/AbstractTPPConnector.java | 131 ++++++ .../venafi/vcert/sdk/connectors/tpp/Tpp.java | 44 +- .../sdk/connectors/tpp/TppConnector.java | 168 +------- .../connectors/tpp/TppVedAuthConnector.java | 407 ++++++++++++++++++ .../vcert/sdk/endpoint/ConnectorType.java | 3 +- 8 files changed, 778 insertions(+), 210 deletions(-) create mode 100644 src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTPPConnector.java create mode 100644 src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppVedAuthConnector.java diff --git a/examples/com/venafi/vcert/sdk/example/TppClient.java b/examples/com/venafi/vcert/sdk/example/TppClient.java index 4ecb986..5f26037 100644 --- a/examples/com/venafi/vcert/sdk/example/TppClient.java +++ b/examples/com/venafi/vcert/sdk/example/TppClient.java @@ -37,7 +37,7 @@ public static void main(String[] args) throws VCertException, CertificateEncodin appInfo = "CompanyName AppName"; // Configuration - Config config = Config.builder().connectorType(ConnectorType.TPP).baseUrl(url).appInfo(appInfo) + Config config = Config.builder().connectorType(ConnectorType.TPP_VEDAUTH).baseUrl(url).appInfo(appInfo) // To use proxy uncomment the lines below // .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888))) // .proxyUser("myUser") @@ -61,7 +61,7 @@ public static void main(String[] args) throws VCertException, CertificateEncodin .province(Collections.singletonList("Utah"))) .keyType(KeyType.RSA).keyLength(2048); - certificateRequest = client.generateRequest(zoneConfiguration, certificateRequest); + certificateRequest = client.generateRequest(zoneConfiguration, certificateRequest, access_token); // Submit the certificate request client.requestCertificate(certificateRequest, zoneConfiguration); diff --git a/src/main/java/com/venafi/vcert/sdk/VCertClient.java b/src/main/java/com/venafi/vcert/sdk/VCertClient.java index 019066d..0706eb0 100644 --- a/src/main/java/com/venafi/vcert/sdk/VCertClient.java +++ b/src/main/java/com/venafi/vcert/sdk/VCertClient.java @@ -1,12 +1,10 @@ package com.venafi.vcert.sdk; import static org.apache.commons.lang3.StringUtils.isBlank; - import java.security.Security; - -import javax.naming.OperationNotSupportedException; - import com.google.common.annotations.VisibleForTesting; +import com.venafi.vcert.sdk.connectors.tpp.TppVedAuthConnector; +import feign.FeignException; import com.venafi.vcert.sdk.certificate.CertificateRequest; import com.venafi.vcert.sdk.certificate.ImportRequest; import com.venafi.vcert.sdk.certificate.ImportResponse; @@ -24,8 +22,6 @@ import com.venafi.vcert.sdk.endpoint.Authentication; import com.venafi.vcert.sdk.endpoint.ConnectorType; -import feign.FeignException; - public class VCertClient implements Connector { private Connector connector; @@ -44,6 +40,9 @@ public VCertClient(Config config) throws VCertException { case CLOUD: connector = new CloudConnector(Cloud.connect(config)); break; + case TPP_VEDAUTH: + connector = new TppVedAuthConnector(Tpp.connect(config)); + break; default: throw new VCertException("ConnectorType is not defined"); } @@ -251,19 +250,25 @@ public Policy readPolicyConfiguration(String zone) throws VCertException { } return null; } - + @Override public TokenInfo getAccessToken( Authentication auth ) throws OperationNotSupportedException, VCertException{ + //=========================================================================================\\ + //=============================== VENAFI 20.2 OAUTH METHODS ===============================\\ + //=========================================================================================\\ + + + return connector.getAccessToken(auth); } @Override public TokenInfo refreshToken( String resfreshToken, String applicationId ) throws OperationNotSupportedException{ - - return connector.refreshToken(resfreshToken, applicationId); - + + return connector.refreshToken(resfreshToken, applicationId); + } @Override @@ -271,5 +276,5 @@ public int revokeAccessToken(String accessToken) throws OperationNotSupportedExc return connector.revokeAccessToken(accessToken); } - + } diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java b/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java index 22d4cc5..b61d18e 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java @@ -1,7 +1,5 @@ package com.venafi.vcert.sdk.connectors; -import javax.naming.OperationNotSupportedException; - import com.venafi.vcert.sdk.VCertException; import com.venafi.vcert.sdk.certificate.CertificateRequest; import com.venafi.vcert.sdk.certificate.ImportRequest; @@ -15,7 +13,9 @@ public interface Connector { - /** + public final String NOT_IMPLEMENTED_STRING = "Method not yet implemented."; + + /** * @return ConnectorType the type of connector Cloud or TPP */ ConnectorType getType(); @@ -52,7 +52,9 @@ public interface Connector { * * @throws VCertException */ - void ping() throws VCertException; + default void ping() throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * Authenticate the user with Venafi using either API key for Venafi Cloud or user and password @@ -61,7 +63,9 @@ public interface Connector { * @param auth * @throws VCertException */ - void authenticate(Authentication auth) throws VCertException; + default void authenticate(Authentication auth) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * Reads the zone configuration needed for generating and requesting a certificate @@ -71,7 +75,9 @@ public interface Connector { * @return * @throws VCertException */ - ZoneConfiguration readZoneConfiguration(String zone) throws VCertException; + default ZoneConfiguration readZoneConfiguration(String zone) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * GenerateRequest creates a new certificate request, based on the zone/policy configuration and @@ -81,8 +87,10 @@ public interface Connector { * @return the zone configuration * @throws VCertException */ - CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request) - throws VCertException; + default CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request) + throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * Submits the CSR to Venafi for processing @@ -92,8 +100,10 @@ CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest * @return request id to track the certificate status. * @throws VCertException */ - String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration) - throws VCertException; + default String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration) + throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * Submits the CSR to Venafi for processing @@ -103,8 +113,10 @@ String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConf * @return request id to track the certificate status. * @throws VCertException */ - String requestCertificate(CertificateRequest request, String zone) - throws VCertException; + default String requestCertificate(CertificateRequest request, String zone) + throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * Retrives the certificate for the specific ID @@ -113,7 +125,9 @@ String requestCertificate(CertificateRequest request, String zone) * @return A collection of PEM files including certificate, chain and potentially a private key. * @throws VCertException */ - PEMCollection retrieveCertificate(CertificateRequest request) throws VCertException; + default PEMCollection retrieveCertificate(CertificateRequest request) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * Attempts to revoke a certificate @@ -121,7 +135,9 @@ String requestCertificate(CertificateRequest request, String zone) * @param request * @throws VCertException */ - void revokeCertificate(RevocationRequest request) throws VCertException; + default void revokeCertificate(RevocationRequest request) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * Attempts to renew a certificate @@ -130,7 +146,9 @@ String requestCertificate(CertificateRequest request, String zone) * @return * @throws VCertException */ - String renewCertificate(RenewalRequest request) throws VCertException; + default String renewCertificate(RenewalRequest request) throws VCertException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * Import an external certificate into Venafi. @@ -139,7 +157,9 @@ String requestCertificate(CertificateRequest request, String zone) * @return * @throws VCertException */ - ImportResponse importCertificate(ImportRequest request) throws VCertException; + default ImportResponse importCertificate(ImportRequest request) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); + } /** * Reads the policy configuration for a specific zone in Venafi @@ -151,7 +171,7 @@ String requestCertificate(CertificateRequest request, String zone) Policy readPolicyConfiguration(String zone) throws VCertException; /** - * + * * @return 1 if the access token was revoked and 0 if not. * @throws OperationNotSupportedException for some types of applications this is not valid. */ @@ -175,10 +195,156 @@ default TokenInfo getAccessToken ( Authentication auth ) throws OperationNotSupp * @param resfreshToken the refresh token. * @param applicationId the application id. * @return a complete info about the new access token, refresh token, expires. - * @throws javax.naming.OperationNotSupportedException + * @throws javax.naming.OperationNotSupportedException */ default TokenInfo refreshToken( String resfreshToken, String applicationId ) throws OperationNotSupportedException{ throw new OperationNotSupportedException(); } - + + //=========================================================================================\\ + //=============================== VENAFI 20.2 VEDAUTH METHODS ===============================\\ + //=========================================================================================\\ + + /** + * VedAuth method. + * + * Attempt to connect the Venafi API and returns an error if it cannot + * + * @throws VCertException + * @throws UnsupportedOperationException + */ + default void ping(String accessToken) throws VCertException, UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * VedAuth method. + * Reads the zone configuration needed for generating and requesting a certificate + * + * @param zone ID (e.g. 2ebd4ec1-57f7-4994-8651-e396b286a3a8) or zone path (e.g. + * "ProjectName\ZoneName") + * @param accessToken The authentication token. + * @return + * @throws VCertException + */ + default ZoneConfiguration readZoneConfiguration(String zone, String accessToken) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(); + } + + /** + * VedAuth method. + * + * GenerateRequest creates a new certificate request, based on the zone/policy configuration and + * the user data + * + * @param config + * @param accessToken The authentication token + * @return the zone configuration + * @throws VCertException + */ + default CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request, String accessToken) + throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(); + }; + + /** + * VedAuth method. + * + * Submits the CSR to Venafi for processing + * + * @param request + * @param zoneConfiguration + * @param accessToken the authentication token. + * @return request id to track the certificate status. + * @throws VCertException + */ + default String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration, String accessToken) + throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(); + } + + /** + * VedAuth method. + * + * Submits the CSR to Venafi for processing + * + * @param request + * @param zone + * @param accesToken the authentication token. + * @return request id to track the certificate status. + * @throws VCertException + */ + default String requestCertificate(CertificateRequest request, String zone, String accesToken) + throws VCertException, UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * VedAuth method. + * + * Retrives the certificate for the specific ID + * + * @param request + * @param accessToken the authentication token. + * @return A collection of PEM files including certificate, chain and potentially a private key. + * @throws VCertException + */ + default PEMCollection retrieveCertificate(CertificateRequest request, String accessToken) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(); + } + + /** + * VedAuth method. + * + * Attempts to revoke a certificate + * + * @param request + * @param accessToken the authentication token. + * @throws VCertException + */ + default void revokeCertificate(RevocationRequest request, String accessToken) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(); + } + + /** + * VedAuth method. + * + * Attempts to renew a certificate + * + * @param request + * @param accessToken the authentication token. + * @return + * @throws VCertException + */ + default String renewCertificate(RenewalRequest request, String accessToken) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(); + } + + /** + * VedAuth method. + * + * Import an external certificate into Venafi. + * + * @param request + * @param accessToken the authentication token. + * @return + * @throws VCertException + */ + default ImportResponse importCertificate(ImportRequest request, String accessToken) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(); + } + + /** + * VedAuth method. + * + * Reads the policy configuration for a specific zone in Venafi + * + * @param zone + * @return + * @throws VCertException + */ + default Policy readPolicyConfiguration(String zone, String accessToken) throws VCertException, UnsupportedOperationException{ + throw new UnsupportedOperationException(); + } + } diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTPPConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTPPConnector.java new file mode 100644 index 0000000..53fa487 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTPPConnector.java @@ -0,0 +1,131 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import com.google.gson.annotations.SerializedName; +import com.venafi.vcert.sdk.connectors.ServerPolicy; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +public abstract class AbstractTPPConnector { + protected static final Pattern policy = Pattern.compile("^\\\\VED\\\\Policy"); + protected static final String HEADER_API_KEY = "x-venafi-api-key"; + protected static final String HEADER_AUTHORIZATION = "Authorization"; + protected static final String HEADER_VALUE_AUTHORIZATION = "Bearer %s"; + + protected final Tpp tpp; + + @Getter + protected String zone; + @Getter + protected String vendorAndProductName; + protected static final String tppAttributeManagementType = "Management Type"; + protected static final String tppAttributeManualCSR = "Manual Csr"; + + // TODO can be enum + @SuppressWarnings("serial") + protected static Map revocationReasons = new HashMap() { + { + put("", 0); // NoReason + put("none", 0); // + put("key-compromise", 1); // UserKeyCompromised + put("ca-compromise", 2); // CAKeyCompromised + put("affiliation-changed", 3); // UserChangedAffiliation + put("superseded", 4); // CertificateSuperseded + put("cessation-of-operation", 5); // OriginalUseNoLongerValid + } + }; + + public AbstractTPPConnector(Tpp tpp) { + this.tpp = tpp; + } + + @Data + @AllArgsConstructor + public static class AuthorizeRequest { + private String username; + private String password; + } + + @Data + @AllArgsConstructor + public static class ReadZoneConfigurationRequest { + String policyDN; + } + + @Data + public static class ReadZoneConfigurationResponse { + Object error; + ServerPolicy policy; + } + + @Data + public static class CertificateRequestsPayload { + @SerializedName("PolicyDN") + private String policyDN; + @SerializedName("CADN") + private String cadn; + private String objectName; + private String subject; + private String organizationalUnit; + private String organization; + private String city; + private String state; + private String country; + @SerializedName("SubjectAltNames") + private Collection subjectAltNames; + private String contact; + @SerializedName("CASpecificAttributes") + private Collection> caSpecificAttributes; + @SerializedName("PKCS10") + private String pkcs10; + private String keyAlgorithm; + private int keyBitSize; + private String ellipticCurve; + private boolean disableAutomaticRenewal; + private String origin; + } + + @Data + protected static class SANItem { + private int type; + private String name; + } + + @Data + @AllArgsConstructor + protected static class NameValuePair { + private K name; + private V value; + } + + @Data + public class CertificateRetrieveRequest { + private String certificateDN; + private String format; + private String password; + private boolean includePrivateKey; + private boolean includeChain; + private String friendlyName; + private boolean rootFirstOrder; + } + + @Data + public class CertificateRevokeRequest { + private String certificateDN; + private String thumbprint; + private Integer reason; + private String comments; + private boolean disable; + } + + @Data + public class CertificateRenewalRequest { + private String certificateDN; + private String PKCS10; + } +} diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java index c0f8590..4a4e76d 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java @@ -35,46 +35,46 @@ public interface Tpp { Response revokeToken(@Param("token") String token); @RequestLine("POST certificates/checkpolicy") - @Headers({"Content-Type: application/json", "x-venafi-api-key: {apiKey}"}) + @Headers({"Content-Type: application/json", "{header}: {value}"}) TppConnector.ReadZoneConfigurationResponse readZoneConfiguration( - TppConnector.ReadZoneConfigurationRequest readZoneConfigurationRequest, - @Param("apiKey") String apiKey); + TppConnector.ReadZoneConfigurationRequest readZoneConfigurationRequest, @Param("header") String header, + @Param("value") String value); @RequestLine("POST certificates/request") - @Headers({"Content-Type: application/json", "x-venafi-api-key: {apiKey}"}) - CertificateRequestResponse requestCertificate(TppConnector.CertificateRequestsPayload payload, - @Param("apiKey") String apiKey); + @Headers({"Content-Type: application/json", "{header}: {value}"}) + CertificateRequestResponse requestCertificate(TppConnector.CertificateRequestsPayload payload, @Param("header") String header, + @Param("value") String value); @RequestLine("GET certificates/") - @Headers("x-venafi-api-key: {apiKey}") - Tpp.CertificateSearchResponse searchCertificates(@QueryMap Map query, - @Param("apiKey") String apiKey); + @Headers("{header}: {value}") + Tpp.CertificateSearchResponse searchCertificates(@QueryMap Map query, @Param("header") String header, + @Param("value") String value); @RequestLine("POST certificates/retrieve") - @Headers({"Content-Type: application/json", "x-venafi-api-key: {apiKey}"}) + @Headers({"Content-Type: application/json", "{header}: {value}"}) CertificateRetrieveResponse certificateRetrieve( - TppConnector.CertificateRetrieveRequest certificateRetrieveRequest, - @Param("apiKey") String apiKey); + TppConnector.CertificateRetrieveRequest certificateRetrieveRequest, @Param("header") String header, + @Param("value") String value); @RequestLine("POST certificates/revoke") - @Headers({"Content-Type: application/json", "x-venafi-api-key: {apiKey}"}) - Tpp.CertificateRevokeResponse revokeCertificate(TppConnector.CertificateRevokeRequest request, - @Param("apiKey") String apiKey); + @Headers({"Content-Type: application/json", "{header}: {value}"}) + Tpp.CertificateRevokeResponse revokeCertificate(TppConnector.CertificateRevokeRequest request, @Param("header") String header, + @Param("value") String value); @RequestLine("POST certificates/renew") - @Headers({"Content-Type: application/json", "x-venafi-api-key: {apiKey}"}) - Tpp.CertificateRenewalResponse renewCertificate(TppConnector.CertificateRenewalRequest request, - @Param("apiKey") String apiKey); + @Headers({"Content-Type: application/json", "{header}: {value}"}) + Tpp.CertificateRenewalResponse renewCertificate(TppConnector.CertificateRenewalRequest request, @Param("header") String header, + @Param("value") String value); @RequestLine("POST certificates/import") - @Headers({"Content-Type: application/json", "x-venafi-api-key: {apiKey}"}) - ImportResponse importCertificate(ImportRequest request, @Param("apiKey") String apiKey); + @Headers({"Content-Type: application/json", "{header}: {value}"}) + ImportResponse importCertificate(ImportRequest request, @Param("header") String header, @Param("value") String value); @RequestLine("GET /") - @Headers("x-venafi-api-key: {apiKey}") - Response ping(@Param("apiKey") String apiKey); + @Headers("{header}: {value}") + Response ping(@Param("header") String header, @Param("value") String value); static Tpp connect(String baseUrl) { return FeignUtils.client(Tpp.class, Config.builder().baseUrl(baseUrl).build()); diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java index 1965991..8a7b5ee 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java @@ -22,11 +22,12 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; - -import javax.naming.OperationNotSupportedException; - import com.google.common.annotations.VisibleForTesting; import com.google.gson.annotations.SerializedName; +import feign.Response; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; import com.venafi.vcert.sdk.VCertException; import com.venafi.vcert.sdk.certificate.CertificateRequest; import com.venafi.vcert.sdk.certificate.ChainOption; @@ -46,29 +47,17 @@ import com.venafi.vcert.sdk.endpoint.ConnectorType; import com.venafi.vcert.sdk.utils.Is; -import feign.Response; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.Getter; -public class TppConnector implements Connector { +public class TppConnector extends AbstractTPPConnector implements Connector { private static final Pattern policy = Pattern.compile("^\\\\VED\\\\Policy"); - private final Tpp tpp; + private static final String MISSING_CREDENTIALS_MESSAGE = "failed to authenticate: missing credentials"; @VisibleForTesting OffsetDateTime bestBeforeEnd; @Getter private String apiKey; - @Getter - private String zone; - @Getter - private String vendorAndProductName; - private static final String tppAttributeManagementType = "Management Type"; - private static final String tppAttributeManualCSR = "Manual Csr"; - private static final String MISSING_CREDENTIALS_MESSAGE = "failed to authenticate: missing credentials"; - // TODO can be enum @SuppressWarnings("serial") private static Map revocationReasons = new HashMap() { @@ -84,7 +73,7 @@ public class TppConnector implements Connector { }; public TppConnector(Tpp tpp) { - this.tpp = tpp; + super(tpp); } @Override @@ -131,8 +120,8 @@ public void authenticate(Authentication auth) throws VCertException { apiKey = response.apiKey(); bestBeforeEnd = response.validUntil(); } - - + + @Override public TokenInfo getAccessToken(Authentication auth) throws OperationNotSupportedException, VCertException { @@ -141,12 +130,12 @@ public TokenInfo getAccessToken(Authentication auth) throws OperationNotSupporte AuthorizeRequestV2 info = new AuthorizeRequestV2( auth.user(), auth.password(), auth.clientId(), auth.scope(), auth.state(), auth.redirectUri() ); AuthorizeResponseV2 response = tpp.authorize( info ); - + TokenInfo accessTokenInfo = new TokenInfo(response.accessToken(), response.refreshToken(), response.expire(), response.tokenType(), response.scope(), response.identity(), response.refreshUntil()); return accessTokenInfo; } - + @Override public TokenInfo refreshToken(String refreshToken, String clientId ) throws OperationNotSupportedException{ @@ -442,7 +431,8 @@ public String renewCertificate(RenewalRequest request) throws VCertException { renewalRequest.certificateDN(certificateDN); if (Objects.nonNull(request.request()) && request.request().csr().length > 0) { - renewalRequest.PKCS10 = org.bouncycastle.util.Strings.fromByteArray(request.request().csr()); + String pkcs10 = org.bouncycastle.util.Strings.fromByteArray(request.request().csr()); + renewalRequest.PKCS10(pkcs10); } final Tpp.CertificateRenewalResponse response = tpp.renewCertificate(renewalRequest, apiKey()); @@ -484,136 +474,4 @@ String getPolicyDN(final String zone) { } return result; } - - @Override - public int revokeAccessToken( String accessToken ) throws OperationNotSupportedException { - - String requestHeader = "Bearer "+accessToken; - - Response response = tpp.revokeToken( requestHeader ); - - return response.status() == 200 ? 1 : 0; - - } - - -@Data - @AllArgsConstructor - static class AuthorizeRequest { - private String username; - private String password; - - } - - @Data - @AllArgsConstructor - static class AuthorizeRequestV2{ - - @SerializedName("username") - private String username; - - @SerializedName("password") - private String password; - - @SerializedName("client_id") - private String client_id; - - @SerializedName("scope") - private String scope; - - @SerializedName("state") - private String state; - - @SerializedName("redirect_uri") - private String redirect_uri; - - } - - @Data - @AllArgsConstructor - static class RefreshTokenRequest{ - @SerializedName("refresh_token") - private String refresh_token; - @SerializedName("client_id") - private String client_id; - } - - @Data - @AllArgsConstructor - static class ReadZoneConfigurationRequest { - String policyDN; - } - - @Data - public static class ReadZoneConfigurationResponse { - Object error; - ServerPolicy policy; - } - - @Data - static class CertificateRequestsPayload { - @SerializedName("PolicyDN") - private String policyDN; - @SerializedName("CADN") - private String cadn; - private String objectName; - private String subject; - private String organizationalUnit; - private String organization; - private String city; - private String state; - private String country; - @SerializedName("SubjectAltNames") - private Collection subjectAltNames; - private String contact; - @SerializedName("CASpecificAttributes") - private Collection> caSpecificAttributes; - @SerializedName("PKCS10") - private String pkcs10; - private String keyAlgorithm; - private int keyBitSize; - private String ellipticCurve; - private boolean disableAutomaticRenewal; - private String origin; - } - - @Data - private static class SANItem { - private int type; - private String name; - } - - @Data - @AllArgsConstructor - private static class NameValuePair { - private K name; - private V value; - } - - @Data - class CertificateRetrieveRequest { - private String certificateDN; - private String format; - private String password; - private boolean includePrivateKey; - private boolean includeChain; - private String friendlyName; - private boolean rootFirstOrder; - } - - @Data - class CertificateRevokeRequest { - private String certificateDN; - private String thumbprint; - private Integer reason; - private String comments; - private boolean disable; - } - - @Data - class CertificateRenewalRequest { - private String certificateDN; - private String PKCS10; - } - } diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppVedAuthConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppVedAuthConnector.java new file mode 100644 index 0000000..ad42978 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppVedAuthConnector.java @@ -0,0 +1,407 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import com.google.common.annotations.VisibleForTesting; +import com.venafi.vcert.sdk.VCertException; +import com.venafi.vcert.sdk.certificate.*; +import com.venafi.vcert.sdk.connectors.Connector; +import com.venafi.vcert.sdk.connectors.Policy; +import com.venafi.vcert.sdk.connectors.ServerPolicy; +import com.venafi.vcert.sdk.connectors.ZoneConfiguration; +import com.venafi.vcert.sdk.endpoint.Authentication; +import com.venafi.vcert.sdk.endpoint.ConnectorType; +import com.venafi.vcert.sdk.utils.Is; +import feign.Response; + +import java.net.InetAddress; +import java.text.MessageFormat; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; + +import static java.lang.String.format; +import static java.time.Duration.ZERO; +import static java.util.Objects.isNull; +import static java.util.stream.Collectors.toList; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class TppVedAuthConnector extends AbstractTPPConnector implements Connector { + + public TppVedAuthConnector(Tpp tpp){ super(tpp); } + + @Override + public ConnectorType getType() { + return ConnectorType.TPP_VEDAUTH; + } + + @Override + public void setBaseUrl(String url) throws VCertException { + throw new UnsupportedOperationException("Method not yet implemented"); + } + + @Override + public void setZone(String zone) { + this.zone = zone; + } + + @Override + public void setVendorAndProductName(String vendorAndProductName) { + this.vendorAndProductName = vendorAndProductName; + } + + @Override + public String getVendorAndProductName() { + return vendorAndProductName; + } + + private String getAuthHeaderValue(String token) throws VCertException { + if(isBlank(token)){ + throw new VCertException("Token cannot be empty"); + } + + return String.format(HEADER_VALUE_AUTHORIZATION, token); + } + + //TODO ADD AUTHENTICATE METHOD HERE + + @Override + public void ping(String accessToken) throws VCertException { + Response response = doPing(accessToken); + if (response.status() != 200) { + throw new VCertException( + format("ping failed with status %d and reason %s", response.status(), response.reason())); + } + } + + private Response doPing(String accessToken) throws VCertException{ + return tpp.ping(HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + } + + + @Override + public ZoneConfiguration readZoneConfiguration(String zone, String accessToken) throws VCertException { + VCertException.throwIfNull(zone, "empty zone"); + ReadZoneConfigurationRequest request = new ReadZoneConfigurationRequest(getPolicyDN(zone)); + ReadZoneConfigurationResponse response = tpp.readZoneConfiguration(request, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + ServerPolicy serverPolicy = response.policy(); + Policy policy = serverPolicy.toPolicy(); + ZoneConfiguration zoneConfig = serverPolicy.toZoneConfig(); + zoneConfig.policy(policy); + zoneConfig.zoneId(zone); + return zoneConfig; + } + + @Override + public CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request, String accessToken) throws VCertException { + // todo: should one really have to pass a request into a "generate request" method? + if (config == null) { + config = readZoneConfiguration(zone, accessToken); + } + String tppMgmtType = config.customAttributeValues().get(tppAttributeManagementType); + if ("Monitoring".equals(tppMgmtType) || "Unassigned".equals(tppMgmtType)) { + throw new VCertException( + "Unable to request certificate from TPP, current TPP configuration would not allow the request to be processed"); + } + + config.applyCertificateRequestDefaultSettingsIfNeeded(request); + + switch (request.csrOrigin()) { + case LocalGeneratedCSR: { + if ("0".equals(config.customAttributeValues().get(tppAttributeManualCSR))) { + throw new VCertException( + "Unable to request certificate by local generated CSR when zone configuration is 'Manual Csr' = 0"); + } + request.generatePrivateKey(); + request.generateCSR(); + break; + } + case UserProvidedCSR: { + if ("0".equals(config.customAttributeValues().get(tppAttributeManualCSR))) { + throw new VCertException( + "Unable to request certificate with user provided CSR when zone configuration is 'Manual Csr' = 0"); + } + if (Is.blank(request.csr())) { + throw new VCertException("CSR was supposed to be provided by user, but it's empty"); + } + break; + } + case ServiceGeneratedCSR: { + request.csr(null); + break; + } + } + + // TODO: should we return the request we modified? It's not a copy, it's the one that was passed in, mutated. + return request; + } + + @Override + public String requestCertificate(CertificateRequest request, String zone, String accessToken) throws VCertException { + return requestCertificate(request, new ZoneConfiguration().zoneId(zone), accessToken); + } + + @Override + public String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration, String accessToken) + throws VCertException { + if (isBlank(zoneConfiguration.zoneId())) { + zoneConfiguration.zoneId(this.zone); + } + CertificateRequestsPayload payload = prepareRequest(request, zoneConfiguration.zoneId()); + Tpp.CertificateRequestResponse response = tpp.requestCertificate(payload, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + String requestId = response.certificateDN(); + request.pickupId(requestId); + return requestId; + } + + private CertificateRequestsPayload prepareRequest(CertificateRequest request, String zone) + throws VCertException { + CertificateRequestsPayload payload; + Collection> caSpecificAttributes = + new ArrayList>(); + + // Workaround to send Origin to TPP versions that does not support it in the payload + if (!isBlank(vendorAndProductName)) { + caSpecificAttributes.add(new NameValuePair("Origin", vendorAndProductName)); + } + + switch (request.csrOrigin()) { + case LocalGeneratedCSR: + payload = new CertificateRequestsPayload().policyDN(getPolicyDN(zone)) + .pkcs10(new String(request.csr())).objectName(request.friendlyName()) + .disableAutomaticRenewal(true).origin(vendorAndProductName) + .caSpecificAttributes(caSpecificAttributes); + break; + case UserProvidedCSR: + payload = new CertificateRequestsPayload().policyDN(getPolicyDN(zone)) + .pkcs10(new String(request.csr())).objectName(request.friendlyName()) + .subjectAltNames(wrapAltNames(request)).disableAutomaticRenewal(true) + .origin(vendorAndProductName).caSpecificAttributes(caSpecificAttributes); + break; + case ServiceGeneratedCSR: + payload = new CertificateRequestsPayload().policyDN(getPolicyDN(zone)) + .objectName(request.friendlyName()).subject(request.subject().commonName()) // TODO (Go + // SDK): + // there is + // some + // problem + // because + // Subject + // is not + // only CN + .subjectAltNames(wrapAltNames(request)).disableAutomaticRenewal(true) + .origin(vendorAndProductName).caSpecificAttributes(caSpecificAttributes); + break; + default: + throw new VCertException(MessageFormat.format("Unexpected option in PrivateKeyOrigin: {0}", + request.csrOrigin())); + } + + if (request.keyType() == null) { + request.keyType(KeyType.defaultKeyType()); + } + + switch (request.keyType()) { + case RSA: { + payload.keyAlgorithm(PublicKeyAlgorithm.RSA.name()); + payload.keyBitSize(request.keyLength()); + break; + } + case ECDSA: { + payload.keyAlgorithm("ECC"); + payload.ellipticCurve(request.keyCurve().value()); + break; + } + } + return payload; + } + + private Collection wrapAltNames(CertificateRequest request) { + List sanItems = new ArrayList<>(); + sanItems.addAll(toSanItems(request.emailAddresses(), 1)); + sanItems.addAll(toSanItems(request.dnsNames(), 2)); + sanItems.addAll(toSanItems(request.ipAddresses(), 7)); + return sanItems; + } + + private List toSanItems(Collection collection, int type) { + return Optional.ofNullable(collection).orElse(Collections.emptyList()).stream() + .filter(Objects::nonNull) + .map(entry -> new SANItem().type(type) + .name(type == 7 ? ((InetAddress) entry).getHostAddress() : entry.toString())) + .collect(toList()); + } + + @Override + public PEMCollection retrieveCertificate(CertificateRequest request, String accessToken) throws VCertException { + boolean includeChain = request.chainOption() != ChainOption.ChainOptionIgnore; + boolean rootFirstOrder = + includeChain && request.chainOption() == ChainOption.ChainOptionRootFirst; + + if (isNotBlank(request.pickupId()) && isNotBlank(request.thumbprint())) { + Tpp.CertificateSearchResponse searchResult = + searchCertificatesByFingerprint(request.thumbprint(), accessToken); + if (searchResult.certificates().size() == 0) { + throw new VCertException( + format("No certificate found using fingerprint %s", request.thumbprint())); + } + if (searchResult.certificates().size() > 1) { + throw new VCertException(format( + "Error: more than one CertificateRequestId was found with the same thumbprint %s", + request.thumbprint())); + } + request.pickupId(searchResult.certificates().get(0).certificateRequestId()); + } + + CertificateRetrieveRequest certReq = + new CertificateRetrieveRequest().certificateDN(request.pickupId()).format("base64") + .rootFirstOrder(rootFirstOrder).includeChain(includeChain); + + if (request.csrOrigin() == CsrOriginOption.ServiceGeneratedCSR || request.fetchPrivateKey()) { + certReq.includePrivateKey(true); + certReq.password(request.keyPassword()); + } + + // TODO move this retry logic to feign client + Instant startTime = Instant.now(); + while (true) { + Tpp.CertificateRetrieveResponse retrieveResponse = retrieveCertificateOnce(certReq, accessToken); + if (isNotBlank(retrieveResponse.certificateData())) { + PEMCollection pemCollection = PEMCollection.fromResponse( + org.bouncycastle.util.Strings + .fromByteArray(Base64.getDecoder().decode(retrieveResponse.certificateData())), + request.chainOption(), request.privateKey()); + request.checkCertificate(pemCollection.certificate()); + return pemCollection; + } + + if (ZERO.equals(request.timeout())) { + throw new VCertException(format("Failed to retrieve certificate %s. Status %s", + request.pickupId(), retrieveResponse.status())); + } + + if (Instant.now().isAfter(startTime.plus(request.timeout()))) { + throw new VCertException( + format("Timeout trying to retrieve certificate %s", request.pickupId())); + } + + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + e.printStackTrace(); + throw new VCertException("Error attempting to retry", e); + } + } + } + + private Tpp.CertificateRetrieveResponse retrieveCertificateOnce( + CertificateRetrieveRequest certificateRetrieveRequest, String accessToken) throws VCertException { + return tpp.certificateRetrieve(certificateRetrieveRequest, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + } + + + private Tpp.CertificateSearchResponse searchCertificatesByFingerprint(String fingerprint, String accessToken) throws VCertException { + final Map searchRequest = new HashMap(); + searchRequest.put("Thumbprint", fingerprint); + + return searchCertificates(searchRequest, accessToken); + } + + private Tpp.CertificateSearchResponse searchCertificates(Map searchRequest, String accessToken) throws VCertException { + return tpp.searchCertificates(searchRequest, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + } + + @Override + public void revokeCertificate(RevocationRequest request, String accessToken) throws VCertException { + Integer reason = revocationReasons.get(request.reason()); + if (reason == null) { + throw new VCertException(format("could not parse revocation reason `%s`", request.reason())); + } + + CertificateRevokeRequest revokeRequest = new CertificateRevokeRequest() + .certificateDN(request.certificateDN()).thumbprint(request.thumbprint()).reason(reason) + .comments(request.comments()).disable(request.disable()); + + Tpp.CertificateRevokeResponse revokeResponse = revokeCertificate(revokeRequest,accessToken); + if (!revokeResponse.success()) { + throw new VCertException(format("Revocation error: %s", revokeResponse.error())); + } + } + + private Tpp.CertificateRevokeResponse revokeCertificate(CertificateRevokeRequest request, String accessToken) throws VCertException { + return tpp.revokeCertificate(request, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + } + + @Override + public String renewCertificate(RenewalRequest request, String accessToken) throws VCertException { + String certificateDN; + + if (isNotBlank(request.thumbprint()) && isBlank(request.certificateDN())) { + Tpp.CertificateSearchResponse searchResult = + searchCertificatesByFingerprint(request.thumbprint(), accessToken); + if (searchResult.certificates().isEmpty()) { + throw new VCertException( + String.format("No certificate found using fingerprint %s", request.thumbprint())); + } + if (searchResult.certificates().size() > 1) { + throw new VCertException( + String.format("More than one certificate was found with the same thumbprint")); + } + certificateDN = searchResult.certificates().get(0).certificateRequestId(); + } else { + certificateDN = request.certificateDN(); + } + + if (isNull(certificateDN)) { + throw new VCertException( + "Failed to create renewal request: CertificateDN or Thumbprint required"); + } + + final CertificateRenewalRequest renewalRequest = new CertificateRenewalRequest(); + renewalRequest.certificateDN(certificateDN); + + if (Objects.nonNull(request.request()) && request.request().csr().length > 0) { + String pkcs10 = org.bouncycastle.util.Strings.fromByteArray(request.request().csr()); + renewalRequest.PKCS10(pkcs10); + } + + final Tpp.CertificateRenewalResponse response = tpp.renewCertificate(renewalRequest, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + if (!response.success()) { + throw new VCertException(String.format("Certificate renewal error: %s", response.error())); + } + + return certificateDN; + } + + + @Override + public ImportResponse importCertificate(ImportRequest request, String accessToken) throws VCertException { + if (isBlank(request.policyDN())) { + request.policyDN(getPolicyDN(zone)); + } + + return doImportCertificate(request, accessToken); + } + + private ImportResponse doImportCertificate(ImportRequest request, String accessToken) throws VCertException { + return tpp.importCertificate(request, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + } + + @Override + public Policy readPolicyConfiguration(String zone) throws VCertException { + throw new UnsupportedOperationException("Method not yet implemented"); + } + + @VisibleForTesting + String getPolicyDN(final String zone) { + String result = zone; + Matcher candidate = policy.matcher(zone); + if (!candidate.matches()) { + if (!policy.matcher(zone).matches()) { + result = "\\" + result; + } + result = "\\VED\\Policy" + result; + } + return result; + } +} diff --git a/src/main/java/com/venafi/vcert/sdk/endpoint/ConnectorType.java b/src/main/java/com/venafi/vcert/sdk/endpoint/ConnectorType.java index a5e82b0..cfee686 100644 --- a/src/main/java/com/venafi/vcert/sdk/endpoint/ConnectorType.java +++ b/src/main/java/com/venafi/vcert/sdk/endpoint/ConnectorType.java @@ -2,5 +2,6 @@ public enum ConnectorType { TPP, - CLOUD + CLOUD, + TPP_VEDAUTH } From c7d8948bce6fcc7ef47abb53f81d04626292fbaa Mon Sep 17 00:00:00 2001 From: Russel Vela Date: Mon, 13 Jul 2020 11:18:58 -0500 Subject: [PATCH 3/3] https://jira.eng.venafi.com/browse/VEN-60642 VCert-Java - Transition from Username/Password to Token Auth for TPP Added a new high level class to invoke the api methods using the token mechanism. This new class VCertTknClient does contain the same methods as VCertClient but the low level implementation changes to make use of the token. It also includes methods for token specific operations (refresh, revoke). --- .../com/venafi/vcert/sdk/VCertClient.java | 36 +-- .../com/venafi/vcert/sdk/VCertTknClient.java | 247 ++++++++++++++ .../vcert/sdk/connectors/Connector.java | 229 +------------ .../vcert/sdk/connectors/TokenConnector.java | 189 +++++++++++ ...nnector.java => AbstractTppConnector.java} | 61 +++- ...nseV2.java => AuthorizeTokenResponse.java} | 2 +- .../venafi/vcert/sdk/connectors/tpp/Tpp.java | 103 ++++-- .../sdk/connectors/tpp/TppConnector.java | 67 +--- ...hConnector.java => TppTokenConnector.java} | 90 ++++-- .../vcert/sdk/endpoint/Authentication.java | 4 +- .../vcert/sdk/endpoint/ConnectorType.java | 2 +- .../venafi/vcert/sdk/utils/FeignUtils.java | 24 +- .../venafi/vcert/sdk/VCertTknClientTest.java | 261 +++++++++++++++ .../connectors/tpp/TppTokenConnectorAT.java | 306 ++++++++++++++++++ .../connectors/tpp/TppTokenConnectorIT.java | 96 ++++++ .../connectors/tpp/TppTokenConnectorTest.java | 262 +++++++++++++++ .../resources/mappings/tpp.token.auth.json | 18 ++ .../tpp.token.certificates.checkpolicy.json | 81 +++++ 18 files changed, 1685 insertions(+), 393 deletions(-) create mode 100644 src/main/java/com/venafi/vcert/sdk/VCertTknClient.java create mode 100644 src/main/java/com/venafi/vcert/sdk/connectors/TokenConnector.java rename src/main/java/com/venafi/vcert/sdk/connectors/tpp/{AbstractTPPConnector.java => AbstractTppConnector.java} (68%) rename src/main/java/com/venafi/vcert/sdk/connectors/tpp/{AuthorizeResponseV2.java => AuthorizeTokenResponse.java} (93%) rename src/main/java/com/venafi/vcert/sdk/connectors/tpp/{TppVedAuthConnector.java => TppTokenConnector.java} (85%) create mode 100644 src/test/java/com/venafi/vcert/sdk/VCertTknClientTest.java create mode 100644 src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorAT.java create mode 100644 src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorIT.java create mode 100644 src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorTest.java create mode 100644 src/test/resources/mappings/tpp.token.auth.json create mode 100644 src/test/resources/mappings/tpp.token.certificates.checkpolicy.json diff --git a/src/main/java/com/venafi/vcert/sdk/VCertClient.java b/src/main/java/com/venafi/vcert/sdk/VCertClient.java index 0706eb0..b59f75b 100644 --- a/src/main/java/com/venafi/vcert/sdk/VCertClient.java +++ b/src/main/java/com/venafi/vcert/sdk/VCertClient.java @@ -3,7 +3,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import java.security.Security; import com.google.common.annotations.VisibleForTesting; -import com.venafi.vcert.sdk.connectors.tpp.TppVedAuthConnector; import feign.FeignException; import com.venafi.vcert.sdk.certificate.CertificateRequest; import com.venafi.vcert.sdk.certificate.ImportRequest; @@ -16,16 +15,15 @@ import com.venafi.vcert.sdk.connectors.ZoneConfiguration; import com.venafi.vcert.sdk.connectors.cloud.Cloud; import com.venafi.vcert.sdk.connectors.cloud.CloudConnector; -import com.venafi.vcert.sdk.connectors.tpp.TokenInfo; import com.venafi.vcert.sdk.connectors.tpp.Tpp; import com.venafi.vcert.sdk.connectors.tpp.TppConnector; import com.venafi.vcert.sdk.endpoint.Authentication; import com.venafi.vcert.sdk.endpoint.ConnectorType; public class VCertClient implements Connector { + private static final String DEFAULT_VENDOR_AND_PRODUCT_NAME = "Venafi VCert-Java"; private Connector connector; - private static final String defaultVendorAndProductName = "Venafi VCert-Java"; public VCertClient(Config config) throws VCertException { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); @@ -40,14 +38,11 @@ public VCertClient(Config config) throws VCertException { case CLOUD: connector = new CloudConnector(Cloud.connect(config)); break; - case TPP_VEDAUTH: - connector = new TppVedAuthConnector(Tpp.connect(config)); - break; default: throw new VCertException("ConnectorType is not defined"); } - connector.setVendorAndProductName(isBlank(config.appInfo()) ? defaultVendorAndProductName : config.appInfo()); + connector.setVendorAndProductName(isBlank(config.appInfo()) ? DEFAULT_VENDOR_AND_PRODUCT_NAME : config.appInfo()); } @VisibleForTesting @@ -250,31 +245,4 @@ public Policy readPolicyConfiguration(String zone) throws VCertException { } return null; } - - @Override - public TokenInfo getAccessToken( Authentication auth ) throws OperationNotSupportedException, VCertException{ - - //=========================================================================================\\ - //=============================== VENAFI 20.2 OAUTH METHODS ===============================\\ - //=========================================================================================\\ - - - - return connector.getAccessToken(auth); - - } - - @Override - public TokenInfo refreshToken( String resfreshToken, String applicationId ) throws OperationNotSupportedException{ - - return connector.refreshToken(resfreshToken, applicationId); - - } - - @Override - public int revokeAccessToken(String accessToken) throws OperationNotSupportedException { - - return connector.revokeAccessToken(accessToken); - } - } diff --git a/src/main/java/com/venafi/vcert/sdk/VCertTknClient.java b/src/main/java/com/venafi/vcert/sdk/VCertTknClient.java new file mode 100644 index 0000000..464d6a7 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/VCertTknClient.java @@ -0,0 +1,247 @@ +package com.venafi.vcert.sdk; + +import com.google.common.annotations.VisibleForTesting; +import com.venafi.vcert.sdk.certificate.*; +import com.venafi.vcert.sdk.connectors.Policy; +import com.venafi.vcert.sdk.connectors.TokenConnector; +import com.venafi.vcert.sdk.connectors.ZoneConfiguration; +import com.venafi.vcert.sdk.connectors.tpp.TokenInfo; +import com.venafi.vcert.sdk.connectors.tpp.Tpp; +import com.venafi.vcert.sdk.connectors.tpp.TppTokenConnector; +import com.venafi.vcert.sdk.endpoint.Authentication; +import com.venafi.vcert.sdk.endpoint.ConnectorType; +import feign.FeignException; + +import java.security.Security; + +import static org.apache.commons.lang3.StringUtils.isBlank; + +public class VCertTknClient implements TokenConnector { + private static final String defaultVendorAndProductName = "Venafi VCert-Java"; + + private TokenConnector connector; + + public VCertTknClient(Config config) throws VCertException { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + switch (config.connectorType()) { + case TPP_TOKEN: + connector = new TppTokenConnector(Tpp.connect(config)); + break; + default: + throw new VCertException("ConnectorType is not defined"); + } + connector.setVendorAndProductName(isBlank(config.appInfo()) ? defaultVendorAndProductName : config.appInfo()); + } + + @VisibleForTesting + VCertTknClient(TokenConnector connector) { + this.connector = connector; + } + + /** + * {@inheritDoc} + */ + @Override + public ConnectorType getType() { + return connector.getType(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setBaseUrl(String url) throws VCertException { + connector.setBaseUrl(url); + } + + /** + * {@inheritDoc} + */ + @Override + public void setZone(String zone) { + connector.setZone(zone); + } + + /** + * {@inheritDoc} + */ + @Override + public void setVendorAndProductName(String vendorAndProductName) { + connector.setVendorAndProductName(vendorAndProductName); + } + + /** + * {@inheritDoc} + */ + @Override + public String getVendorAndProductName() { + return connector.getVendorAndProductName(); + } + + //=========================================================================================\\ + //=============================== VENAFI 20.2 OAUTH METHODS ===============================\\ + //=========================================================================================\\ + + @Override + public TokenInfo getAccessToken(Authentication auth) throws VCertException{ + try { + return connector.getAccessToken(auth); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + } + + @Override + public TokenInfo refreshAccessToken(String refreshToken, String applicationId) throws VCertException{ + return connector.refreshAccessToken(refreshToken, applicationId); + } + + @Override + public int revokeAccessToken(String accessToken) throws VCertException { + return connector.revokeAccessToken(accessToken); + } + + /** + * {@inheritDoc} + */ + @Override + public void ping(String accessToken) throws VCertException { + try { + connector.ping(accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public ZoneConfiguration readZoneConfiguration(String zone, String accessToken) throws VCertException { + try { + return connector.readZoneConfiguration(zone, accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request, String accessToken) + throws VCertException { + try { + return connector.generateRequest(config, request, accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + } + + @Override + public String requestCertificate(CertificateRequest request, String zone, String accessToken) throws VCertException { + try { + return connector.requestCertificate(request, zone, accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration, String accessToken) + throws VCertException { + try { + return connector.requestCertificate(request, zoneConfiguration, accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public PEMCollection retrieveCertificate(CertificateRequest request, String accessToken) throws VCertException { + try { + return connector.retrieveCertificate(request, accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void revokeCertificate(RevocationRequest request, String accessToken) throws VCertException { + try { + connector.revokeCertificate(request, accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String renewCertificate(RenewalRequest request, String accessToken) throws VCertException { + try { + connector.renewCertificate(request, accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public ImportResponse importCertificate(ImportRequest request, String accessToken) throws VCertException { + try { + connector.importCertificate(request, accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Policy readPolicyConfiguration(String zone, String accessToken) throws VCertException { + try { + connector.readPolicyConfiguration(zone, accessToken); + } catch (FeignException e) { + throw VCertException.fromFeignException(e); + } catch (Exception e) { + throw new VCertException("Unexpected exception", e); + } + return null; + } +} diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java b/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java index b61d18e..b379185 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/Connector.java @@ -7,15 +7,12 @@ import com.venafi.vcert.sdk.certificate.PEMCollection; import com.venafi.vcert.sdk.certificate.RenewalRequest; import com.venafi.vcert.sdk.certificate.RevocationRequest; -import com.venafi.vcert.sdk.connectors.tpp.TokenInfo; import com.venafi.vcert.sdk.endpoint.Authentication; import com.venafi.vcert.sdk.endpoint.ConnectorType; public interface Connector { - public final String NOT_IMPLEMENTED_STRING = "Method not yet implemented."; - - /** + /** * @return ConnectorType the type of connector Cloud or TPP */ ConnectorType getType(); @@ -52,9 +49,7 @@ public interface Connector { * * @throws VCertException */ - default void ping() throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + void ping() throws VCertException; /** * Authenticate the user with Venafi using either API key for Venafi Cloud or user and password @@ -63,9 +58,7 @@ default void ping() throws VCertException, UnsupportedOperationException{ * @param auth * @throws VCertException */ - default void authenticate(Authentication auth) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + void authenticate(Authentication auth) throws VCertException; /** * Reads the zone configuration needed for generating and requesting a certificate @@ -75,9 +68,7 @@ default void authenticate(Authentication auth) throws VCertException, Unsupporte * @return * @throws VCertException */ - default ZoneConfiguration readZoneConfiguration(String zone) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + ZoneConfiguration readZoneConfiguration(String zone) throws VCertException; /** * GenerateRequest creates a new certificate request, based on the zone/policy configuration and @@ -87,10 +78,8 @@ default ZoneConfiguration readZoneConfiguration(String zone) throws VCertExcepti * @return the zone configuration * @throws VCertException */ - default CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request) - throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request) + throws VCertException; /** * Submits the CSR to Venafi for processing @@ -100,10 +89,8 @@ default CertificateRequest generateRequest(ZoneConfiguration config, Certificate * @return request id to track the certificate status. * @throws VCertException */ - default String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration) - throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration) + throws VCertException; /** * Submits the CSR to Venafi for processing @@ -113,10 +100,8 @@ default String requestCertificate(CertificateRequest request, ZoneConfiguration * @return request id to track the certificate status. * @throws VCertException */ - default String requestCertificate(CertificateRequest request, String zone) - throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + String requestCertificate(CertificateRequest request, String zone) + throws VCertException; /** * Retrives the certificate for the specific ID @@ -125,9 +110,7 @@ default String requestCertificate(CertificateRequest request, String zone) * @return A collection of PEM files including certificate, chain and potentially a private key. * @throws VCertException */ - default PEMCollection retrieveCertificate(CertificateRequest request) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + PEMCollection retrieveCertificate(CertificateRequest request) throws VCertException; /** * Attempts to revoke a certificate @@ -135,9 +118,7 @@ default PEMCollection retrieveCertificate(CertificateRequest request) throws VCe * @param request * @throws VCertException */ - default void revokeCertificate(RevocationRequest request) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + void revokeCertificate(RevocationRequest request) throws VCertException; /** * Attempts to renew a certificate @@ -146,9 +127,7 @@ default void revokeCertificate(RevocationRequest request) throws VCertException, * @return * @throws VCertException */ - default String renewCertificate(RenewalRequest request) throws VCertException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + String renewCertificate(RenewalRequest request) throws VCertException; /** * Import an external certificate into Venafi. @@ -157,9 +136,7 @@ default String renewCertificate(RenewalRequest request) throws VCertException{ * @return * @throws VCertException */ - default ImportResponse importCertificate(ImportRequest request) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(NOT_IMPLEMENTED_STRING); - } + ImportResponse importCertificate(ImportRequest request) throws VCertException; /** * Reads the policy configuration for a specific zone in Venafi @@ -169,182 +146,4 @@ default ImportResponse importCertificate(ImportRequest request) throws VCertExce * @throws VCertException */ Policy readPolicyConfiguration(String zone) throws VCertException; - - /** - * - * @return 1 if the access token was revoked and 0 if not. - * @throws OperationNotSupportedException for some types of applications this is not valid. - */ - default int revokeAccessToken( String accessToken ) throws OperationNotSupportedException { - throw new OperationNotSupportedException(); - } - - /** - * returns a new access token. - * @param auth authentication info - * @return the new token. - * @throws OperationNotSupportedException for some types of applications this is not valid. - * @throws VCertException throws this exception when authentication info is null. - */ - default TokenInfo getAccessToken ( Authentication auth ) throws OperationNotSupportedException, VCertException { - throw new OperationNotSupportedException(); - } - - /** - * this is for refreshing a token. - * @param resfreshToken the refresh token. - * @param applicationId the application id. - * @return a complete info about the new access token, refresh token, expires. - * @throws javax.naming.OperationNotSupportedException - */ - default TokenInfo refreshToken( String resfreshToken, String applicationId ) throws OperationNotSupportedException{ - throw new OperationNotSupportedException(); - } - - //=========================================================================================\\ - //=============================== VENAFI 20.2 VEDAUTH METHODS ===============================\\ - //=========================================================================================\\ - - /** - * VedAuth method. - * - * Attempt to connect the Venafi API and returns an error if it cannot - * - * @throws VCertException - * @throws UnsupportedOperationException - */ - default void ping(String accessToken) throws VCertException, UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /** - * VedAuth method. - * Reads the zone configuration needed for generating and requesting a certificate - * - * @param zone ID (e.g. 2ebd4ec1-57f7-4994-8651-e396b286a3a8) or zone path (e.g. - * "ProjectName\ZoneName") - * @param accessToken The authentication token. - * @return - * @throws VCertException - */ - default ZoneConfiguration readZoneConfiguration(String zone, String accessToken) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(); - } - - /** - * VedAuth method. - * - * GenerateRequest creates a new certificate request, based on the zone/policy configuration and - * the user data - * - * @param config - * @param accessToken The authentication token - * @return the zone configuration - * @throws VCertException - */ - default CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request, String accessToken) - throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(); - }; - - /** - * VedAuth method. - * - * Submits the CSR to Venafi for processing - * - * @param request - * @param zoneConfiguration - * @param accessToken the authentication token. - * @return request id to track the certificate status. - * @throws VCertException - */ - default String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration, String accessToken) - throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(); - } - - /** - * VedAuth method. - * - * Submits the CSR to Venafi for processing - * - * @param request - * @param zone - * @param accesToken the authentication token. - * @return request id to track the certificate status. - * @throws VCertException - */ - default String requestCertificate(CertificateRequest request, String zone, String accesToken) - throws VCertException, UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /** - * VedAuth method. - * - * Retrives the certificate for the specific ID - * - * @param request - * @param accessToken the authentication token. - * @return A collection of PEM files including certificate, chain and potentially a private key. - * @throws VCertException - */ - default PEMCollection retrieveCertificate(CertificateRequest request, String accessToken) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(); - } - - /** - * VedAuth method. - * - * Attempts to revoke a certificate - * - * @param request - * @param accessToken the authentication token. - * @throws VCertException - */ - default void revokeCertificate(RevocationRequest request, String accessToken) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(); - } - - /** - * VedAuth method. - * - * Attempts to renew a certificate - * - * @param request - * @param accessToken the authentication token. - * @return - * @throws VCertException - */ - default String renewCertificate(RenewalRequest request, String accessToken) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(); - } - - /** - * VedAuth method. - * - * Import an external certificate into Venafi. - * - * @param request - * @param accessToken the authentication token. - * @return - * @throws VCertException - */ - default ImportResponse importCertificate(ImportRequest request, String accessToken) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(); - } - - /** - * VedAuth method. - * - * Reads the policy configuration for a specific zone in Venafi - * - * @param zone - * @return - * @throws VCertException - */ - default Policy readPolicyConfiguration(String zone, String accessToken) throws VCertException, UnsupportedOperationException{ - throw new UnsupportedOperationException(); - } - } diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/TokenConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/TokenConnector.java new file mode 100644 index 0000000..4d39191 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/TokenConnector.java @@ -0,0 +1,189 @@ +package com.venafi.vcert.sdk.connectors; + +import com.venafi.vcert.sdk.VCertException; +import com.venafi.vcert.sdk.certificate.*; +import com.venafi.vcert.sdk.connectors.tpp.TokenInfo; +import com.venafi.vcert.sdk.endpoint.Authentication; +import com.venafi.vcert.sdk.endpoint.ConnectorType; + +public interface TokenConnector { + + /** + * @return ConnectorType the type of connector Cloud or TPP + */ + ConnectorType getType(); + + /** + * Allows overriding the default URL used to communicate with Venafi + * + * @param url + * @throws VCertException + */ + void setBaseUrl(String url) throws VCertException; + + /** + * Set the default zone + * + * @param zone + */ + void setZone(String zone); + + /** + * Set the vendor and product name + * + * @param vendorAndProductName + */ + void setVendorAndProductName(String vendorAndProductName); + + /** + * @return the vendor and product name + */ + String getVendorAndProductName(); + + //=========================================================================================\\ + //=============================== VENAFI 20.2 TOKEN METHODS ===============================\\ + //=========================================================================================\\ + + /** + * returns a new access token. + * @param auth authentication info + * @return the new token. + * @throws VCertException throws this exception when authentication info is null. + */ + TokenInfo getAccessToken (Authentication auth ) throws VCertException; + + /** + * this is for refreshing a token. + * @param refreshToken the refresh token. + * @param applicationId the application id. + * @return a complete info about the new access token, refresh token, expires. + */ + TokenInfo refreshAccessToken( String refreshToken, String applicationId ) throws VCertException; + + /** + * + * @return 1 if the access token was revoked and 0 if not. + */ + int revokeAccessToken( String accessToken ) throws VCertException; + + /** + * VedAuth method. + * + * Attempt to connect the Venafi API and returns an error if it cannot + * + * @throws VCertException + */ + void ping(String accessToken) throws VCertException; + + /** + * VedAuth method. + * Reads the zone configuration needed for generating and requesting a certificate + * + * @param zone ID (e.g. 2ebd4ec1-57f7-4994-8651-e396b286a3a8) or zone path (e.g. + * "ProjectName\ZoneName") + * @param accessToken The authentication token. + * @return + * @throws VCertException + */ + ZoneConfiguration readZoneConfiguration(String zone, String accessToken) throws VCertException; + + /** + * VedAuth method. + * + * GenerateRequest creates a new certificate request, based on the zone/policy configuration and + * the user data + * + * @param config + * @param accessToken The authentication token + * @return the zone configuration + * @throws VCertException + */ + CertificateRequest generateRequest(ZoneConfiguration config, CertificateRequest request, String accessToken) + throws VCertException; + + /** + * VedAuth method. + * + * Submits the CSR to Venafi for processing + * + * @param request + * @param zoneConfiguration + * @param accessToken the authentication token. + * @return request id to track the certificate status. + * @throws VCertException + */ + String requestCertificate(CertificateRequest request, ZoneConfiguration zoneConfiguration, String accessToken) + throws VCertException, UnsupportedOperationException; + + /** + * VedAuth method. + * + * Submits the CSR to Venafi for processing + * + * @param request + * @param zone + * @param accessToken the authentication token. + * @return request id to track the certificate status. + * @throws VCertException + */ + String requestCertificate(CertificateRequest request, String zone, String accessToken) + throws VCertException, UnsupportedOperationException; + + /** + * VedAuth method. + * + * Retrives the certificate for the specific ID + * + * @param request + * @param accessToken the authentication token. + * @return A collection of PEM files including certificate, chain and potentially a private key. + * @throws VCertException + */ + PEMCollection retrieveCertificate(CertificateRequest request, String accessToken) throws VCertException; + + /** + * VedAuth method. + * + * Attempts to revoke a certificate + * + * @param request + * @param accessToken the authentication token. + * @throws VCertException + */ + void revokeCertificate(RevocationRequest request, String accessToken) throws VCertException; + + /** + * VedAuth method. + * + * Attempts to renew a certificate + * + * @param request + * @param accessToken the authentication token. + * @return + * @throws VCertException + */ + String renewCertificate(RenewalRequest request, String accessToken) throws VCertException; + + /** + * VedAuth method. + * + * Import an external certificate into Venafi. + * + * @param request + * @param accessToken the authentication token. + * @return + * @throws VCertException + */ + ImportResponse importCertificate(ImportRequest request, String accessToken) throws VCertException; + + /** + * VedAuth method. + * + * Reads the policy configuration for a specific zone in Venafi + * + * @param zone + * @return + * @throws VCertException + */ + Policy readPolicyConfiguration(String zone, String accessToken) throws VCertException; +} diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTPPConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTppConnector.java similarity index 68% rename from src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTPPConnector.java rename to src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTppConnector.java index 53fa487..05922a6 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTPPConnector.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTppConnector.java @@ -1,5 +1,6 @@ package com.venafi.vcert.sdk.connectors.tpp; +import com.google.common.annotations.VisibleForTesting; import com.google.gson.annotations.SerializedName; import com.venafi.vcert.sdk.connectors.ServerPolicy; import lombok.AllArgsConstructor; @@ -9,14 +10,16 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.regex.Matcher; import java.util.regex.Pattern; -public abstract class AbstractTPPConnector { - protected static final Pattern policy = Pattern.compile("^\\\\VED\\\\Policy"); - protected static final String HEADER_API_KEY = "x-venafi-api-key"; - protected static final String HEADER_AUTHORIZATION = "Authorization"; +public abstract class AbstractTppConnector { + protected static final Pattern POLICY_REGEX = Pattern.compile("^\\\\VED\\\\Policy"); protected static final String HEADER_VALUE_AUTHORIZATION = "Bearer %s"; + protected static final String MISSING_CREDENTIALS_MESSAGE = "failed to authenticate: missing credentials"; + + protected final Tpp tpp; @Getter @@ -28,7 +31,7 @@ public abstract class AbstractTPPConnector { // TODO can be enum @SuppressWarnings("serial") - protected static Map revocationReasons = new HashMap() { + protected static final Map revocationReasons = new HashMap() { { put("", 0); // NoReason put("none", 0); // @@ -40,10 +43,23 @@ public abstract class AbstractTPPConnector { } }; - public AbstractTPPConnector(Tpp tpp) { + public AbstractTppConnector(Tpp tpp) { this.tpp = tpp; } + @VisibleForTesting + String getPolicyDN(final String zone) { + String result = zone; + Matcher candidate = POLICY_REGEX.matcher(zone); + if (!candidate.matches()) { + if (!POLICY_REGEX.matcher(zone).matches()) { + result = "\\" + result; + } + result = "\\VED\\Policy" + result; + } + return result; + } + @Data @AllArgsConstructor public static class AuthorizeRequest { @@ -51,6 +67,39 @@ public static class AuthorizeRequest { private String password; } + @Data + @AllArgsConstructor + static class AuthorizeTokenRequest{ + + @SerializedName("username") + private String username; + + @SerializedName("password") + private String password; + + @SerializedName("client_id") + private String client_id; + + @SerializedName("scope") + private String scope; + + @SerializedName("state") + private String state; + + @SerializedName("redirect_uri") + private String redirect_uri; + + } + + @Data + @AllArgsConstructor + static class RefreshTokenRequest{ + @SerializedName("refresh_token") + private String refresh_token; + @SerializedName("client_id") + private String client_id; + } + @Data @AllArgsConstructor public static class ReadZoneConfigurationRequest { diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeResponseV2.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeTokenResponse.java similarity index 93% rename from src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeResponseV2.java rename to src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeTokenResponse.java index 72b4913..91ed936 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeResponseV2.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeTokenResponse.java @@ -5,7 +5,7 @@ import lombok.Data; @Data -public class AuthorizeResponseV2 { +public class AuthorizeTokenResponse { @SerializedName("access_token") private String accessToken; diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java index 4a4e76d..e72cd81 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/Tpp.java @@ -21,60 +21,95 @@ public interface Tpp { @RequestLine("POST authorize/") @Headers("Content-Type: application/json") AuthorizeResponse authorize(TppConnector.AuthorizeRequest authorizeRequest); - - @RequestLine("POST vedauth/authorize/OAuth") - @Headers("Content-Type: application/json") - AuthorizeResponseV2 authorize(TppConnector.AuthorizeRequestV2 authorizeRequest); - - @RequestLine("POST vedauth/authorize/token") - @Headers("Content-Type: application/json") - ResfreshTokenResponse refreshToken(TppConnector.RefreshTokenRequest request); - - @RequestLine("GET vedauth/revoke/token") - @Headers("Authorization: {token}") - Response revokeToken(@Param("token") String token); @RequestLine("POST certificates/checkpolicy") - @Headers({"Content-Type: application/json", "{header}: {value}"}) + @Headers({"Content-Type: application/json", "x-venafi-api-key: {value}"}) TppConnector.ReadZoneConfigurationResponse readZoneConfiguration( - TppConnector.ReadZoneConfigurationRequest readZoneConfigurationRequest, @Param("header") String header, - @Param("value") String value); + TppConnector.ReadZoneConfigurationRequest readZoneConfigurationRequest, @Param("value") String value); @RequestLine("POST certificates/request") - @Headers({"Content-Type: application/json", "{header}: {value}"}) - CertificateRequestResponse requestCertificate(TppConnector.CertificateRequestsPayload payload, @Param("header") String header, - @Param("value") String value); + @Headers({"Content-Type: application/json", "x-venafi-api-key: {value}"}) + CertificateRequestResponse requestCertificate(TppConnector.CertificateRequestsPayload payload, @Param("value") String value); @RequestLine("GET certificates/") - @Headers("{header}: {value}") - Tpp.CertificateSearchResponse searchCertificates(@QueryMap Map query, @Param("header") String header, - @Param("value") String value); + @Headers("x-venafi-api-key: {value}") + Tpp.CertificateSearchResponse searchCertificates(@QueryMap Map query, @Param("value") String value); @RequestLine("POST certificates/retrieve") - @Headers({"Content-Type: application/json", "{header}: {value}"}) + @Headers({"Content-Type: application/json", "x-venafi-api-key: {value}"}) CertificateRetrieveResponse certificateRetrieve( - TppConnector.CertificateRetrieveRequest certificateRetrieveRequest, @Param("header") String header, - @Param("value") String value); + TppConnector.CertificateRetrieveRequest certificateRetrieveRequest, @Param("value") String value); @RequestLine("POST certificates/revoke") - @Headers({"Content-Type: application/json", "{header}: {value}"}) - Tpp.CertificateRevokeResponse revokeCertificate(TppConnector.CertificateRevokeRequest request, @Param("header") String header, - @Param("value") String value); + @Headers({"Content-Type: application/json", "x-venafi-api-key: {value}"}) + Tpp.CertificateRevokeResponse revokeCertificate(TppConnector.CertificateRevokeRequest request, @Param("value") String value); @RequestLine("POST certificates/renew") - @Headers({"Content-Type: application/json", "{header}: {value}"}) - Tpp.CertificateRenewalResponse renewCertificate(TppConnector.CertificateRenewalRequest request, @Param("header") String header, - @Param("value") String value); + @Headers({"Content-Type: application/json", "x-venafi-api-key: {value}"}) + Tpp.CertificateRenewalResponse renewCertificate(TppConnector.CertificateRenewalRequest request, @Param("value") String value); @RequestLine("POST certificates/import") - @Headers({"Content-Type: application/json", "{header}: {value}"}) - ImportResponse importCertificate(ImportRequest request, @Param("header") String header, @Param("value") String value); + @Headers({"Content-Type: application/json", "x-venafi-api-key: {value}"}) + ImportResponse importCertificate(ImportRequest request, @Param("value") String value); @RequestLine("GET /") - @Headers("{header}: {value}") - Response ping(@Param("header") String header, @Param("value") String value); + @Headers("x-venafi-api-key: {value}") + Response ping(@Param("value") String value); + + + //============================Authorization Token Specific operations============================\\ + + @RequestLine("POST /vedauth/authorize/oauth") + @Headers("Content-Type: application/json") + AuthorizeTokenResponse authorizeToken(AbstractTppConnector.AuthorizeTokenRequest authorizeRequest); + + @RequestLine("POST /vedauth/authorize/token") + @Headers("Content-Type: application/json") + ResfreshTokenResponse refreshToken(AbstractTppConnector.RefreshTokenRequest request); + + @RequestLine("GET /vedauth/revoke/token") + @Headers("Authorization: {token}") + Response revokeToken(@Param("token") String token); + + @RequestLine("POST /vedsdk/certificates/checkpolicy") + @Headers({"Content-Type: application/json", "Authorization: {value}"}) + TppConnector.ReadZoneConfigurationResponse readZoneConfigurationToken( + TppConnector.ReadZoneConfigurationRequest readZoneConfigurationRequest, @Param("value") String value); + + @RequestLine("POST /vedsdk/certificates/request") + @Headers({"Content-Type: application/json", "Authorization: {value}"}) + CertificateRequestResponse requestCertificateToken(TppConnector.CertificateRequestsPayload payload, @Param("value") String value); + + @RequestLine("GET /vedsdk/certificates/") + @Headers("Authorization: {value}") + Tpp.CertificateSearchResponse searchCertificatesToken(@QueryMap Map query, @Param("value") String value); + + @RequestLine("POST /vedsdk/certificates/retrieve") + @Headers({"Content-Type: application/json", "Authorization: {value}"}) + CertificateRetrieveResponse certificateRetrieveToken( + TppConnector.CertificateRetrieveRequest certificateRetrieveRequest, @Param("value") String value); + + @RequestLine("POST /vedsdk/certificates/revoke") + @Headers({"Content-Type: application/json", "Authorization: {value}"}) + Tpp.CertificateRevokeResponse revokeCertificateToken(TppConnector.CertificateRevokeRequest request, @Param("value") String value); + + + @RequestLine("POST /vedsdk/certificates/renew") + @Headers({"Content-Type: application/json", "Authorization: {value}"}) + Tpp.CertificateRenewalResponse renewCertificateToken(TppConnector.CertificateRenewalRequest request, @Param("value") String value); + + + @RequestLine("POST /vedsdk/certificates/import") + @Headers({"Content-Type: application/json", "Authorization: {value}"}) + ImportResponse importCertificateToken(ImportRequest request, @Param("value") String value); + + @RequestLine("GET /vedsdk") + @Headers("Authorization: {value}") + Response pingToken(@Param("value") String value); + + //===============================================================================================\\ static Tpp connect(String baseUrl) { return FeignUtils.client(Tpp.class, Config.builder().baseUrl(baseUrl).build()); diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java index 8a7b5ee..546e84f 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppConnector.java @@ -20,13 +20,8 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import com.google.common.annotations.VisibleForTesting; -import com.google.gson.annotations.SerializedName; import feign.Response; -import lombok.AllArgsConstructor; -import lombok.Data; import lombok.Getter; import com.venafi.vcert.sdk.VCertException; import com.venafi.vcert.sdk.certificate.CertificateRequest; @@ -48,30 +43,12 @@ import com.venafi.vcert.sdk.utils.Is; -public class TppConnector extends AbstractTPPConnector implements Connector { - - private static final Pattern policy = Pattern.compile("^\\\\VED\\\\Policy"); - private static final String MISSING_CREDENTIALS_MESSAGE = "failed to authenticate: missing credentials"; - +public class TppConnector extends AbstractTppConnector implements Connector { @VisibleForTesting OffsetDateTime bestBeforeEnd; @Getter private String apiKey; - // TODO can be enum - @SuppressWarnings("serial") - private static Map revocationReasons = new HashMap() { - { - put("", 0); // NoReason - put("none", 0); // - put("key-compromise", 1); // UserKeyCompromised - put("ca-compromise", 2); // CAKeyCompromised - put("affiliation-changed", 3); // UserChangedAffiliation - put("superseded", 4); // CertificateSuperseded - put("cessation-of-operation", 5); // OriginalUseNoLongerValid - } - }; - public TppConnector(Tpp tpp) { super(tpp); } @@ -121,34 +98,6 @@ public void authenticate(Authentication auth) throws VCertException { bestBeforeEnd = response.validUntil(); } - - @Override - public TokenInfo getAccessToken(Authentication auth) throws OperationNotSupportedException, VCertException { - - VCertException.throwIfNull( auth, MISSING_CREDENTIALS_MESSAGE ); - - AuthorizeRequestV2 info = new AuthorizeRequestV2( auth.user(), auth.password(), auth.clientId(), auth.scope(), auth.state(), auth.redirectUri() ); - - AuthorizeResponseV2 response = tpp.authorize( info ); - - TokenInfo accessTokenInfo = new TokenInfo(response.accessToken(), response.refreshToken(), response.expire(), response.tokenType(), response.scope(), response.identity(), response.refreshUntil()); - - return accessTokenInfo; - } - - @Override - public TokenInfo refreshToken(String refreshToken, String clientId ) throws OperationNotSupportedException{ - - RefreshTokenRequest request = new RefreshTokenRequest(refreshToken, clientId); - ResfreshTokenResponse response = tpp.refreshToken( request ); - - TokenInfo tokenInfo = new TokenInfo( response.accessToken(), response.refreshToken(), response.expire(), - response.tokenType(), response.scope(), "", - response.refreshUntil()); - - return tokenInfo; - } - @Override public ZoneConfiguration readZoneConfiguration(String zone) throws VCertException { VCertException.throwIfNull(zone, "empty zone"); @@ -443,7 +392,6 @@ public String renewCertificate(RenewalRequest request) throws VCertException { return certificateDN; } - @Override public ImportResponse importCertificate(ImportRequest request) throws VCertException { if (isBlank(request.policyDN())) { @@ -461,17 +409,4 @@ private ImportResponse doImportCertificate(ImportRequest request) { public Policy readPolicyConfiguration(String zone) throws VCertException { throw new UnsupportedOperationException("Method not yet implemented"); } - - @VisibleForTesting - String getPolicyDN(final String zone) { - String result = zone; - Matcher candidate = policy.matcher(zone); - if (!candidate.matches()) { - if (!policy.matcher(zone).matches()) { - result = "\\" + result; - } - result = "\\VED\\Policy" + result; - } - return result; - } } diff --git a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppVedAuthConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnector.java similarity index 85% rename from src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppVedAuthConnector.java rename to src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnector.java index ad42978..856d4f1 100644 --- a/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppVedAuthConnector.java +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnector.java @@ -1,15 +1,12 @@ package com.venafi.vcert.sdk.connectors.tpp; -import com.google.common.annotations.VisibleForTesting; import com.venafi.vcert.sdk.VCertException; import com.venafi.vcert.sdk.certificate.*; -import com.venafi.vcert.sdk.connectors.Connector; -import com.venafi.vcert.sdk.connectors.Policy; -import com.venafi.vcert.sdk.connectors.ServerPolicy; -import com.venafi.vcert.sdk.connectors.ZoneConfiguration; +import com.venafi.vcert.sdk.connectors.*; import com.venafi.vcert.sdk.endpoint.Authentication; import com.venafi.vcert.sdk.endpoint.ConnectorType; import com.venafi.vcert.sdk.utils.Is; +import feign.FeignException; import feign.Response; import java.net.InetAddress; @@ -17,7 +14,6 @@ import java.time.Instant; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; import static java.lang.String.format; import static java.time.Duration.ZERO; @@ -26,13 +22,13 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; -public class TppVedAuthConnector extends AbstractTPPConnector implements Connector { +public class TppTokenConnector extends AbstractTppConnector implements TokenConnector { - public TppVedAuthConnector(Tpp tpp){ super(tpp); } + public TppTokenConnector(Tpp tpp){ super(tpp); } @Override public ConnectorType getType() { - return ConnectorType.TPP_VEDAUTH; + return ConnectorType.TPP_TOKEN; } @Override @@ -63,8 +59,6 @@ private String getAuthHeaderValue(String token) throws VCertException { return String.format(HEADER_VALUE_AUTHORIZATION, token); } - //TODO ADD AUTHENTICATE METHOD HERE - @Override public void ping(String accessToken) throws VCertException { Response response = doPing(accessToken); @@ -75,15 +69,58 @@ public void ping(String accessToken) throws VCertException { } private Response doPing(String accessToken) throws VCertException{ - return tpp.ping(HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + return tpp.pingToken(getAuthHeaderValue(accessToken)); + } + + @Override + public TokenInfo getAccessToken(Authentication auth) throws VCertException { + + VCertException.throwIfNull( auth, MISSING_CREDENTIALS_MESSAGE ); + + AuthorizeTokenRequest info = new AuthorizeTokenRequest( auth.user(), auth.password(), auth.clientId(), auth.scope(), auth.state(), auth.redirectUri() ); + + AuthorizeTokenResponse response = tpp.authorizeToken( info ); + + TokenInfo accessTokenInfo = new TokenInfo(response.accessToken(), response.refreshToken(), response.expire(), response.tokenType(), response.scope(), response.identity(), response.refreshUntil()); + + return accessTokenInfo; } + @Override + public TokenInfo refreshAccessToken(String refreshToken, String clientId ) throws VCertException{ + try { + RefreshTokenRequest request = new RefreshTokenRequest(refreshToken, clientId); + ResfreshTokenResponse response = tpp.refreshToken( request ); + + TokenInfo tokenInfo = new TokenInfo(response.accessToken(), response.refreshToken(), response.expire(), + response.tokenType(), response.scope(), "", + response.refreshUntil()); + + return tokenInfo; + }catch (FeignException.BadRequest e){ + throw new VCertException(e.getMessage()); + } + } + + @Override + public int revokeAccessToken( String accessToken ) throws VCertException { + + String requestHeader = getAuthHeaderValue(accessToken);//"Bearer "+accessToken; + + Response response = tpp.revokeToken( requestHeader ); + if(response.status() == 200){ + return response.status(); + }else{ + throw new VCertException(response.toString()); + } + + } @Override public ZoneConfiguration readZoneConfiguration(String zone, String accessToken) throws VCertException { VCertException.throwIfNull(zone, "empty zone"); ReadZoneConfigurationRequest request = new ReadZoneConfigurationRequest(getPolicyDN(zone)); - ReadZoneConfigurationResponse response = tpp.readZoneConfiguration(request, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + ReadZoneConfigurationResponse response = tpp.readZoneConfigurationToken(request, getAuthHeaderValue(accessToken)); ServerPolicy serverPolicy = response.policy(); Policy policy = serverPolicy.toPolicy(); ZoneConfiguration zoneConfig = serverPolicy.toZoneConfig(); @@ -148,7 +185,7 @@ public String requestCertificate(CertificateRequest request, ZoneConfiguration z zoneConfiguration.zoneId(this.zone); } CertificateRequestsPayload payload = prepareRequest(request, zoneConfiguration.zoneId()); - Tpp.CertificateRequestResponse response = tpp.requestCertificate(payload, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + Tpp.CertificateRequestResponse response = tpp.requestCertificateToken(payload, getAuthHeaderValue(accessToken)); String requestId = response.certificateDN(); request.pickupId(requestId); return requestId; @@ -296,7 +333,7 @@ public PEMCollection retrieveCertificate(CertificateRequest request, String acce private Tpp.CertificateRetrieveResponse retrieveCertificateOnce( CertificateRetrieveRequest certificateRetrieveRequest, String accessToken) throws VCertException { - return tpp.certificateRetrieve(certificateRetrieveRequest, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + return tpp.certificateRetrieveToken(certificateRetrieveRequest, getAuthHeaderValue(accessToken)); } @@ -308,7 +345,7 @@ private Tpp.CertificateSearchResponse searchCertificatesByFingerprint(String fin } private Tpp.CertificateSearchResponse searchCertificates(Map searchRequest, String accessToken) throws VCertException { - return tpp.searchCertificates(searchRequest, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + return tpp.searchCertificatesToken(searchRequest, getAuthHeaderValue(accessToken)); } @Override @@ -329,7 +366,7 @@ public void revokeCertificate(RevocationRequest request, String accessToken) thr } private Tpp.CertificateRevokeResponse revokeCertificate(CertificateRevokeRequest request, String accessToken) throws VCertException { - return tpp.revokeCertificate(request, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + return tpp.revokeCertificateToken(request, getAuthHeaderValue(accessToken)); } @Override @@ -365,7 +402,7 @@ public String renewCertificate(RenewalRequest request, String accessToken) throw renewalRequest.PKCS10(pkcs10); } - final Tpp.CertificateRenewalResponse response = tpp.renewCertificate(renewalRequest, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + final Tpp.CertificateRenewalResponse response = tpp.renewCertificateToken(renewalRequest, getAuthHeaderValue(accessToken)); if (!response.success()) { throw new VCertException(String.format("Certificate renewal error: %s", response.error())); } @@ -384,24 +421,11 @@ public ImportResponse importCertificate(ImportRequest request, String accessToke } private ImportResponse doImportCertificate(ImportRequest request, String accessToken) throws VCertException { - return tpp.importCertificate(request, HEADER_AUTHORIZATION, getAuthHeaderValue(accessToken)); + return tpp.importCertificateToken(request, getAuthHeaderValue(accessToken)); } @Override - public Policy readPolicyConfiguration(String zone) throws VCertException { + public Policy readPolicyConfiguration(String zone, String accessToken) throws VCertException { throw new UnsupportedOperationException("Method not yet implemented"); } - - @VisibleForTesting - String getPolicyDN(final String zone) { - String result = zone; - Matcher candidate = policy.matcher(zone); - if (!candidate.matches()) { - if (!policy.matcher(zone).matches()) { - result = "\\" + result; - } - result = "\\VED\\Policy" + result; - } - return result; - } } diff --git a/src/main/java/com/venafi/vcert/sdk/endpoint/Authentication.java b/src/main/java/com/venafi/vcert/sdk/endpoint/Authentication.java index 0d2480f..812b945 100644 --- a/src/main/java/com/venafi/vcert/sdk/endpoint/Authentication.java +++ b/src/main/java/com/venafi/vcert/sdk/endpoint/Authentication.java @@ -13,12 +13,12 @@ public class Authentication { @Builder.Default private String clientId = "vcert-sdk"; @Builder.Default - private String scope = "certificate:manage, revoke"; + private String scope = "certificate:manage,revoke"; @Builder.Default private String state = ""; @Builder.Default private String redirectUri =""; - + public Authentication() {} public Authentication(String user, String password, String apiKey) { diff --git a/src/main/java/com/venafi/vcert/sdk/endpoint/ConnectorType.java b/src/main/java/com/venafi/vcert/sdk/endpoint/ConnectorType.java index cfee686..b7f0958 100644 --- a/src/main/java/com/venafi/vcert/sdk/endpoint/ConnectorType.java +++ b/src/main/java/com/venafi/vcert/sdk/endpoint/ConnectorType.java @@ -3,5 +3,5 @@ public enum ConnectorType { TPP, CLOUD, - TPP_VEDAUTH + TPP_TOKEN } diff --git a/src/main/java/com/venafi/vcert/sdk/utils/FeignUtils.java b/src/main/java/com/venafi/vcert/sdk/utils/FeignUtils.java index 456056b..a4f397d 100644 --- a/src/main/java/com/venafi/vcert/sdk/utils/FeignUtils.java +++ b/src/main/java/com/venafi/vcert/sdk/utils/FeignUtils.java @@ -22,18 +22,27 @@ import feign.slf4j.Slf4jLogger; import com.venafi.vcert.sdk.Config; import com.venafi.vcert.sdk.connectors.tpp.Tpp; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.ssl.SSLContexts; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; public class FeignUtils { static Supplier gsonBuilderFactory = GsonBuilder::new; + //ALWAYS SET THIS FLAG TO FALSE BEFORE COMMIT. + //This flag allows to bypass SSL verification in local dev environments. + private static final boolean DEBUG = false; + public static T client(Class clazz, Config config) { GsonBuilder builder = gsonBuilderFor(clazz); Client client = config.client(); if (client == null) { if (config.proxy() == null) { - client = new Client.Default(null, null); + client = new Client.Default(getSSLSocketFactory(), null); } else { if (config.proxyUser() != null && config.proxyPassword() != null) { client = new Client.Proxied(null, null, config.proxy(), config.proxyUser(), @@ -66,6 +75,19 @@ private static GsonEncoder encoder(GsonBuilder builder) { return new GsonEncoder(builder.create()); } + private static SSLSocketFactory getSSLSocketFactory() { + try { + if(DEBUG){ + SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(); + return sslContext.getSocketFactory(); + } else{ + return null; + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + /** * @see origin diff --git a/src/test/java/com/venafi/vcert/sdk/VCertTknClientTest.java b/src/test/java/com/venafi/vcert/sdk/VCertTknClientTest.java new file mode 100644 index 0000000..d0590ed --- /dev/null +++ b/src/test/java/com/venafi/vcert/sdk/VCertTknClientTest.java @@ -0,0 +1,261 @@ +package com.venafi.vcert.sdk; + +import com.venafi.vcert.sdk.certificate.CertificateRequest; +import com.venafi.vcert.sdk.certificate.ImportRequest; +import com.venafi.vcert.sdk.certificate.RenewalRequest; +import com.venafi.vcert.sdk.certificate.RevocationRequest; +import com.venafi.vcert.sdk.connectors.TokenConnector; +import com.venafi.vcert.sdk.connectors.ZoneConfiguration; +import com.venafi.vcert.sdk.endpoint.Authentication; +import com.venafi.vcert.sdk.endpoint.ConnectorType; +import feign.FeignException; +import feign.Request; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.security.Security; +import java.util.Collection; +import java.util.HashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +public class VCertTknClientTest { + + private final TokenConnector connector = mock(TokenConnector.class); + private final VCertTknClient classUnderTest = new VCertTknClient(connector); + private final Request request = Request.create(Request.HttpMethod.GET, "https://base_url_test/", + new HashMap>(), Request.Body.empty()); + private final String mockToken = "abc1234567890"; + + @Test + @DisplayName("Create venafi tpp token client") + void getTypeTpp() throws VCertException { + final Config config = Config.builder().connectorType(ConnectorType.TPP_TOKEN) + .baseUrl("https://localhost/").build(); + + VCertTknClient client = new VCertTknClient(config); + + assertThat(client).isNotNull(); + assertThat(client.getType()).isEqualTo(ConnectorType.TPP_TOKEN); + assertThat(Security.getProviders()).anyMatch(p -> p instanceof BouncyCastleProvider); + } + + @Test + @DisplayName("Set baseurl") + void setBaseUrl() throws VCertException { + classUnderTest.setBaseUrl("https://base_url_test/"); + verify(connector).setBaseUrl("https://base_url_test/"); + } + + @Test + @DisplayName("Set venafi default zone") + void setZone() { + classUnderTest.setZone("test_zone"); + verify(connector).setZone("test_zone"); + } + + @Test + @DisplayName("Ping venafi service") + void ping() throws VCertException { + classUnderTest.ping(mockToken); + verify(connector).ping(mockToken); + } + + @Test + @DisplayName("Ping venafi service with server error") + void pingWithException() throws VCertException { + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .ping(mockToken); + + assertThrows(VCertException.class, () -> classUnderTest.ping(mockToken)); + } + + @Test + @DisplayName("Authenticated with venafi endpoint") + void authenticateWithException() throws VCertException { + final Authentication auth = mock(Authentication.class); + + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .getAccessToken(auth); + + assertThrows(VCertException.class, () -> classUnderTest.getAccessToken(auth)); + } + + @Test + @DisplayName("Authenticated with venafi endpoint with server error") + void authenticate() throws VCertException { + final Authentication auth = mock(Authentication.class); + classUnderTest.getAccessToken(auth); + + verify(connector).getAccessToken(auth); + } + + @Test + @DisplayName("Read zone configuration") + void readZoneConfiguration() throws VCertException { + classUnderTest.readZoneConfiguration("test_project\\test_zone", mockToken); + + verify(connector).readZoneConfiguration("test_project\\test_zone", mockToken); + } + + @Test + @DisplayName("Read zone configuration with server error") + void readZoneConfigurationWithServerError() throws VCertException { + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .readZoneConfiguration("test_project\\test_zone", mockToken); + + assertThrows(VCertException.class, + () -> classUnderTest.readZoneConfiguration("test_project\\test_zone", mockToken)); + } + + @Test + @DisplayName("Generate request") + void generateRequest() throws VCertException { + final ZoneConfiguration zoneConfiguration = mock(ZoneConfiguration.class); + final CertificateRequest certificateRequest = mock(CertificateRequest.class); + + classUnderTest.generateRequest(zoneConfiguration, certificateRequest, mockToken); + verify(connector).generateRequest(zoneConfiguration, certificateRequest, mockToken); + } + + @Test + @DisplayName("Generate request with server error") + void generateRequestWithServerError() throws VCertException { + final ZoneConfiguration zoneConfiguration = mock(ZoneConfiguration.class); + final CertificateRequest certificateRequest = mock(CertificateRequest.class); + + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .generateRequest(zoneConfiguration, certificateRequest, mockToken); + + assertThrows(VCertException.class, + () -> classUnderTest.generateRequest(zoneConfiguration, certificateRequest, mockToken)); + } + + @Test + @DisplayName("Request certificate") + void requestCertificate() throws VCertException { + final CertificateRequest certificateRequest = mock(CertificateRequest.class); + final ZoneConfiguration zoneConfiguration = mock(ZoneConfiguration.class); + zoneConfiguration.zoneId("test_zone"); + + classUnderTest.requestCertificate(certificateRequest, zoneConfiguration, mockToken); + + verify(connector).requestCertificate(certificateRequest, zoneConfiguration, mockToken); + } + + @Test + @DisplayName("Request certificate with server error") + void requestCertificateWithServerError() throws VCertException { + final CertificateRequest certificateRequest = mock(CertificateRequest.class); + final ZoneConfiguration zoneConfiguration = mock(ZoneConfiguration.class); + zoneConfiguration.zoneId("test_zone"); + + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .requestCertificate(certificateRequest, zoneConfiguration, mockToken); + + assertThrows(VCertException.class, + () -> classUnderTest.requestCertificate(certificateRequest, zoneConfiguration, mockToken)); + } + + @Test + @DisplayName("Retrieve certificate") + void retrieveCertificate() throws VCertException { + final CertificateRequest certificateRequest = mock(CertificateRequest.class); + + classUnderTest.retrieveCertificate(certificateRequest, mockToken); + verify(connector).retrieveCertificate(certificateRequest, mockToken); + + } + + @Test + @DisplayName("Retrieve certificate with server error") + void retrieveCertificateWithServerError() throws VCertException { + final CertificateRequest certificateRequest = mock(CertificateRequest.class); + + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .retrieveCertificate(certificateRequest, mockToken); + + assertThrows(VCertException.class, + () -> classUnderTest.retrieveCertificate(certificateRequest, mockToken)); + } + + @Test + @DisplayName("Revoke certificate") + void revokeCertificate() throws VCertException { + final RevocationRequest revocationRequest = mock(RevocationRequest.class); + + classUnderTest.revokeCertificate(revocationRequest, mockToken); + verify(connector).revokeCertificate(revocationRequest, mockToken); + } + + @Test + @DisplayName("Revoke certificate with server error") + void revokeCertificateWithServerError() throws VCertException { + final RevocationRequest revocationRequest = mock(RevocationRequest.class); + + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .revokeCertificate(revocationRequest, mockToken); + + assertThrows(VCertException.class, () -> classUnderTest.revokeCertificate(revocationRequest, mockToken)); + } + + + @Test + @DisplayName("Renew certificate") + void renewCertificate() throws VCertException { + final RenewalRequest renewalRequest = mock(RenewalRequest.class); + + classUnderTest.renewCertificate(renewalRequest, mockToken); + verify(connector).renewCertificate(renewalRequest, mockToken); + } + + @Test + @DisplayName("Renew certificate with server error") + void renewCertificateWithServerError() throws VCertException { + final RenewalRequest renewalRequest = mock(RenewalRequest.class); + + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .renewCertificate(renewalRequest, mockToken); + + assertThrows(VCertException.class, () -> classUnderTest.renewCertificate(renewalRequest, mockToken)); + } + + @Test + @DisplayName("Import certificate") + void importCertificate() throws VCertException { + final ImportRequest importRequest = mock(ImportRequest.class); + + classUnderTest.importCertificate(importRequest, mockToken); + verify(connector).importCertificate(importRequest, mockToken); + } + + @Test + @DisplayName("Import certificate with server error") + void importCertificateWithServerError() throws VCertException { + final ImportRequest importRequest = mock(ImportRequest.class); + + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .importCertificate(importRequest, mockToken); + + assertThrows(VCertException.class, () -> classUnderTest.importCertificate(importRequest, mockToken)); + } + + @Test + @DisplayName("Read policy configuration") + void readPolicyConfiguration() throws VCertException { + classUnderTest.readZoneConfiguration("test_project\\test_zone", mockToken); + verify(connector).readZoneConfiguration("test_project\\test_zone", mockToken); + } + + @Test + @DisplayName("Read policy configuration with server error") + void readPolicyConfigurationWithServerError() throws VCertException { + doThrow(new FeignException.InternalServerError("Error", request, "".getBytes())).when(connector) + .readPolicyConfiguration("test_zone", mockToken); + + assertThrows(VCertException.class, () -> classUnderTest.readPolicyConfiguration("test_zone", mockToken)); + } +} diff --git a/src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorAT.java b/src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorAT.java new file mode 100644 index 0000000..5ecd795 --- /dev/null +++ b/src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorAT.java @@ -0,0 +1,306 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import com.venafi.vcert.sdk.TestUtils; +import com.venafi.vcert.sdk.VCertException; +import com.venafi.vcert.sdk.certificate.*; +import com.venafi.vcert.sdk.connectors.ZoneConfiguration; +import com.venafi.vcert.sdk.endpoint.Authentication; +import feign.FeignException; +import org.apache.commons.codec.digest.DigestUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; + +import static com.venafi.vcert.sdk.TestUtils.getTestIps; +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class TppTokenConnectorAT { + + private static TppTokenConnector classUnderTest = new TppTokenConnector(Tpp.connect(System.getenv("TPP_TOKEN_URL"))); + private static TokenInfo info; + + @BeforeEach + void authenticate() throws VCertException { + Security.addProvider(new BouncyCastleProvider()); + Authentication authentication = Authentication.builder() + .user(System.getenv("TPPUSER")) + .password(System.getenv("TPPPASSWORD")) + .scope("certificate:manage,revoke,discover") + .build(); + + TokenInfo info = classUnderTest.getAccessToken(authentication); + + assertThat(info).isNotNull(); + assertThat(info.accessToken()).isNotNull(); + assertThat(info.refreshToken()).isNotNull(); + + TppTokenConnectorAT.info = info; + } + + @Test + void readZoneConfiguration() throws VCertException { + try { + classUnderTest.readZoneConfiguration(System.getenv("TPPZONE"), info.accessToken()); + } catch (FeignException fe) { + throw VCertException.fromFeignException(fe); + } + } + + @Test + void ping() throws VCertException { + assertThatCode(() -> classUnderTest.ping(info.accessToken())).doesNotThrowAnyException(); + } + + @Test + void generateRequest() throws VCertException, IOException { + String zone = System.getenv("TPPZONE"); + String commonName = TestUtils.randomCN(); + ZoneConfiguration zoneConfiguration = classUnderTest.readZoneConfiguration(zone, info.accessToken()); + CertificateRequest certificateRequest = new CertificateRequest() + .subject(new CertificateRequest.PKIXName().commonName(commonName) + .organization(Collections.singletonList("Venafi, Inc.")) + .organizationalUnit(Arrays.asList("Engineering", "Automated Tests")) + .country(Collections.singletonList("US")).locality(Collections.singletonList("SLC")) + .province(Collections.singletonList("Utah"))) + .dnsNames(Collections.singletonList(InetAddress.getLocalHost().getHostName())) + .ipAddresses(getTestIps()).keyType(KeyType.RSA).keyLength(2048); + + certificateRequest = classUnderTest.generateRequest(zoneConfiguration, certificateRequest, info.accessToken()); + + assertThat(certificateRequest.csr()).isNotEmpty(); + + PKCS10CertificationRequest request = (PKCS10CertificationRequest) new PEMParser( + new StringReader(new String(certificateRequest.csr()))).readObject(); + + // Values overridden by policy which is why they don't match the above values + String subject = request.getSubject().toString(); + + assertThat(subject).contains(format("CN=%s", commonName)); + } + + @Test + void requestCertificate() throws VCertException, SocketException, UnknownHostException { + String zoneName = System.getenv("TPPZONE"); + ZoneConfiguration zoneConfiguration = classUnderTest.readZoneConfiguration(zoneName, info.accessToken()); + CertificateRequest certificateRequest = new CertificateRequest() + .subject(new CertificateRequest.PKIXName().commonName(TestUtils.randomCN()) + .organization(Collections.singletonList("Venafi")) + .organizationalUnit(Collections.singletonList("Demo")) + .country(Collections.singletonList("GB")) + .locality(Collections.singletonList("Bracknell")) + .province(Collections.singletonList("Berkshire"))) + .dnsNames(Collections.singletonList(InetAddress.getLocalHost().getHostName())) + .ipAddresses(getTestIps()).keyType(KeyType.RSA).keyLength(2048); + + certificateRequest = classUnderTest.generateRequest(zoneConfiguration, certificateRequest, info.accessToken()); + CertificateRequest csrRequestOnly = new CertificateRequest().csr(certificateRequest.csr()); + assertThat(classUnderTest.requestCertificate(csrRequestOnly, zoneConfiguration, info.accessToken())).isNotNull(); + } + + @Test + void retrieveCertificate() throws VCertException, SocketException, UnknownHostException { + String zoneName = System.getenv("TPPZONE"); + ZoneConfiguration zoneConfiguration = classUnderTest.readZoneConfiguration(zoneName, info.accessToken()); + CertificateRequest certificateRequest = new CertificateRequest() + .subject(new CertificateRequest.PKIXName().commonName(TestUtils.randomCN()) + .organization(Collections.singletonList("Venafi")) + .organizationalUnit(Collections.singletonList("Demo")) + .country(Collections.singletonList("GB")) + .locality(Collections.singletonList("Bracknell")) + .province(Collections.singletonList("Berkshire"))) + .dnsNames(Collections.singletonList(InetAddress.getLocalHost().getHostName())) + .ipAddresses(getTestIps()).keyType(KeyType.RSA).keyLength(2048); + + certificateRequest = classUnderTest.generateRequest(zoneConfiguration, certificateRequest, info.accessToken()); + String certificateId = classUnderTest.requestCertificate(certificateRequest, zoneConfiguration, info.accessToken()); + assertThat(certificateId).isNotNull(); + + PEMCollection pemCollection = classUnderTest.retrieveCertificate(certificateRequest, info.accessToken()); + + assertThat(pemCollection.certificate()).isNotNull(); + assertThat(pemCollection.privateKey()).isNotNull(); + } + + @Test + void revokeCertificate() throws VCertException, SocketException, UnknownHostException { + String zoneName = System.getenv("TPPZONE"); + ZoneConfiguration zoneConfiguration = classUnderTest.readZoneConfiguration(zoneName, info.accessToken()); + CertificateRequest certificateRequest = new CertificateRequest() + .subject(new CertificateRequest.PKIXName().commonName(TestUtils.randomCN()) + .organization(Collections.singletonList("Venafi")) + .organizationalUnit(Collections.singletonList("Demo")) + .country(Collections.singletonList("GB")) + .locality(Collections.singletonList("Bracknell")) + .province(Collections.singletonList("Berkshire"))) + .dnsNames(Collections.singletonList(InetAddress.getLocalHost().getHostName())) + .ipAddresses(getTestIps()).keyType(KeyType.RSA).keyLength(2048); + + certificateRequest = classUnderTest.generateRequest(zoneConfiguration, certificateRequest, info.accessToken()); + String certificateId = classUnderTest.requestCertificate(certificateRequest, zoneConfiguration, info.accessToken()); + assertThat(certificateId).isNotNull(); + + // just wait for the certificate issuance + classUnderTest.retrieveCertificate(certificateRequest, info.accessToken()); + + RevocationRequest revocationRequest = new RevocationRequest(); + revocationRequest.reason("key-compromise"); + revocationRequest.certificateDN(certificateRequest.pickupId()); + + classUnderTest.revokeCertificate(revocationRequest, info.accessToken()); + } + + @Test + void renewCertificate() throws VCertException, UnknownHostException, SocketException, + CertificateException, NoSuchAlgorithmException { + String zoneName = System.getenv("TPPZONE"); + String commonName = TestUtils.randomCN(); + ZoneConfiguration zoneConfiguration = classUnderTest.readZoneConfiguration(zoneName, info.accessToken()); + CertificateRequest certificateRequest = new CertificateRequest() + .subject(new CertificateRequest.PKIXName().commonName(commonName) + .organization(Collections.singletonList("Venafi")) + .organizationalUnit(Collections.singletonList("Demo")) + .country(Collections.singletonList("GB")) + .locality(Collections.singletonList("Bracknell")) + .province(Collections.singletonList("Berkshire"))) + .dnsNames(Collections.singletonList(InetAddress.getLocalHost().getHostName())) + .ipAddresses(getTestIps()).keyType(KeyType.RSA).keyLength(2048); + + certificateRequest = classUnderTest.generateRequest(zoneConfiguration, certificateRequest, info.accessToken()); + String certificateId = classUnderTest.requestCertificate(certificateRequest, zoneConfiguration, info.accessToken()); + assertThat(certificateId).isNotNull(); + + PEMCollection pemCollection = classUnderTest.retrieveCertificate(certificateRequest, info.accessToken()); + X509Certificate cert = (X509Certificate) pemCollection.certificate(); + + String thumbprint = DigestUtils.sha1Hex(cert.getEncoded()).toUpperCase(); + + CertificateRequest certificateRequestToRenew = new CertificateRequest() + .subject(new CertificateRequest.PKIXName().commonName(commonName) + .organization(Collections.singletonList("Venafi")) + .organizationalUnit(Collections.singletonList("Demo")) + .country(Collections.singletonList("GB")) + .locality(Collections.singletonList("Bracknell")) + .province(Collections.singletonList("Berkshire"))) + .dnsNames(Collections.singletonList(InetAddress.getLocalHost().getHostName())) + .ipAddresses(getTestIps()).keyType(KeyType.RSA).keyLength(2048); + classUnderTest.generateRequest(zoneConfiguration, certificateRequestToRenew, info.accessToken()); + + String renewRequestId = classUnderTest.renewCertificate( + new RenewalRequest().request(certificateRequestToRenew).thumbprint(thumbprint), info.accessToken()); + + assertThat(renewRequestId).isNotNull(); + } + + @Test + void importCertificate() throws VCertException { + final String cert = "-----BEGIN CERTIFICATE-----\n" + + "MIIDdjCCAl6gAwIBAgIRAPqSZQ04IjWgO2rwIDRcOY8wDQYJKoZIhvcNAQENBQAw\n" + + "gYAxCzAJBgNVBAYTAlVTMQ0wCwYDVQQIDARVdGFoMRcwFQYDVQQHDA5TYWx0IExh\n" + + "a2UgQ2l0eTEPMA0GA1UECgwGVmVuYWZpMRswGQYDVQQLDBJOT1QgRk9SIFBST0RV\n" + + "Q1RJT04xGzAZBgNVBAMMElZDZXJ0IFRlc3QgTW9kZSBDQTAeFw0xODA5MTIxMzUw\n" + + "MzNaFw0xODEyMTExMzUwMzNaMCQxIjAgBgNVBAMTGWltcG9ydC52ZW5hZmkuZXhh\n" + + "bXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChjQk0jSE5\n" + + "ktVdH8bAM0QCpGs1rOOVMmRkMc7d4hQ6bTlFlIypMq9t+1O2Z8i4fiKDS7vSBmBo\n" + + "WBgN9e0fbAnKEvBIcNLBS4lmwzRDxDCrNV3Dr5s+yJtUw9V2XBwiXbtW7qs5+c0O\n" + + "y7a2S/5HudXUlAuXf7SF4MboMMpHRg+UkyA4j0peir8PtmlJjlYBt3lZdaeLlD6F\n" + + "EIlIVQFZ6ulUF/kULhxhTUl2yNUUzJ/bqJlhFU6pkL+GoW1lnaZ8FYXwA1EKYyRk\n" + + "DYL581eqvIBJY9tCNWbOdU1r+5wR4OOKe/WWWhcDC6nL/M8ZYhfQg1nHoD58A8Dk\n" + + "H4AAt8A3EZpdAgMBAAGjRjBEMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB\n" + + "/wQCMAAwHwYDVR0jBBgwFoAUzqRFDvLX0mz4AjPb45tLGavm8AcwDQYJKoZIhvcN\n" + + "AQENBQADggEBABa4wqh+A63O5PHrdUCBSmQs9ve/oIXj561VBmqXkTHLrtKtbtcA\n" + + "yvsMi8RD8BibBAsUCljkCmLoQD/XeQFtsPlMAxisSMYhChh58008CIYDR8Nf/qoe\n" + + "YfzdMB/3VWCqTn9KGF8aMKeQvbFvuqmbtdCv//eYe6mNe2fa/x6PSdGMi4BPmjUC\n" + + "PmBT4p1iwMtu8LnL4UM4awjmmExR4X4rafcyGEbf0D/CRfhDLSwxvrrVcWd6TMMY\n" + + "HPZ/pw//+UrVLgEEsyM2zwf+LokbszPBvPAtHMJtr7Pnq2MQtEEkLfPqOWG3ol1H\n" + + "t+4v2LIW1q4GkwOUjPqgyIaJC5jj5pH9/g8=\n" + "-----END CERTIFICATE-----"; + + final String pk = "-----BEGIN RSA PRIVATE KEY-----\n" + + "MIIEpAIBAAKCAQEAoY0JNI0hOZLVXR/GwDNEAqRrNazjlTJkZDHO3eIUOm05RZSM\n" + + "qTKvbftTtmfIuH4ig0u70gZgaFgYDfXtH2wJyhLwSHDSwUuJZsM0Q8QwqzVdw6+b\n" + + "PsibVMPVdlwcIl27Vu6rOfnNDsu2tkv+R7nV1JQLl3+0heDG6DDKR0YPlJMgOI9K\n" + + "Xoq/D7ZpSY5WAbd5WXWni5Q+hRCJSFUBWerpVBf5FC4cYU1JdsjVFMyf26iZYRVO\n" + + "qZC/hqFtZZ2mfBWF8ANRCmMkZA2C+fNXqryASWPbQjVmznVNa/ucEeDjinv1lloX\n" + + "Awupy/zPGWIX0INZx6A+fAPA5B+AALfANxGaXQIDAQABAoIBAE7of6WOhbsEcHkz\n" + + "CzZYFBEiVEd8chEu8wBJn9ybD/xV21KUM3x1iGC1EPeYi98ppRvygwQcHzz4Qo+X\n" + + "HsJpWAK+62TGzvqhNbTfBglPq+IEiA8MGE07WTu3B+3vIcLbe6UDoNkJndJrSIyU\n" + + "Y9iO+dYClgLi2r9FwoIpSrQzkWqlB3edle4Nq1WABtWTOSDYysz1gk0KrLmQQfXP\n" + + "CPiwkL0SjB+sfbOiVX0B2liV2oxJ5VZWNo/250wFcvrcYrgTNtEVNMXtpN0tnRMH\n" + + "NPwnY+B9WGu/NVhtvOcOTPHq9xQhbmBCS1axikizCaIqEOyegdeDJ4ASJnVybfCA\n" + + "KzjoCpUCgYEAwOmeEvzSP8hCKtLPU8QDBA1y+mEvZMwBY4qr3hfqv3qa0QmFvxkk\n" + + "7Ubmy2oFOoUnVgnhRzAf/bajbkz4ScUgd2JrUdIEhNNVwDn/llnS/UHBlZY++BtW\n" + + "mvyon9ObXgPNPoHcJqzrqARu8PPJQEsZ+xjxM/gyif3prn6Uct6R8B8CgYEA1mHd\n" + + "Astwht39z16FoX9rQRGgx64Z0nesfTjl+4mkypz6ukkcfU1GjobqEG3k666+OJk1\n" + + "SRs8s20Pahrh21LO5x/QtvChhZ+nIedqlhBlNH9uUJI9ChbUN0luetiSPT8F5aqg\n" + + "gZMY13K5icAQ+98EcNwl7ZhVPq0BvLlbqTWi9gMCgYEAjtVqoQxob6lKtIJZ19+t\n" + + "i/aZRyFmAe+6p4UpM8vpl9SjhFrUmGV5neV9ROc+79FfCqlOD3NmfGgaIbUDsTsv\n" + + "irVoWLBzgBUpzKYkw6HGQpXJS4RvIyy6tw6Tm6MFylpuQPXNlyU5ZrHBos4eGGiC\n" + + "2BPjo2MFqH5D41r9dv+sdmkCgYEAtSJYx3y2pe04/xYhGFP9fivzyeMrRC4DWoZR\n" + + "oxcoWl0KZ41QefppzBDoAVuo2Q17AX1JjWxq/DsAlCkEffhYguXZxkhIYQuE/lt2\n" + + "LjbKG/IzdfYphrXFNrVfmIIWBZOTWvqwxOpRSfBQHbhfYUCMkwMfNMHJ/LvWxOtk\n" + + "K/L6rpsCgYB6p9RU2kXexAh9kUpbGqVeJBoIh6ArXHgepESE/7dPw26D0DM0mef0\n" + + "X1MasxN3JF7ZsSGfcCLXnICSJHuNTy9WztqF3hUbQwYd9vmZxtzAo5/fK4DVAaXS\n" + + "ZtIVl/CH/az0xqLKWIlmWOip9SfUVlZdgege+PlQtRqoFVOsH8+MEg==\n" + + "-----END RSA PRIVATE KEY-----"; + + String zone = System.getenv("TPPZONE"); + ImportRequest importRequest = new ImportRequest(); + importRequest.certificateData(cert); + importRequest.privateKeyData(pk); + importRequest.policyDN(classUnderTest.getPolicyDN(zone)); + + + ImportResponse response = classUnderTest.importCertificate(importRequest, info.accessToken()); + assertThat(response).isNotNull(); + assertThat(response.certificateDN()).isNotNull(); + assertThat(response.certificateVaultId()).isNotNull(); + assertThat(response.privateKeyVaultId()).isNotNull(); + + } + + @Test + void readPolicyConfiguration() { + assertThrows(UnsupportedOperationException.class, + () -> classUnderTest.readPolicyConfiguration("zone", info.accessToken())); + } + + @Test + void refreshToken() throws VCertException{ + TokenInfo refreshInfo = classUnderTest.refreshAccessToken(info.refreshToken(), "vcert-sdk"); + + assertThat(refreshInfo).isNotNull(); + assertThat(refreshInfo.accessToken()).isNotEqualTo(info.accessToken()); + } + + @Test + void refreshTokenInvalid() throws VCertException{ + assertThrows(VCertException.class, () -> classUnderTest.refreshAccessToken("1234-1234-12345-123", "vcert-sdk")); + } + + @Test + void revokeToken() throws VCertException{ + int status = classUnderTest.revokeAccessToken(info.accessToken()); + assertThat(status).isEqualTo(200); + } + + @Test + void revokeTokenInvalid() throws VCertException{ + assertThrows(VCertException.class, () ->classUnderTest.revokeAccessToken("1234-1234")); + } +} diff --git a/src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorIT.java b/src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorIT.java new file mode 100644 index 0000000..be6f7bf --- /dev/null +++ b/src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorIT.java @@ -0,0 +1,96 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import com.github.jenspiegsa.wiremockextension.InjectServer; +import com.github.jenspiegsa.wiremockextension.WireMockExtension; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.venafi.vcert.sdk.VCertException; +import com.venafi.vcert.sdk.connectors.ZoneConfiguration; +import com.venafi.vcert.sdk.endpoint.Authentication; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static com.venafi.vcert.sdk.SignatureAlgorithm.SHA256WithRSA; +import static com.venafi.vcert.sdk.certificate.EllipticCurve.*; +import static com.venafi.vcert.sdk.certificate.EllipticCurve.EllipticCurveP521; +import static com.venafi.vcert.sdk.certificate.KeyType.ECDSA; +import static com.venafi.vcert.sdk.certificate.KeyType.RSA; +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(WireMockExtension.class) +public class TppTokenConnectorIT { + + @InjectServer + private WireMockServer serverMock; + private TppTokenConnector classUnderTest; + private TokenInfo info; + + @BeforeEach + void setup() throws VCertException { + classUnderTest = new TppTokenConnector(Tpp.connect("http://localhost:" + serverMock.port() + "/")); + // String.format() + Authentication auth = Authentication.builder() + .user("user") + .password("pass") + .build(); + info = classUnderTest.getAccessToken(auth); + } + + @Test + @DisplayName("should start and inject server.") + void shouldInjectServer() { + assertThat(serverMock).isNotNull(); + assertThat(serverMock.isRunning()).describedAs("server expected to be running.").isTrue(); + } + + @Test + void authenticate() throws VCertException { + // call in @BeforeEach + assertThat(info.accessToken()).isEqualTo("12345678-1234-1234-1234-123456789012"); + assertThat(info.refreshToken()).isEqualTo("abcdefgh-abcd-abcd-abcd-abcdefghijkl"); + } + + @Test + void readZoneConfiguration() throws VCertException { + ZoneConfiguration zoneConfiguration = classUnderTest.readZoneConfiguration("tag", info.accessToken()); + + assertThat(zoneConfiguration).isNotNull(); + assertThat(zoneConfiguration.organization()).isNull(); + assertThat(zoneConfiguration.organizationalUnit()).isNotNull(); + assertThat(zoneConfiguration.organizationalUnit()).isEmpty(); + assertThat(zoneConfiguration.country()).isNull(); + assertThat(zoneConfiguration.province()).isNull(); + assertThat(zoneConfiguration.locality()).isNull(); + assertThat(zoneConfiguration.policy()).isNotNull(); + assertThat(zoneConfiguration.policy().subjectCNRegexes()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().subjectORegexes()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().subjectOURegexes()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().subjectSTRegexes()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().subjectLRegexes()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().subjectCRegexes()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().allowedKeyConfigurations()).isNotNull(); + assertThat(zoneConfiguration.policy().allowedKeyConfigurations()).hasSize(2); + assertThat(zoneConfiguration.policy().allowedKeyConfigurations().get(0).keyType()) + .isEqualTo(RSA); + assertThat(zoneConfiguration.policy().allowedKeyConfigurations().get(0).keySizes()) + .containsExactly(512, 1024, 2048, 4096, 8192); + assertThat(zoneConfiguration.policy().allowedKeyConfigurations().get(0).keyCurves()).isNull(); + assertThat(zoneConfiguration.policy().allowedKeyConfigurations().get(1).keyType()) + .isEqualTo(ECDSA); + assertThat(zoneConfiguration.policy().allowedKeyConfigurations().get(1).keySizes()).isNull(); + assertThat(zoneConfiguration.policy().allowedKeyConfigurations().get(1).keyCurves()) + .containsExactly(EllipticCurveP224, EllipticCurveP256, EllipticCurveP384, + EllipticCurveP521); + assertThat(zoneConfiguration.policy().dnsSanRegExs()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().ipSanRegExs()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().emailSanRegExs()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().uriSanRegExs()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().upnSanRegExs()).containsExactly(".*"); + assertThat(zoneConfiguration.policy().allowWildcards()).isTrue(); + assertThat(zoneConfiguration.policy().allowKeyReuse()).isFalse(); + assertThat(zoneConfiguration.hashAlgorithm()).isEqualTo(SHA256WithRSA); + assertThat(zoneConfiguration.customAttributeValues()).isNotNull(); + assertThat(zoneConfiguration.customAttributeValues()).isEmpty(); + } +} diff --git a/src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorTest.java b/src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorTest.java new file mode 100644 index 0000000..5ea4bfb --- /dev/null +++ b/src/test/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnectorTest.java @@ -0,0 +1,262 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import com.venafi.vcert.sdk.VCertException; +import com.venafi.vcert.sdk.certificate.CertificateRequest; +import com.venafi.vcert.sdk.certificate.RenewalRequest; +import com.venafi.vcert.sdk.connectors.LockableValue; +import com.venafi.vcert.sdk.connectors.LockableValues; +import com.venafi.vcert.sdk.connectors.ServerPolicy; +import com.venafi.vcert.sdk.connectors.ZoneConfiguration; +import com.venafi.vcert.sdk.endpoint.Authentication; +import feign.FeignException; +import feign.Request; +import feign.Response; +import org.apache.commons.lang3.RandomStringUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.Security; +import java.time.Instant; +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class TppTokenConnectorTest { + + private static final Logger logger = LoggerFactory.getLogger(TppConnectorTest.class); + private static final String ACCESS_TOKEN = "12345678-1234-1234-1234-123456789012"; + private static final String HEADER_AUTHORIZATION = "Bearer " + ACCESS_TOKEN; + private static final String REFRESH_TOKEN = "abcdefgh-abcd-abcd-abcd-abcdefghijkl"; + + @Mock + private Tpp tpp; + private TppTokenConnector classUnderTest; + private TokenInfo info; + + @Captor + private ArgumentCaptor certificateRenewalRequestArgumentCaptor; + + @BeforeEach + void setUp() throws VCertException { + this.classUnderTest = new TppTokenConnector(tpp); + + AuthorizeTokenResponse response = + new AuthorizeTokenResponse().accessToken(ACCESS_TOKEN). + refreshToken(REFRESH_TOKEN); + when(tpp.authorizeToken(any(TppTokenConnector.AuthorizeTokenRequest.class))).thenReturn(response); + + Authentication authentication = Authentication.builder().user("user").password("pass").build(); + info = classUnderTest.getAccessToken(authentication); + } + + @Test + void canGetAuthToken() throws VCertException { + assertNotNull(info.accessToken()); + assertNotNull(info.refreshToken()); + } + + @Test + @DisplayName("Request a certificate from TPP") + void requestCertificate() throws VCertException { + Security.addProvider(new BouncyCastleProvider()); + + TppTokenConnector.ReadZoneConfigurationRequest expectedRZCRequest = + new TppTokenConnector.ReadZoneConfigurationRequest("\\VED\\Policy\\\\VED\\Policy\\myZone"); + when( + tpp.readZoneConfigurationToken(eq(expectedRZCRequest), eq(HEADER_AUTHORIZATION))) + .thenReturn( + new TppTokenConnector.ReadZoneConfigurationResponse() + .policy( + new ServerPolicy() + .subject(new ServerPolicy.Subject() + .organizationalUnit(new LockableValues(false, + Collections.singletonList("OU"))) + .state(new LockableValue<>(false, "state")) + .city(new LockableValue<>(false, "city")) + .country(new LockableValue<>(false, "country")) + .organization(new LockableValue<>(false, "organization"))) + + + .keyPair(new ServerPolicy.KeyPair(new LockableValue<>(false, "keyAlgo"), + new LockableValue<>(false, 1024), null)))); + when(tpp.requestCertificateToken(any(TppTokenConnector.CertificateRequestsPayload.class), eq(HEADER_AUTHORIZATION))) + .thenReturn(new Tpp.CertificateRequestResponse().certificateDN("reqId")); + String zoneTag = "myZone"; + ZoneConfiguration zoneConfig = + classUnderTest.readZoneConfiguration(classUnderTest.getPolicyDN(zoneTag), info.accessToken()); + String cn = String.format("t%d-%s.venafi.xample.com", Instant.now().getEpochSecond(), + RandomStringUtils.randomAlphabetic(4).toLowerCase()); + CertificateRequest request = new CertificateRequest() + .subject(new CertificateRequest.PKIXName().commonName(cn) + .organization(Collections.singletonList("Venafi, Inc.")) + .organizationalUnit(Collections.singletonList("Automated Tests")) + .locality(Collections.singletonList("Las Vegas")) + .province(Collections.singletonList("Nevada")).country(Collections.singletonList("US"))) + .friendlyName(cn).keyLength(512); + classUnderTest.generateRequest(zoneConfig, request, info.accessToken()); + logger.info("getPolicyDN(ZoneTag) = %s", classUnderTest.getPolicyDN(zoneTag)); + + ZoneConfiguration zoneConfiguration = new ZoneConfiguration(); + zoneConfiguration.zoneId(classUnderTest.getPolicyDN(zoneTag)); + String requestId = classUnderTest.requestCertificate(request, zoneConfiguration, info.accessToken()); + assertEquals("reqId", requestId); + } + + + @Test + @DisplayName("Renew Certificate with an empty request") + void renewCertificateWithEmptyRequest() throws VCertException { + final RenewalRequest renewalRequest = mock(RenewalRequest.class); + final Throwable throwable = + assertThrows(VCertException.class, () -> classUnderTest.renewCertificate(renewalRequest, info.accessToken())); + + assertThat(throwable.getMessage()).contains("CertificateDN or Thumbprint required"); + } + + @Test + @DisplayName("Renew Certificate with fingerprint not found") + void renewCertificateWithFingeprintNoSearchResults() throws VCertException { + final RenewalRequest renewalRequest = mock(RenewalRequest.class); + final Tpp.CertificateSearchResponse certificateSearchResponse = + mock(Tpp.CertificateSearchResponse.class); + + when(renewalRequest.thumbprint()).thenReturn("1111:1111:1111:1111"); + when(tpp.searchCertificatesToken(any(), eq(HEADER_AUTHORIZATION))).thenReturn(certificateSearchResponse); + + final Throwable throwable = + assertThrows(VCertException.class, () -> classUnderTest.renewCertificate(renewalRequest, info.accessToken())); + assertThat(throwable.getMessage()).contains("No certificate found using fingerprint"); + } + + @Test + @DisplayName("Renew Certificate multiple certificates for the fingerprint") + void renewCertificateWithFingerPrintMultipleCertificates() throws VCertException { + final RenewalRequest renewalRequest = mock(RenewalRequest.class); + final Tpp.CertificateSearchResponse certificateSearchResponse = + mock(Tpp.CertificateSearchResponse.class); + + when(renewalRequest.thumbprint()).thenReturn("1111:1111:1111:1111"); + when(tpp.searchCertificatesToken(any(), eq(HEADER_AUTHORIZATION))).thenReturn(certificateSearchResponse); + when(certificateSearchResponse.certificates()) + .thenReturn(Arrays.asList(new Tpp.Certificate(), new Tpp.Certificate())); + + final Throwable throwable = + assertThrows(VCertException.class, () -> classUnderTest.renewCertificate(renewalRequest, info.accessToken())); + assertThat(throwable.getMessage()).contains("More than one certificate was found"); + } + + @Test + @DisplayName("Renew Certificate with fingerprint") + void renewCertificateWithFingerPrint() throws VCertException { + final RenewalRequest renewalRequest = mock(RenewalRequest.class); + final Tpp.CertificateSearchResponse certificateSearchResponse = + mock(Tpp.CertificateSearchResponse.class); + final Tpp.Certificate certificate = mock(Tpp.Certificate.class); + final Tpp.CertificateRenewalResponse certificateRenewalResponse = + mock(Tpp.CertificateRenewalResponse.class); + + when(renewalRequest.thumbprint()).thenReturn("1111:1111:1111:1111"); + when(tpp.searchCertificatesToken(any(), eq(HEADER_AUTHORIZATION))).thenReturn(certificateSearchResponse); + when(certificateSearchResponse.certificates()).thenReturn(Arrays.asList(certificate)); + when(certificate.certificateRequestId()).thenReturn("test_certificate_requestid"); + when(tpp.renewCertificateToken(certificateRenewalRequestArgumentCaptor.capture(), any())) + .thenReturn(certificateRenewalResponse); + when(certificateRenewalResponse.success()).thenReturn(true); + + String result = classUnderTest.renewCertificate(renewalRequest, info.accessToken()); + assertThat(result).isEqualTo("test_certificate_requestid"); + } + + @Test + @DisplayName("Renew Certificate with DN") + void renewCertificateWithDN() throws VCertException { + final RenewalRequest renewalRequest = mock(RenewalRequest.class); + final Tpp.CertificateRenewalResponse certificateRenewalResponse = + mock(Tpp.CertificateRenewalResponse.class); + + when(renewalRequest.certificateDN()).thenReturn("certificateDN"); + when(tpp.renewCertificateToken(certificateRenewalRequestArgumentCaptor.capture(), any())) + .thenReturn(certificateRenewalResponse); + when(certificateRenewalResponse.success()).thenReturn(true); + + String result = classUnderTest.renewCertificate(renewalRequest, info.accessToken()); + assertThat(result).isEqualTo("certificateDN"); + } + + @Test + @DisplayName("Refresh access token") + void refreshAccessToken() throws VCertException{ + final ResfreshTokenResponse tokenResponse = mock(ResfreshTokenResponse.class); + + when(tokenResponse.accessToken()).thenReturn("123456"); + when(tokenResponse.refreshToken()).thenReturn("abcdef"); + + when(tpp.refreshToken(any(AbstractTppConnector.RefreshTokenRequest.class))).thenReturn(tokenResponse); + + TokenInfo newInfo = classUnderTest.refreshAccessToken(info.refreshToken(), "vcert-sdk"); + assertNotNull(newInfo); + assertNotNull(newInfo.accessToken()); + assertNotNull(newInfo.refreshToken()); + + assertThat(newInfo.accessToken()).isNotEqualTo(info.accessToken()); + assertThat(newInfo.refreshToken()).isNotEqualTo(info.refreshToken()); + } + + @Test + @DisplayName("Refresh invalid access token") + void refreshAccessTokenInvalid(){ + final Request request = Request.create(Request.HttpMethod.POST, "", new HashMap>(), null); + + when(tpp.refreshToken(any(AbstractTppConnector.RefreshTokenRequest.class))).thenThrow(new FeignException.BadRequest("400 Grant has been revoked, has expired, or the refresh token is invalid", request, null)); + + final Throwable throwable = + assertThrows(VCertException.class, () -> classUnderTest.refreshAccessToken(info.accessToken(), "vcert-sdk")); + logger.info("VCertException = %s", throwable.getMessage()); + + assertThat(throwable.getMessage()).contains("Grant has been revoked, has expired, or the refresh token is invalid"); + } + + @Test + @DisplayName("Revoke access token") + void revokeAccessToken() throws VCertException{ + final Request request = Request.create(Request.HttpMethod.GET, "", new HashMap>(), null); + + final Response response = Response.builder().status(200).request(request).build(); + + when(tpp.revokeToken(eq(HEADER_AUTHORIZATION))).thenReturn(response); + + int responseValue = classUnderTest.revokeAccessToken(info.accessToken()); + + assertThat(responseValue).isEqualTo(200); + } + + @Test + @DisplayName("Revoke access token invalid") + void revokeAccessTokenInvalid(){ + final Request request = Request.create(Request.HttpMethod.GET, "", new HashMap>(), null); + + final Response response = Response.builder().status(202).request(request).build(); + + when(tpp.revokeToken(eq(HEADER_AUTHORIZATION))).thenReturn(response); + + Throwable throwable = assertThrows(VCertException.class, () ->classUnderTest.revokeAccessToken(info.accessToken())); + assertThat(throwable.getMessage()).contains("202"); + } +} diff --git a/src/test/resources/mappings/tpp.token.auth.json b/src/test/resources/mappings/tpp.token.auth.json new file mode 100644 index 0000000..1bdcd85 --- /dev/null +++ b/src/test/resources/mappings/tpp.token.auth.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "POST", + "url": "/vedauth/authorize/oauth" + }, + "response": { + "status": 200, + "jsonBody":{ + "access_token": "12345678-1234-1234-1234-123456789012", + "refresh_token": "abcdefgh-abcd-abcd-abcd-abcdefghijkl", + "expires": 1594504904, + "token_type": "Bearer", + "scope": "certificate:manage,revoke", + "identity": "local:{500abcf0-8e70-4103-a97b-5a690d4d00d0}", + "refresh_until": 1595023304 + } + } +} \ No newline at end of file diff --git a/src/test/resources/mappings/tpp.token.certificates.checkpolicy.json b/src/test/resources/mappings/tpp.token.certificates.checkpolicy.json new file mode 100644 index 0000000..1393583 --- /dev/null +++ b/src/test/resources/mappings/tpp.token.certificates.checkpolicy.json @@ -0,0 +1,81 @@ +{ + "request": { + "method": "POST", + "url": "/vedsdk/certificates/checkpolicy", + "headers": { + "Authorization": { + "equalTo": "Bearer 12345678-1234-1234-1234-123456789012" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"PolicyDN\":\"\\\\VED\\\\Policy\\\\tag\"}" + } + ] + }, + "response": { + "status": 200, + "jsonBody": { + "Error": null, + "Policy": { + "CertificateAuthority": { + "Locked": false, + "Value": null + }, + "CsrGeneration": { + "Locked": false, + "Value": "ServiceGenerated" + }, + "KeyGeneration": { + "Locked": false, + "Value": "Central" + }, + "KeyPair": { + "KeyAlgorithm": { + "Locked": false, + "Value": "RSA" + }, + "KeySize": { + "Locked": false, + "Value": 2048 + } + }, + "ManagementType": { + "Locked": false, + "Value": "Unassigned" + }, + "PrivateKeyReuseAllowed": false, + "SubjAltNameDnsAllowed": true, + "SubjAltNameEmailAllowed": true, + "SubjAltNameIpAllowed": true, + "SubjAltNameUpnAllowed": true, + "SubjAltNameUriAllowed": true, + "Subject": { + "City": { + "Locked": false, + "Value": null + }, + "Country": { + "Locked": false, + "Value": null + }, + "Organization": { + "Locked": false, + "Value": null + }, + "OrganizationalUnit": { + "Locked": false, + "Values": [] + }, + "State": { + "Locked": false, + "Value": null + } + }, + "UniqueSubjectEnforced": false, + "WhitelistedDomains": [], + "WildcardsAllowed": true + } + } + } +} \ No newline at end of file