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/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 ae58650..b59f75b 100644 --- a/src/main/java/com/venafi/vcert/sdk/VCertClient.java +++ b/src/main/java/com/venafi/vcert/sdk/VCertClient.java @@ -21,9 +21,9 @@ 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()); @@ -42,7 +42,7 @@ public VCertClient(Config config) throws VCertException { 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 @@ -245,5 +245,4 @@ public Policy readPolicyConfiguration(String zone) throws VCertException { } return null; } - } 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/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 new file mode 100644 index 0000000..05922a6 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AbstractTppConnector.java @@ -0,0 +1,180 @@ +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; +import lombok.Data; +import lombok.Getter; + +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_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 + 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 final 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; + } + + @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 { + private String username; + 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 { + 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/AuthorizeTokenResponse.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeTokenResponse.java new file mode 100644 index 0000000..91ed936 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/AuthorizeTokenResponse.java @@ -0,0 +1,31 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import com.google.gson.annotations.SerializedName; + +import lombok.Data; + +@Data +public class AuthorizeTokenResponse { + + @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..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 @@ -23,46 +23,93 @@ public interface Tpp { AuthorizeResponse authorize(TppConnector.AuthorizeRequest authorizeRequest); @RequestLine("POST certificates/checkpolicy") - @Headers({"Content-Type: application/json", "x-venafi-api-key: {apiKey}"}) + @Headers({"Content-Type: application/json", "x-venafi-api-key: {value}"}) TppConnector.ReadZoneConfigurationResponse readZoneConfiguration( - TppConnector.ReadZoneConfigurationRequest readZoneConfigurationRequest, - @Param("apiKey") String apiKey); + TppConnector.ReadZoneConfigurationRequest readZoneConfigurationRequest, @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", "x-venafi-api-key: {value}"}) + CertificateRequestResponse requestCertificate(TppConnector.CertificateRequestsPayload payload, @Param("value") String value); @RequestLine("GET certificates/") - @Headers("x-venafi-api-key: {apiKey}") - Tpp.CertificateSearchResponse searchCertificates(@QueryMap Map query, - @Param("apiKey") String apiKey); + @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", "x-venafi-api-key: {apiKey}"}) + @Headers({"Content-Type: application/json", "x-venafi-api-key: {value}"}) CertificateRetrieveResponse certificateRetrieve( - TppConnector.CertificateRetrieveRequest certificateRetrieveRequest, - @Param("apiKey") String apiKey); + TppConnector.CertificateRetrieveRequest certificateRetrieveRequest, @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", "x-venafi-api-key: {value}"}) + Tpp.CertificateRevokeResponse revokeCertificate(TppConnector.CertificateRevokeRequest request, @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", "x-venafi-api-key: {value}"}) + Tpp.CertificateRenewalResponse renewCertificate(TppConnector.CertificateRenewalRequest request, @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", "x-venafi-api-key: {value}"}) + ImportResponse importCertificate(ImportRequest request, @Param("value") String value); @RequestLine("GET /") - @Headers("x-venafi-api-key: {apiKey}") - Response ping(@Param("apiKey") String apiKey); + @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 8262d1d..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,39 +43,14 @@ import com.venafi.vcert.sdk.utils.Is; -public class TppConnector implements Connector { - - private static final Pattern policy = Pattern.compile("^\\\\VED\\\\Policy"); - private final Tpp tpp; - +public class TppConnector extends AbstractTppConnector implements Connector { @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"; - - // 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) { - this.tpp = tpp; + super(tpp); } @Override @@ -122,7 +92,7 @@ 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(); @@ -410,7 +380,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()); @@ -421,7 +392,6 @@ public String renewCertificate(RenewalRequest request) throws VCertException { return certificateDN; } - @Override public ImportResponse importCertificate(ImportRequest request) throws VCertException { if (isBlank(request.policyDN())) { @@ -439,103 +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; - } - - @Data - @AllArgsConstructor - static class AuthorizeRequest { - private String username; - private String password; - } - - @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/TppTokenConnector.java b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnector.java new file mode 100644 index 0000000..856d4f1 --- /dev/null +++ b/src/main/java/com/venafi/vcert/sdk/connectors/tpp/TppTokenConnector.java @@ -0,0 +1,431 @@ +package com.venafi.vcert.sdk.connectors.tpp; + +import com.venafi.vcert.sdk.VCertException; +import com.venafi.vcert.sdk.certificate.*; +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; +import java.text.MessageFormat; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.TimeUnit; + +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 TppTokenConnector extends AbstractTppConnector implements TokenConnector { + + public TppTokenConnector(Tpp tpp){ super(tpp); } + + @Override + public ConnectorType getType() { + return ConnectorType.TPP_TOKEN; + } + + @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); + } + + @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.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.readZoneConfigurationToken(request, 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.requestCertificateToken(payload, 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.certificateRetrieveToken(certificateRetrieveRequest, 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.searchCertificatesToken(searchRequest, 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.revokeCertificateToken(request, 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.renewCertificateToken(renewalRequest, 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.importCertificateToken(request, getAuthHeaderValue(accessToken)); + } + + @Override + public Policy readPolicyConfiguration(String zone, String accessToken) throws VCertException { + throw new UnsupportedOperationException("Method not yet implemented"); + } +} 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..812b945 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 =""; - @Override - public String toString() { - return Authentication.class.getSimpleName() + "(user=" + user + ", apiKey=" + apiKey - + ", password=" + (!password.isEmpty() ? "****" : "not set") + ")"; - } -} + public Authentication() {} + + 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 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..b7f0958 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_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