diff --git a/pom.xml b/pom.xml index 5667e5d..c525b19 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,11 @@ + + org.project-kessel + common-client-java + 0.1 + jakarta.enterprise jakarta.enterprise.cdi-api diff --git a/src/main/java/org/project_kessel/inventory/client/AuthnConfigConverter.java b/src/main/java/org/project_kessel/inventory/client/AuthnConfigConverter.java new file mode 100644 index 0000000..6786327 --- /dev/null +++ b/src/main/java/org/project_kessel/inventory/client/AuthnConfigConverter.java @@ -0,0 +1,36 @@ + +package org.project_kessel.inventory.client; + +import org.project_kessel.clients.authn.AuthenticationConfig; +import org.project_kessel.clients.authn.oidc.client.OIDCClientCredentialsAuthenticationConfig; +import org.project_kessel.clients.authn.oidc.client.OIDCClientCredentialsAuthenticationConfig.OIDCClientCredentialsConfig; + +public class AuthnConfigConverter { + + public static AuthenticationConfig convert(Config.AuthenticationConfig authnConfig) { + if(authnConfig == null) { + return null; + } + AuthenticationConfig convertedAuthnConfig; + if(authnConfig.clientCredentialsConfig().isPresent()) { + Config.OIDCClientCredentialsConfig oidcClientCredentialsConfig = authnConfig.clientCredentialsConfig().get(); + + convertedAuthnConfig = new OIDCClientCredentialsAuthenticationConfig(); + var convertedOidcClientCredentialsConfig = new OIDCClientCredentialsConfig(); + convertedOidcClientCredentialsConfig.setIssuer(oidcClientCredentialsConfig.issuer()); + convertedOidcClientCredentialsConfig.setClientId(oidcClientCredentialsConfig.clientId()); + convertedOidcClientCredentialsConfig.setClientSecret(oidcClientCredentialsConfig.clientSecret()); + convertedOidcClientCredentialsConfig.setScope(oidcClientCredentialsConfig.scope()); + convertedOidcClientCredentialsConfig.setOidcClientCredentialsMinterImplementation(oidcClientCredentialsConfig.oidcClientCredentialsMinterImplementation()); + + ((OIDCClientCredentialsAuthenticationConfig)convertedAuthnConfig).setCredentialsConfig(convertedOidcClientCredentialsConfig); + } else { + convertedAuthnConfig = new AuthenticationConfig(); + } + + convertedAuthnConfig.setMode(authnConfig.mode()); + + return convertedAuthnConfig; + } +} + diff --git a/src/main/java/org/project_kessel/inventory/client/CDIManagedClients.java b/src/main/java/org/project_kessel/inventory/client/CDIManagedInventoryClients.java similarity index 86% rename from src/main/java/org/project_kessel/inventory/client/CDIManagedClients.java rename to src/main/java/org/project_kessel/inventory/client/CDIManagedInventoryClients.java index 8f02ca3..b80f639 100644 --- a/src/main/java/org/project_kessel/inventory/client/CDIManagedClients.java +++ b/src/main/java/org/project_kessel/inventory/client/CDIManagedInventoryClients.java @@ -2,20 +2,21 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import org.project_kessel.clients.authn.AuthenticationConfig.AuthMode; /** - * A managed bean for providing relations api clients for injection in apps. + * A managed bean for providing clients for injection in apps. * It has the current limitation that only one underlying grpc connection can be configured. - * However, it is still possible to create more via InventoryGrpcClientsManager directly. + * However, it is still possible to create more via KesselClientsManager implementation directly. * This class does nothing unless the client is being managed by a CDI container (e.g. Quarkus) */ @ApplicationScoped -public class CDIManagedClients { +public class CDIManagedInventoryClients { @Produces InventoryGrpcClientsManager getManager(Config config) { var isSecureClients = config.isSecureClients(); var targetUrl = config.targetUrl(); - var authnEnabled = config.authenticationConfig().map(t -> !t.mode().equals(Config.AuthMode.DISABLED)).orElse(false); + var authnEnabled = config.authenticationConfig().map(t -> !t.mode().equals(AuthMode.DISABLED)).orElse(false); if (isSecureClients) { if(authnEnabled) { return InventoryGrpcClientsManager.forSecureClients(targetUrl, config.authenticationConfig().get()); diff --git a/src/main/java/org/project_kessel/inventory/client/Config.java b/src/main/java/org/project_kessel/inventory/client/Config.java index ba09e9f..0a951e7 100644 --- a/src/main/java/org/project_kessel/inventory/client/Config.java +++ b/src/main/java/org/project_kessel/inventory/client/Config.java @@ -3,6 +3,7 @@ import io.smallrye.config.ConfigMapping; import io.smallrye.config.WithDefault; import io.smallrye.config.WithName; +import org.project_kessel.clients.authn.AuthenticationConfig.AuthMode; import java.util.Optional; @@ -14,10 +15,6 @@ */ @ConfigMapping(prefix = "inventory-api") public interface Config { - enum AuthMode { - DISABLED, - OIDC_CLIENT_CREDENTIALS - } @WithDefault("false") boolean isSecureClients(); diff --git a/src/main/java/org/project_kessel/inventory/client/InventoryGrpcClientsManager.java b/src/main/java/org/project_kessel/inventory/client/InventoryGrpcClientsManager.java index 4b5251e..1f9015a 100644 --- a/src/main/java/org/project_kessel/inventory/client/InventoryGrpcClientsManager.java +++ b/src/main/java/org/project_kessel/inventory/client/InventoryGrpcClientsManager.java @@ -1,119 +1,38 @@ package org.project_kessel.inventory.client; -import io.grpc.*; -import org.project_kessel.inventory.client.authn.CallCredentialsFactory; +import io.grpc.Channel; +import org.project_kessel.clients.ChannelManager; +import org.project_kessel.clients.KesselClientsManager; -import java.util.HashMap; - -public class InventoryGrpcClientsManager { - private static final HashMap insecureManagers = new HashMap<>(); - private static final HashMap secureManagers = new HashMap<>(); - - private final ManagedChannel channel; - - public static synchronized InventoryGrpcClientsManager forInsecureClients(String targetUrl) { - if (!insecureManagers.containsKey(targetUrl)) { - var manager = new InventoryGrpcClientsManager(targetUrl, InsecureChannelCredentials.create()); - insecureManagers.put(targetUrl, manager); - } - return insecureManagers.get(targetUrl); +public class InventoryGrpcClientsManager extends KesselClientsManager { + private InventoryGrpcClientsManager(Channel channel) { + super(channel); } - public static synchronized InventoryGrpcClientsManager forInsecureClients(String targetUrl, Config.AuthenticationConfig authnConfig) throws RuntimeException { - if (!insecureManagers.containsKey(targetUrl)) { - try { - var manager = new InventoryGrpcClientsManager(targetUrl, - InsecureChannelCredentials.create(), - CallCredentialsFactory.create(authnConfig)); - insecureManagers.put(targetUrl, manager); - } catch (CallCredentialsFactory.CallCredentialsCreationException e) { - throw new RuntimeException(e); - } - } - return insecureManagers.get(targetUrl); - } + private static final String CHANNEL_MANAGER_KEY = InventoryGrpcClientsManager.class.getName(); - public static synchronized InventoryGrpcClientsManager forSecureClients(String targetUrl) { - if (!secureManagers.containsKey(targetUrl)) { - var tlsChannelCredentials = TlsChannelCredentials.create(); - var manager = new InventoryGrpcClientsManager(targetUrl, tlsChannelCredentials); - secureManagers.put(targetUrl, manager); - } - return secureManagers.get(targetUrl); + public static InventoryGrpcClientsManager forInsecureClients(String targetUrl) { + return new InventoryGrpcClientsManager(ChannelManager.getInstance(CHANNEL_MANAGER_KEY).forInsecureClients(targetUrl)); } - public static synchronized InventoryGrpcClientsManager forSecureClients(String targetUrl, Config.AuthenticationConfig authnConfig) { - if (!secureManagers.containsKey(targetUrl)) { - var tlsChannelCredentials = TlsChannelCredentials.create(); - try { - var manager = new InventoryGrpcClientsManager(targetUrl, - tlsChannelCredentials, - CallCredentialsFactory.create(authnConfig)); - secureManagers.put(targetUrl, manager); - } catch (CallCredentialsFactory.CallCredentialsCreationException e) { - throw new RuntimeException(e); - } - } - return secureManagers.get(targetUrl); + public static InventoryGrpcClientsManager forInsecureClients(String targetUrl, Config.AuthenticationConfig authnConfig) throws RuntimeException { + return new InventoryGrpcClientsManager(ChannelManager.getInstance(CHANNEL_MANAGER_KEY).forInsecureClients(targetUrl, AuthnConfigConverter.convert(authnConfig))); } - - public static synchronized void shutdownAll() { - for (var manager : insecureManagers.values()) { - manager.closeClientChannel(); - } - insecureManagers.clear(); - for (var manager : secureManagers.values()) { - manager.closeClientChannel(); - } - secureManagers.clear(); + public static InventoryGrpcClientsManager forSecureClients(String targetUrl) { + return new InventoryGrpcClientsManager(ChannelManager.getInstance(CHANNEL_MANAGER_KEY).forSecureClients(targetUrl)); } - public static synchronized void shutdownManager(InventoryGrpcClientsManager managerToShutdown) { - var iter = insecureManagers.entrySet().iterator(); - while (iter.hasNext()) { - var entry = iter.next(); - if(entry.getValue().channel == managerToShutdown.channel) { - entry.getValue().closeClientChannel(); - iter.remove(); - return; - } - } - iter = secureManagers.entrySet().iterator(); - while (iter.hasNext()) { - var entry = iter.next(); - if(entry.getValue().channel == managerToShutdown.channel) { - entry.getValue().closeClientChannel(); - iter.remove(); - return; - } - } - } - /** - * - * Bearer token and other things can be added to ChannelCredentials. New static factory methods can be added. - * Config management also required. - * @param targetUrl - * @param credentials - */ - private InventoryGrpcClientsManager(String targetUrl, ChannelCredentials credentials) { - this.channel = Grpc.newChannelBuilder(targetUrl, credentials).build(); + public static InventoryGrpcClientsManager forSecureClients(String targetUrl, Config.AuthenticationConfig authnConfig) { + return new InventoryGrpcClientsManager(ChannelManager.getInstance(CHANNEL_MANAGER_KEY).forSecureClients(targetUrl, AuthnConfigConverter.convert(authnConfig))); } - /** - * Create a manager for a grpc channel with server credentials and credentials for per-rpc client authentication. - * @param targetUrl - * @param serverCredentials authenticates the server for TLS or are InsecureChannelCredentials - * @param authnCredentials authenticates the client on each rpc - */ - private InventoryGrpcClientsManager(String targetUrl, ChannelCredentials serverCredentials, CallCredentials authnCredentials) { - this.channel = Grpc.newChannelBuilder(targetUrl, - CompositeChannelCredentials.create(serverCredentials, authnCredentials)).build(); + public static void shutdownAll() { + ChannelManager.getInstance(CHANNEL_MANAGER_KEY).shutdownAll(); } - - private void closeClientChannel() { - channel.shutdown(); + public static void shutdownManager(InventoryGrpcClientsManager managerToShutdown) { + ChannelManager.getInstance(CHANNEL_MANAGER_KEY).shutdownChannel(managerToShutdown.channel); } public RhelHostClient getHostClient() { diff --git a/src/main/java/org/project_kessel/inventory/client/InventoryHealthClient.java b/src/main/java/org/project_kessel/inventory/client/InventoryHealthClient.java index bea6744..2e32f10 100644 --- a/src/main/java/org/project_kessel/inventory/client/InventoryHealthClient.java +++ b/src/main/java/org/project_kessel/inventory/client/InventoryHealthClient.java @@ -2,17 +2,16 @@ import io.grpc.Channel; import org.project_kessel.api.inventory.v1.*; +import org.project_kessel.clients.KesselClient; import java.util.logging.Logger; -public class InventoryHealthClient { +public class InventoryHealthClient extends KesselClient{ private static final Logger logger = Logger.getLogger(InventoryHealthClient.class.getName()); - private final KesselInventoryHealthServiceGrpc.KesselInventoryHealthServiceBlockingStub blockingStub; - InventoryHealthClient(Channel channel){ - blockingStub = KesselInventoryHealthServiceGrpc.newBlockingStub(channel); + super(KesselInventoryHealthServiceGrpc.newStub(channel), KesselInventoryHealthServiceGrpc.newBlockingStub(channel)); } public GetReadyzResponse getReadyz(GetReadyzRequest request) { diff --git a/src/main/java/org/project_kessel/inventory/client/K8sClustersClient.java b/src/main/java/org/project_kessel/inventory/client/K8sClustersClient.java index b8b4d6a..a148748 100644 --- a/src/main/java/org/project_kessel/inventory/client/K8sClustersClient.java +++ b/src/main/java/org/project_kessel/inventory/client/K8sClustersClient.java @@ -5,18 +5,17 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.operators.multi.processors.UnicastProcessor; import org.project_kessel.api.inventory.v1beta1.*; +import org.project_kessel.clients.KesselClient; import java.util.logging.Logger; -public class K8sClustersClient { +public class K8sClustersClient extends KesselClient{ private static final Logger logger = Logger.getLogger(K8sClustersClient.class.getName()); - private final KesselK8sClusterServiceGrpc.KesselK8sClusterServiceStub asyncStub; - private final KesselK8sClusterServiceGrpc.KesselK8sClusterServiceBlockingStub blockingStub; K8sClustersClient(Channel channel){ - asyncStub = KesselK8sClusterServiceGrpc.newStub(channel); - blockingStub = KesselK8sClusterServiceGrpc.newBlockingStub(channel); + super(KesselK8sClusterServiceGrpc.newStub(channel), KesselK8sClusterServiceGrpc.newBlockingStub(channel)); + } public void UpdateK8sCluster(UpdateK8sClusterRequest request, StreamObserver responseObserver){ diff --git a/src/main/java/org/project_kessel/inventory/client/NotificationsIntegrationClient.java b/src/main/java/org/project_kessel/inventory/client/NotificationsIntegrationClient.java index 976a6e4..0dc3423 100644 --- a/src/main/java/org/project_kessel/inventory/client/NotificationsIntegrationClient.java +++ b/src/main/java/org/project_kessel/inventory/client/NotificationsIntegrationClient.java @@ -5,20 +5,17 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.operators.multi.processors.UnicastProcessor; import org.project_kessel.api.inventory.v1beta1.*; +import org.project_kessel.clients.KesselClient; import java.util.logging.Logger; -public class NotificationsIntegrationClient { +public class NotificationsIntegrationClient extends KesselClient{ private static final Logger logger = Logger.getLogger(RhelHostClient.class.getName()); - private final KesselNotificationsIntegrationServiceGrpc.KesselNotificationsIntegrationServiceStub asyncStub; - private final KesselNotificationsIntegrationServiceGrpc.KesselNotificationsIntegrationServiceBlockingStub blockingStub; - NotificationsIntegrationClient(Channel channel) { - asyncStub = KesselNotificationsIntegrationServiceGrpc.newStub(channel); - blockingStub = KesselNotificationsIntegrationServiceGrpc.newBlockingStub(channel); + super(KesselNotificationsIntegrationServiceGrpc.newStub(channel), KesselNotificationsIntegrationServiceGrpc.newBlockingStub(channel)); } public CreateNotificationsIntegrationResponse CreateNotificationsIntegration(CreateNotificationsIntegrationRequest request) { diff --git a/src/main/java/org/project_kessel/inventory/client/PoliciesClient.java b/src/main/java/org/project_kessel/inventory/client/PoliciesClient.java index 46bb932..c0f303d 100644 --- a/src/main/java/org/project_kessel/inventory/client/PoliciesClient.java +++ b/src/main/java/org/project_kessel/inventory/client/PoliciesClient.java @@ -5,18 +5,15 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.operators.multi.processors.UnicastProcessor; import org.project_kessel.api.inventory.v1beta1.*; +import org.project_kessel.clients.KesselClient; import java.util.logging.Logger; -public class PoliciesClient { +public class PoliciesClient extends KesselClient{ private static final Logger logger = Logger.getLogger(PoliciesClient.class.getName()); - private final KesselPolicyServiceGrpc.KesselPolicyServiceStub asyncStub; - private final KesselPolicyServiceGrpc.KesselPolicyServiceBlockingStub blockingStub; - PoliciesClient(Channel channel){ - asyncStub = KesselPolicyServiceGrpc.newStub(channel); - blockingStub = KesselPolicyServiceGrpc.newBlockingStub(channel); + super(KesselPolicyServiceGrpc.newStub(channel), KesselPolicyServiceGrpc.newBlockingStub(channel)); } public CreatePolicyResponse CreatePolicy(CreatePolicyRequest request) { diff --git a/src/main/java/org/project_kessel/inventory/client/PolicyRelationshipClient.java b/src/main/java/org/project_kessel/inventory/client/PolicyRelationshipClient.java index ba7daca..24368a7 100644 --- a/src/main/java/org/project_kessel/inventory/client/PolicyRelationshipClient.java +++ b/src/main/java/org/project_kessel/inventory/client/PolicyRelationshipClient.java @@ -5,19 +5,16 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.operators.multi.processors.UnicastProcessor; import org.project_kessel.api.inventory.v1beta1.*; +import org.project_kessel.clients.KesselClient; import java.util.logging.Logger; -public class PolicyRelationshipClient { +public class PolicyRelationshipClient extends KesselClient{ private static final Logger logger = Logger.getLogger(PoliciesClient.class.getName()); - private final KesselPolicyRelationshipServiceGrpc.KesselPolicyRelationshipServiceStub asyncStub; - private final KesselPolicyRelationshipServiceGrpc.KesselPolicyRelationshipServiceBlockingStub blockingStub; - PolicyRelationshipClient(Channel channel){ - asyncStub = KesselPolicyRelationshipServiceGrpc.newStub(channel); - blockingStub = KesselPolicyRelationshipServiceGrpc.newBlockingStub(channel); + super(KesselPolicyRelationshipServiceGrpc.newStub(channel), KesselPolicyRelationshipServiceGrpc.newBlockingStub(channel)); } public CreatePolicyRelationshipResponse CreatePolicyRelationship(CreatePolicyRelationshipRequest request){ diff --git a/src/main/java/org/project_kessel/inventory/client/authn/CallCredentialsFactory.java b/src/main/java/org/project_kessel/inventory/client/authn/CallCredentialsFactory.java deleted file mode 100644 index 3735d6f..0000000 --- a/src/main/java/org/project_kessel/inventory/client/authn/CallCredentialsFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.project_kessel.inventory.client.authn; - -import io.grpc.CallCredentials; -import org.project_kessel.inventory.client.Config; -import org.project_kessel.inventory.client.authn.oidc.client.OIDCClientCredentialsCallCredentials; - -public class CallCredentialsFactory { - - private CallCredentialsFactory() { - - } - - public static CallCredentials create(Config.AuthenticationConfig authnConfig) throws CallCredentialsCreationException { - if (authnConfig == null) { - throw new CallCredentialsCreationException("AuthenticationConfig is required to create CallCredentials and must not be null."); - } - - try { - switch (authnConfig.mode()) { - case DISABLED: return null; - case OIDC_CLIENT_CREDENTIALS: return new OIDCClientCredentialsCallCredentials(authnConfig); - } - } catch (OIDCClientCredentialsCallCredentials.OIDCClientCredentialsCallCredentialsException e) { - throw new CallCredentialsCreationException("Failed to create OIDCClientCredentialsCallCredentials.", e); - } - - return null; - } - - public static class CallCredentialsCreationException extends Exception { - public CallCredentialsCreationException(String message) { - super(message); - } - - public CallCredentialsCreationException(String message, Throwable cause) { - super(message, cause); - } - } -} diff --git a/src/main/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsCallCredentials.java b/src/main/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsCallCredentials.java deleted file mode 100644 index 769ed32..0000000 --- a/src/main/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsCallCredentials.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.project_kessel.inventory.client.authn.oidc.client; - -import io.grpc.Metadata; -import io.grpc.Status; -import org.project_kessel.inventory.client.Config; - -import java.util.Optional; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicReference; - -public class OIDCClientCredentialsCallCredentials extends io.grpc.CallCredentials { - static final Metadata.Key authorizationKey = Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER); - - private final Config.OIDCClientCredentialsConfig clientCredentialsConfig; - private final OIDCClientCredentialsMinter minter; - - private final AtomicReference storedBearerHeaderRef = new AtomicReference<>(); - - public OIDCClientCredentialsCallCredentials(Config.AuthenticationConfig authnConfig) throws OIDCClientCredentialsCallCredentialsException { - this.clientCredentialsConfig = validateAndExtractConfig(authnConfig); - - Optional minterImpl = clientCredentialsConfig.oidcClientCredentialsMinterImplementation(); - try { - if(minterImpl.isPresent()) { - this.minter = OIDCClientCredentialsMinter.forName(minterImpl.get()); - } else { - this.minter = OIDCClientCredentialsMinter.forDefaultImplementation(); - } - } catch (OIDCClientCredentialsMinter.OIDCClientCredentialsMinterException e) { - throw new OIDCClientCredentialsCallCredentialsException("Couldn't create GrpcCallCredentials because minter impl not instantiated.", e); - } - } - - OIDCClientCredentialsCallCredentials(Config.OIDCClientCredentialsConfig clientCredentialsConfig, OIDCClientCredentialsMinter minter) { - this.clientCredentialsConfig = clientCredentialsConfig; - this.minter = minter; - } - - @Override - public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) { - appExecutor.execute(() -> { - try { - synchronized (storedBearerHeaderRef) { - if (storedBearerHeaderRef.get() == null || storedBearerHeaderRef.get().isExpired()) { - storedBearerHeaderRef.set(minter.authenticateAndRetrieveAuthorizationHeader(clientCredentialsConfig)); - } - - Metadata headers = new Metadata(); - headers.put(authorizationKey, storedBearerHeaderRef.get().getAuthorizationHeader()); - applier.apply(headers); - } - } catch (Exception e) { - applier.fail(Status.UNAUTHENTICATED.withCause(e)); - } - }); - } - - /** - * For unusual cases where stored credentials (i.e. token), which may be long-lived, is bad and needs to be flushed. - */ - public void flushStoredCredentials() { - synchronized (storedBearerHeaderRef) { - storedBearerHeaderRef.set(null); - } - } - - /* We don't know that smallrye config validation will be used by clients, so do some validation here. */ - static Config.OIDCClientCredentialsConfig validateAndExtractConfig(Config.AuthenticationConfig authnConfig) throws OIDCClientCredentialsCallCredentialsException { - if (authnConfig.clientCredentialsConfig().isEmpty()) { - throw new OIDCClientCredentialsCallCredentialsException("ClientCredentialsConfig is required for OIDC client credentials authentication method."); - } - if(authnConfig.clientCredentialsConfig().get().issuer() == null) { - throw new OIDCClientCredentialsCallCredentialsException("ClientCredentialsConfig Issuer must not be null."); - } - if(authnConfig.clientCredentialsConfig().get().clientId() == null) { - throw new OIDCClientCredentialsCallCredentialsException("ClientCredentialsConfig Client id must not be null."); - } - if(authnConfig.clientCredentialsConfig().get().clientSecret() == null) { - throw new OIDCClientCredentialsCallCredentialsException("ClientCredentialsConfig Client secret must not be null."); - } - - return authnConfig.clientCredentialsConfig().get(); - } - - public static class OIDCClientCredentialsCallCredentialsException extends Exception { - public OIDCClientCredentialsCallCredentialsException(String message) { - super(message); - } - - public OIDCClientCredentialsCallCredentialsException(String message, Throwable cause) { - super(message, cause); - } - } - -} diff --git a/src/main/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsMinter.java b/src/main/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsMinter.java deleted file mode 100644 index e25645f..0000000 --- a/src/main/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsMinter.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.project_kessel.inventory.client.authn.oidc.client; - -import org.project_kessel.inventory.client.Config; -import org.project_kessel.inventory.client.authn.oidc.client.nimbus.NimbusOIDCClientCredentialsMinter; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.time.LocalDateTime; -import java.util.Optional; - -public abstract class OIDCClientCredentialsMinter { - private static final Class defaultMinter = NimbusOIDCClientCredentialsMinter.class; - - public static OIDCClientCredentialsMinter forDefaultImplementation() throws OIDCClientCredentialsMinterException { - return forClass(defaultMinter); - } - - public static OIDCClientCredentialsMinter forClass(Class minterClass) throws OIDCClientCredentialsMinterException { - try { - Constructor constructor = minterClass.getConstructor(); - return (OIDCClientCredentialsMinter)constructor.newInstance(); - } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e) { - throw new OIDCClientCredentialsMinterException("Can't create instance of OIDC client credentials minter", e); - } - } - - public static OIDCClientCredentialsMinter forName(String name) throws OIDCClientCredentialsMinterException { - try { - Class minterImplClass = Class.forName(name); - return forClass(minterImplClass); - } catch(ClassNotFoundException e) { - throw new OIDCClientCredentialsMinterException("Can't find the specified OIDC client credentials minter implementation", e); - } - } - - public abstract BearerHeader authenticateAndRetrieveAuthorizationHeader(Config.OIDCClientCredentialsConfig clientConfig) throws OIDCClientCredentialsMinterException; - - public static class BearerHeader { - private final String authorizationHeader; - private final Optional expiry; - - public BearerHeader(String authorizationHeader, Optional expiry) { - this.authorizationHeader = authorizationHeader; - this.expiry = expiry; - } - - public String getAuthorizationHeader() { - return authorizationHeader; - } - - public boolean isExpired() { - return expiry.map(t -> t.isBefore(LocalDateTime.now())).orElse(true); - } - } - - /** - * Utility method to derive an expiry dateTime from a just granted token with expires_in set. - * @param expiresIn 0 is expected if expiresIn is not set or otherwise not applicable. - * @return - */ - public static Optional getExpiryDateFromExpiresIn(long expiresIn) { - Optional expiryTime; - if (expiresIn != 0) { - // this processing happens some time after token is granted with lifetime so subtract buffer from lifetime - long bufferSeconds = 60; - if(expiresIn < bufferSeconds) { - expiryTime = Optional.empty(); - } else { - expiryTime = Optional.of(LocalDateTime.now().plusSeconds(expiresIn).minusSeconds(bufferSeconds)); - } - } else { - expiryTime = Optional.empty(); - } - - return expiryTime; - } - - public static Class getDefaultMinterImplementation() { - return defaultMinter; - } - - public static class OIDCClientCredentialsMinterException extends Exception { - public OIDCClientCredentialsMinterException(String message) { - super(message); - } - public OIDCClientCredentialsMinterException(String message, Throwable cause) { - super(message, cause); - } - } -} diff --git a/src/main/java/org/project_kessel/inventory/client/authn/oidc/client/nimbus/NimbusOIDCClientCredentialsMinter.java b/src/main/java/org/project_kessel/inventory/client/authn/oidc/client/nimbus/NimbusOIDCClientCredentialsMinter.java deleted file mode 100644 index a93bab0..0000000 --- a/src/main/java/org/project_kessel/inventory/client/authn/oidc/client/nimbus/NimbusOIDCClientCredentialsMinter.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.project_kessel.inventory.client.authn.oidc.client.nimbus; - -import com.nimbusds.oauth2.sdk.*; -import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; -import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic; -import com.nimbusds.oauth2.sdk.auth.Secret; -import com.nimbusds.oauth2.sdk.id.ClientID; -import com.nimbusds.oauth2.sdk.id.Issuer; -import com.nimbusds.oauth2.sdk.token.BearerAccessToken; -import com.nimbusds.openid.connect.sdk.OIDCTokenResponse; -import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser; -import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata; -import io.quarkus.runtime.annotations.RegisterForReflection; -import org.project_kessel.inventory.client.Config; -import org.project_kessel.inventory.client.authn.oidc.client.OIDCClientCredentialsMinter; - -import java.io.IOException; -import java.net.URI; -import java.time.LocalDateTime; -import java.util.Optional; - -/** - * Implementation pulled in by reflection in Vanilla java and registered for reflection if Quarkus native is used. - */ -@RegisterForReflection -public class NimbusOIDCClientCredentialsMinter extends OIDCClientCredentialsMinter { - @Override - public BearerHeader authenticateAndRetrieveAuthorizationHeader(Config.OIDCClientCredentialsConfig config) throws OIDCClientCredentialsMinterException { - Issuer issuer = new Issuer(config.issuer()); - ClientID clientID = new ClientID(config.clientId()); - Secret clientSecret = new Secret(config.clientSecret()); - Optional scope = config.scope().map(Scope::new); - AuthorizationGrant clientGrant = new ClientCredentialsGrant(); - - try { - OIDCProviderMetadata providerMetadata = OIDCProviderMetadata.resolve(issuer); - URI tokenEndpoint = providerMetadata.getTokenEndpointURI(); - ClientAuthentication clientAuth = new ClientSecretBasic(clientID, clientSecret); - // Make the token request - TokenRequest request; - if(scope.isPresent()) { - request = new TokenRequest(tokenEndpoint, clientAuth, clientGrant, scope.get()); - } else { - request = new TokenRequest(tokenEndpoint, clientAuth, clientGrant); - } - - TokenResponse tokenResponse = OIDCTokenResponseParser.parse(request.toHTTPRequest().send()); - if (!tokenResponse.indicatesSuccess()) { - TokenErrorResponse errorResponse = tokenResponse.toErrorResponse(); - String code = errorResponse.getErrorObject().getCode(); - String message = errorResponse.getErrorObject().getDescription(); - throw new OIDCClientCredentialsMinterException( - "Error requesting token from endpoint. TokenErrorResponse: code: " + code + ", message: " + message); - } - - OIDCTokenResponse successResponse = (OIDCTokenResponse)tokenResponse.toSuccessResponse(); - BearerAccessToken bearerAccessToken = successResponse.getOIDCTokens().getBearerAccessToken(); - - // Capture expiry if its exists in the token - long lifetime = bearerAccessToken.getLifetime(); - Optional expiryTime = getExpiryDateFromExpiresIn(lifetime); - - return new BearerHeader(bearerAccessToken.toAuthorizationHeader(), expiryTime); - } - catch(IOException | GeneralException e) { - throw new OIDCClientCredentialsMinterException("Failed to retrieve and parse OIDC well-known configuration from provider.", e); - } - } -} diff --git a/src/test/java/org/project_kessel/inventory/client/CDIManagedClientsContainerTests.java b/src/test/java/org/project_kessel/inventory/client/CDIManagedInventoryClientsContainerTests.java similarity index 99% rename from src/test/java/org/project_kessel/inventory/client/CDIManagedClientsContainerTests.java rename to src/test/java/org/project_kessel/inventory/client/CDIManagedInventoryClientsContainerTests.java index 2b816e2..cd3e0d0 100644 --- a/src/test/java/org/project_kessel/inventory/client/CDIManagedClientsContainerTests.java +++ b/src/test/java/org/project_kessel/inventory/client/CDIManagedInventoryClientsContainerTests.java @@ -23,7 +23,7 @@ * Use Weld as a test container to check CDI functionality. */ @EnableWeld -class CDIManagedClientsContainerTests { +class CDIManagedInventoryClientsContainerTests { @WeldSetup public WeldInitiator weld = WeldInitiator.from(new Weld().setBeanDiscoveryMode(BeanDiscoveryMode.ALL).addBeanClass(TestConfig.class)).build(); diff --git a/src/test/java/org/project_kessel/inventory/client/CDIManagedClientsTest.java b/src/test/java/org/project_kessel/inventory/client/CDIManagedInventoryClientsTest.java similarity index 86% rename from src/test/java/org/project_kessel/inventory/client/CDIManagedClientsTest.java rename to src/test/java/org/project_kessel/inventory/client/CDIManagedInventoryClientsTest.java index c92d2d1..dd77663 100644 --- a/src/test/java/org/project_kessel/inventory/client/CDIManagedClientsTest.java +++ b/src/test/java/org/project_kessel/inventory/client/CDIManagedInventoryClientsTest.java @@ -4,18 +4,20 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; +import org.project_kessel.clients.authn.AuthenticationConfig; + import java.util.Optional; import static org.mockito.Mockito.*; -class CDIManagedClientsTest { +class CDIManagedInventoryClientsTest { @Test void testInsecureNoAuthnMakesCorrectManagerCall() { Config config = makeDummyConfig(false, makeDummyAuthenticationConfig(false)); - CDIManagedClients cdiManagedClients = new CDIManagedClients(); + CDIManagedInventoryClients cdiManagedInventoryClients = new CDIManagedInventoryClients(); try (MockedStatic dummyManager = Mockito.mockStatic(InventoryGrpcClientsManager.class)) { - cdiManagedClients.getManager(config); + cdiManagedInventoryClients.getManager(config); dummyManager.verify( () -> InventoryGrpcClientsManager.forInsecureClients(anyString()), times(1) @@ -38,10 +40,10 @@ void testInsecureNoAuthnMakesCorrectManagerCall() { @Test void testInsecureWithAuthnMakesCorrectManagerCall() { Config config = makeDummyConfig(false, makeDummyAuthenticationConfig(true)); - CDIManagedClients cdiManagedClients = new CDIManagedClients(); + CDIManagedInventoryClients cdiManagedInventoryClients = new CDIManagedInventoryClients(); try (MockedStatic dummyManager = Mockito.mockStatic(InventoryGrpcClientsManager.class)) { - cdiManagedClients.getManager(config); + cdiManagedInventoryClients.getManager(config); dummyManager.verify( () -> InventoryGrpcClientsManager.forInsecureClients(anyString()), times(0) @@ -64,10 +66,10 @@ void testInsecureWithAuthnMakesCorrectManagerCall() { @Test void testSecureNoAuthnMakesCorrectManagerCall() { Config config = makeDummyConfig(true, makeDummyAuthenticationConfig(false)); - CDIManagedClients cdiManagedClients = new CDIManagedClients(); + CDIManagedInventoryClients cdiManagedInventoryClients = new CDIManagedInventoryClients(); try (MockedStatic dummyManager = Mockito.mockStatic(InventoryGrpcClientsManager.class)) { - cdiManagedClients.getManager(config); + cdiManagedInventoryClients.getManager(config); dummyManager.verify( () -> InventoryGrpcClientsManager.forInsecureClients(anyString()), times(0) @@ -90,10 +92,10 @@ void testSecureNoAuthnMakesCorrectManagerCall() { @Test void testSecureWithAuthnMakesCorrectManagerCall() { Config config = makeDummyConfig(true, makeDummyAuthenticationConfig(true)); - CDIManagedClients cdiManagedClients = new CDIManagedClients(); + CDIManagedInventoryClients cdiManagedInventoryClients = new CDIManagedInventoryClients(); try (MockedStatic dummyManager = Mockito.mockStatic(InventoryGrpcClientsManager.class)) { - cdiManagedClients.getManager(config); + cdiManagedInventoryClients.getManager(config); dummyManager.verify( () -> InventoryGrpcClientsManager.forInsecureClients(anyString()), times(0) @@ -135,12 +137,12 @@ public Optional authenticationConfig() { static Config.AuthenticationConfig makeDummyAuthenticationConfig(boolean authnEnabled) { return new Config.AuthenticationConfig() { @Override - public Config.AuthMode mode() { + public AuthenticationConfig.AuthMode mode() { if(!authnEnabled) { - return Config.AuthMode.DISABLED; + return AuthenticationConfig.AuthMode.DISABLED; } // pick some arbitrary non disabled mode - return Config.AuthMode.OIDC_CLIENT_CREDENTIALS; + return AuthenticationConfig.AuthMode.OIDC_CLIENT_CREDENTIALS; } @Override diff --git a/src/test/java/org/project_kessel/inventory/client/InventoryGrpcClientsManagerTest.java b/src/test/java/org/project_kessel/inventory/client/InventoryGrpcClientsManagerTest.java index ef36a43..b26227e 100644 --- a/src/test/java/org/project_kessel/inventory/client/InventoryGrpcClientsManagerTest.java +++ b/src/test/java/org/project_kessel/inventory/client/InventoryGrpcClientsManagerTest.java @@ -1,144 +1,17 @@ package org.project_kessel.inventory.client; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.project_kessel.clients.authn.AuthenticationConfig; -import java.util.HashMap; -import java.util.Hashtable; import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import static io.smallrye.common.constraint.Assert.assertNotNull; -import static org.junit.jupiter.api.Assertions.*; public class InventoryGrpcClientsManagerTest { - @BeforeAll - public static void testSetup() { - /* Make sure all client managers shutdown/removed before tests */ - InventoryGrpcClientsManager.shutdownAll(); - } - - @AfterEach - public void testTeardown() { - /* Make sure all client managers shutdown/removed after each test */ - InventoryGrpcClientsManager.shutdownAll(); - } - - @Test - public void testManagerReusePatterns() { - var one = InventoryGrpcClientsManager.forInsecureClients("localhost:8080"); - var two = InventoryGrpcClientsManager.forInsecureClients("localhost:8080"); // same as one - var three = InventoryGrpcClientsManager.forInsecureClients("localhost1:8080"); - var four = InventoryGrpcClientsManager.forSecureClients("localhost:8080"); - var five = InventoryGrpcClientsManager.forSecureClients("localhost1:8080"); - var six = InventoryGrpcClientsManager.forSecureClients("localhost1:8080"); // same as five - - assertNotNull(one); - assertNotNull(two); - assertNotNull(three); - assertNotNull(four); - assertNotNull(five); - assertNotNull(six); - assertEquals(one, two); - assertNotEquals(two, three); - assertEquals(five, six); - assertNotEquals(four, five); - } - - @Test - public void testThreadingChaos() { - /* Basic testing to ensure that we don't get ConcurrentModificationExceptions, or any other exceptions, when - * creating and destroying managers on different threads. */ - - try { - Hashtable managers = new Hashtable<>(); - - int numberOfThreads = 100; - ExecutorService service = Executors.newFixedThreadPool(numberOfThreads); - CountDownLatch latch1 = new CountDownLatch(numberOfThreads / 3); - CountDownLatch latch2 = new CountDownLatch(numberOfThreads * 2 / 3 - numberOfThreads / 3); - CountDownLatch latch3 = new CountDownLatch(numberOfThreads - numberOfThreads * 2 / 3); - - /* A: Use 1/3 of threads to request/create managers at the same time. */ - for (int i = 0; i < numberOfThreads / 3; i++) { - final int j = i; - service.submit(() -> { - InventoryGrpcClientsManager manager; - if (j % 2 == 0) { - manager = InventoryGrpcClientsManager.forInsecureClients("localhost" + j); - } else { - manager = InventoryGrpcClientsManager.forSecureClients("localhost" + j); - } - managers.put("localhost" + j, manager); - - latch1.countDown(); - }); - } - - latch1.await(); - /* B and C, below, trigger at the same time once A is done. */ - - /* B: Use 1/3 of threads to shut down the above created managers. */ - for (int i = numberOfThreads / 3; i < numberOfThreads * 2 / 3; i++) { - final int j = i - numberOfThreads / 3; - service.submit(() -> { - InventoryGrpcClientsManager.shutdownManager(managers.get("localhost" + j)); - latch2.countDown(); - }); - } - - /* C: Use 1/3 of the threads to recreate/retrieve the same managers at the same time as B. */ - for (int i = numberOfThreads * 2 / 3; i < numberOfThreads; i++) { - final int j = i - numberOfThreads * 2 / 3; - service.submit(() -> { - InventoryGrpcClientsManager manager; - if (j % 2 == 0) { - manager = InventoryGrpcClientsManager.forInsecureClients("localhost" + j); - } else { - manager = InventoryGrpcClientsManager.forSecureClients("localhost" + j); - } - managers.put("localhost" + j, manager); - - latch3.countDown(); - }); - } - latch2.await(); - latch3.await(); - } catch (Exception e) { - fail("Should not have thrown any exception"); - } - } - - @Test - public void testManagerReuseInternal() throws Exception { - InventoryGrpcClientsManager.forInsecureClients("localhost:8080"); - InventoryGrpcClientsManager.forInsecureClients("localhost:8080"); // same as one - InventoryGrpcClientsManager.forInsecureClients("localhost1:8080"); - InventoryGrpcClientsManager.forSecureClients("localhost:8080"); - InventoryGrpcClientsManager.forSecureClients("localhost1:8080"); - InventoryGrpcClientsManager.forSecureClients("localhost1:8080"); // same as five - - var insecureField = InventoryGrpcClientsManager.class.getDeclaredField("insecureManagers"); - insecureField.setAccessible(true); - var secureField = InventoryGrpcClientsManager.class.getDeclaredField("secureManagers"); - secureField.setAccessible(true); - var insecureManagers = (HashMap) insecureField.get(null); - var secureManagers = (HashMap) secureField.get(null); - - assertEquals(2, insecureManagers.size()); - assertEquals(2, secureManagers.size()); - } - public static Config.AuthenticationConfig dummyAuthConfigWithGoodOIDCClientCredentials() { return new Config.AuthenticationConfig() { @Override - public Config.AuthMode mode() { - return Config.AuthMode.OIDC_CLIENT_CREDENTIALS; // any non-disabled value + public AuthenticationConfig.AuthMode mode() { + return AuthenticationConfig.AuthMode.OIDC_CLIENT_CREDENTIALS; // any non-disabled value } @Override diff --git a/src/test/java/org/project_kessel/inventory/client/authn/CallCredentialsFactoryTest.java b/src/test/java/org/project_kessel/inventory/client/authn/CallCredentialsFactoryTest.java deleted file mode 100644 index cca95f5..0000000 --- a/src/test/java/org/project_kessel/inventory/client/authn/CallCredentialsFactoryTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.project_kessel.inventory.client.authn; - -import org.junit.jupiter.api.Test; -import org.project_kessel.inventory.client.Config; - - -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.fail; -import static org.project_kessel.inventory.client.InventoryGrpcClientsManagerTest.dummyAuthConfigWithGoodOIDCClientCredentials; - - -class CallCredentialsFactoryTest { - - @Test - void testCreateOIDCClientCallCredentials() { - Config.AuthenticationConfig authnConfig = dummyAuthConfigWithGoodOIDCClientCredentials(); - try { - CallCredentialsFactory.create(authnConfig); - } catch (CallCredentialsFactory.CallCredentialsCreationException e) { - fail("CallCredentialsFactory creation for OIDC client should not throw an exception when OIDC client config is good."); - } - } - - @Test - void testFailToCreateCallCredentialsWhenAuthnConfigEmpty() { - Config.AuthenticationConfig authnConfig = null; - try { - CallCredentialsFactory.create(authnConfig); - fail("CallCredentialsFactory creation for OIDC client should throw an exception when OIDC client config is empty."); - } catch (CallCredentialsFactory.CallCredentialsCreationException e) { - } - } - - @Test - void testFailToCreateCallCredentialsForOIDCWhenConfigEmpty() { - Config.AuthenticationConfig authnConfig = new Config.AuthenticationConfig() { - @Override - public Config.AuthMode mode() { - return Config.AuthMode.OIDC_CLIENT_CREDENTIALS; - } - - @Override - public Optional clientCredentialsConfig() { - return Optional.empty(); - } - }; - try { - CallCredentialsFactory.create(authnConfig); - fail("CallCredentialsFactory creation for OIDC client should throw an exception when OIDC client config is empty."); - } catch (CallCredentialsFactory.CallCredentialsCreationException e) { - } - } - -} diff --git a/src/test/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsCallCredentialsTest.java b/src/test/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsCallCredentialsTest.java deleted file mode 100644 index d07f03c..0000000 --- a/src/test/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsCallCredentialsTest.java +++ /dev/null @@ -1,263 +0,0 @@ -package org.project_kessel.inventory.client.authn.oidc.client; - -import io.grpc.CallCredentials; -import io.grpc.Metadata; -import io.grpc.Status; -import io.grpc.netty.shaded.io.netty.util.concurrent.DefaultEventExecutor; -import org.junit.jupiter.api.Test; -import org.project_kessel.inventory.client.Config; - - -import java.time.LocalDateTime; -import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.jupiter.api.Assertions.*; -import static org.project_kessel.inventory.client.authn.oidc.client.OIDCClientCredentialsMinter.getDefaultMinterImplementation; - - -public class OIDCClientCredentialsCallCredentialsTest { - - @Test - void initializationShouldFailWithNullIssuer() { - try { - var authConfig = makeAuthConfig(null, "some", "some"); - new OIDCClientCredentialsCallCredentials(authConfig); - } - catch (OIDCClientCredentialsCallCredentials.OIDCClientCredentialsCallCredentialsException e) { - return; // expected - } - - fail("Issuer should not be null"); - } - - @Test - void initializationShouldFailWithNullClientId() { - try { - var authConfig = makeAuthConfig("some", null, "some"); - new OIDCClientCredentialsCallCredentials(authConfig); - } - catch (OIDCClientCredentialsCallCredentials.OIDCClientCredentialsCallCredentialsException e) { - return; // expected - } - - fail("Client id should not be null"); - } - - @Test - void initializationShouldFailWithNullClientSecret() { - try { - var authConfig = makeAuthConfig("some", "some", null); - new OIDCClientCredentialsCallCredentials(authConfig); - } - catch (OIDCClientCredentialsCallCredentials.OIDCClientCredentialsCallCredentialsException e) { - return; // expected - } - - fail("Client secret should not be null"); - } - - @Test - void unknownSpecifiedMinterShouldThrowException() { - var authConfig = makeAuthConfig("some", "some", "some", Optional.empty(), - Optional.of("one.bogus.clazz")); - try { - new OIDCClientCredentialsCallCredentials(authConfig); - } - catch (OIDCClientCredentialsCallCredentials.OIDCClientCredentialsCallCredentialsException e) { - return; // expected - } - - fail("Shouldn't be able to instantiate OIDCClientCredentialsCallCredentials with a bogus minter."); - } - - @Test - void knownSpecifiedMinterShouldNotThrowException() { - var authConfig = makeAuthConfig("some", "some", "some", Optional.empty(), - Optional.of(getDefaultMinterImplementation().getName())); - try { - new OIDCClientCredentialsCallCredentials(authConfig); - } - catch (OIDCClientCredentialsCallCredentials.OIDCClientCredentialsCallCredentialsException e) { - fail("Should be able create default minter with no problems."); - } - } - - @Test - void unspecifiedMinterShouldUseDefaultAndNotThrowException() { - var authConfig = makeAuthConfig("some", "some", "some", Optional.empty(), - Optional.empty()); - try { - new OIDCClientCredentialsCallCredentials(authConfig); - } - catch (OIDCClientCredentialsCallCredentials.OIDCClientCredentialsCallCredentialsException e) { - fail("Should be able create default minter with no problems."); - } - } - - @Test - void shouldApplyBearerMetadata() throws InterruptedException { - var authConfig = makeAuthConfig("some", "some", "some", Optional.empty(), - Optional.empty()); - var oidcClientCredentialsConfig = authConfig.clientCredentialsConfig().orElse(null); - var minter = makeFakeMinter(true, 0); - var callCreds = new OIDCClientCredentialsCallCredentials(oidcClientCredentialsConfig, minter); - final AtomicReference metaDataRef = new AtomicReference<>(); - final AtomicReference statusRef = new AtomicReference<>(); - var latch = new CountDownLatch(1); - var metaDataApplier = makeFakeMetadataApplier(metaDataRef, statusRef, latch); - - callCreds.applyRequestMetadata(null, new DefaultEventExecutor(), metaDataApplier); - - latch.await(); - assertEquals("token0", metaDataRef.get().get(OIDCClientCredentialsCallCredentials.authorizationKey)); - assertNull(statusRef.get()); - } - - @Test - void shouldApplyPreviouslyObtainedTokenWhenInLifetime() throws InterruptedException { - var authConfig = makeAuthConfig("some", "some", "some", Optional.empty(), - Optional.empty()); - var oidcClientCredentialsConfig = authConfig.clientCredentialsConfig().orElse(null); - var minter = makeFakeMinter(true, 100000); // big lifetime - var callCreds = new OIDCClientCredentialsCallCredentials(oidcClientCredentialsConfig, minter); - final AtomicReference metaDataRef = new AtomicReference<>(); - final AtomicReference statusRef = new AtomicReference<>(); - var latch = new CountDownLatch(1); - var metaDataApplier = makeFakeMetadataApplier(metaDataRef, statusRef, latch); - - callCreds.applyRequestMetadata(null, new DefaultEventExecutor(), metaDataApplier); - - latch.await(); - var latch2 = new CountDownLatch(1); - var metaDataApplier2 = makeFakeMetadataApplier(metaDataRef, statusRef, latch2); - - callCreds.applyRequestMetadata(null, new DefaultEventExecutor(), metaDataApplier2); - - latch2.await(); - // token0 is the original minted token -- shows there was no second authentication and new token - assertEquals("token0", metaDataRef.get().get(OIDCClientCredentialsCallCredentials.authorizationKey)); - assertNull(statusRef.get()); - } - - @Test - void shouldApplyNewTokenWhenOutOfLifetime() throws InterruptedException { - var authConfig = makeAuthConfig("some", "some", "some", Optional.empty(), - Optional.empty()); - var oidcClientCredentialsConfig = authConfig.clientCredentialsConfig().orElse(null); - var minter = makeFakeMinter(true, 0); // zero lifetime forces new auth token - var callCreds = new OIDCClientCredentialsCallCredentials(oidcClientCredentialsConfig, minter); - final AtomicReference metaDataRef = new AtomicReference<>(); - final AtomicReference statusRef = new AtomicReference<>(); - var latch = new CountDownLatch(1); - var metaDataApplier = makeFakeMetadataApplier(metaDataRef, statusRef, latch); - - callCreds.applyRequestMetadata(null, new DefaultEventExecutor(), metaDataApplier); - - latch.await(); - var latch2 = new CountDownLatch(1); - var metaDataApplier2 = makeFakeMetadataApplier(metaDataRef, statusRef, latch2); - - callCreds.applyRequestMetadata(null, new DefaultEventExecutor(), metaDataApplier2); - - latch2.await(); - // token1 is the second minted token -- shows that when out of lifetime there is a second authn and new token - assertEquals("token1", metaDataRef.get().get(OIDCClientCredentialsCallCredentials.authorizationKey)); - assertNull(statusRef.get()); - } - - @Test - void shouldApplyUnauthenticatedWhenAuthnFails() throws InterruptedException { - var authConfig = makeAuthConfig("some", "some", "some", Optional.empty(), - Optional.empty()); - var oidcClientCredentialsConfig = authConfig.clientCredentialsConfig().orElse(null); - var minter = makeFakeMinter(false, 0); - var callCreds = new OIDCClientCredentialsCallCredentials(oidcClientCredentialsConfig, minter); - final AtomicReference metaDataRef = new AtomicReference<>(); - final AtomicReference statusRef = new AtomicReference<>(); - var latch = new CountDownLatch(1); - var metaDataApplier = makeFakeMetadataApplier(metaDataRef, statusRef, latch); - - callCreds.applyRequestMetadata(null, new DefaultEventExecutor(), metaDataApplier); - - latch.await(); - assertNull(metaDataRef.get()); - assertEquals(Status.Code.UNAUTHENTICATED, statusRef.get().getCode()); - } - - static OIDCClientCredentialsMinter makeFakeMinter(boolean alwaysSucceedsOrFails, long tokensExpireIn) { - return new OIDCClientCredentialsMinter() { - int mintedNumber = 0; - - @Override - public BearerHeader authenticateAndRetrieveAuthorizationHeader(Config.OIDCClientCredentialsConfig clientConfig) throws OIDCClientCredentialsMinterException { - if (!alwaysSucceedsOrFails) { - throw new OIDCClientCredentialsMinterException("Authentication failed."); - } - - Optional expiry = Optional.of(LocalDateTime.now().plusSeconds(tokensExpireIn)); - return new BearerHeader("token" + mintedNumber++, expiry); - } - }; - } - - static CallCredentials.MetadataApplier makeFakeMetadataApplier(AtomicReference metaDataRef, AtomicReference statusRef, CountDownLatch latch) { - return new CallCredentials.MetadataApplier() { - @Override - public void apply(Metadata headers) { - metaDataRef.set(headers); - latch.countDown(); - } - - @Override - public void fail(Status status) { - statusRef.set(status); - latch.countDown(); - } - }; - } - - public static Config.AuthenticationConfig makeAuthConfig(String issuer, String clientId, String clientSecret) { - return makeAuthConfig(issuer, clientId, clientSecret, Optional.empty(), Optional.empty()); - } - - public static Config.AuthenticationConfig makeAuthConfig(String issuer, String clientId, String clientSecret, Optional scope, Optional minterImpl) { - return new Config.AuthenticationConfig() { - @Override - public Config.AuthMode mode() { - return null; - } - - @Override - public Optional clientCredentialsConfig() { - return Optional.of(new Config.OIDCClientCredentialsConfig() { - @Override - public String issuer() { - return issuer; - } - - @Override - public String clientId() { - return clientId; - } - - @Override - public String clientSecret() { - return clientSecret; - } - - @Override - public Optional scope() { - return scope; - } - - @Override - public Optional oidcClientCredentialsMinterImplementation() { - return minterImpl; - } - }); - } - }; - } -} diff --git a/src/test/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsMinterTest.java b/src/test/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsMinterTest.java deleted file mode 100644 index f095529..0000000 --- a/src/test/java/org/project_kessel/inventory/client/authn/oidc/client/OIDCClientCredentialsMinterTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.project_kessel.inventory.client.authn.oidc.client; - -import org.junit.jupiter.api.Test; -import org.project_kessel.inventory.client.Config; - - -import java.time.LocalDateTime; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; -import static org.project_kessel.inventory.client.authn.oidc.client.OIDCClientCredentialsMinter.getExpiryDateFromExpiresIn; - - -class OIDCClientCredentialsMinterTest { - - @Test - void testCreateDefaultMinter() { - Class defaultMinterClass = OIDCClientCredentialsMinter.getDefaultMinterImplementation(); - try { - var minter = OIDCClientCredentialsMinter.forClass(defaultMinterClass); - assertInstanceOf(defaultMinterClass, minter); - } catch (OIDCClientCredentialsMinter.OIDCClientCredentialsMinterException e) { - fail("Creating minter from default implementation name should not throw an OIDCClientCredentialsMinterException"); - } - } - - @Test - void testCreateMinterFromClass() { - Class testMinterClass = TestMinter.class; - try { - var minter = OIDCClientCredentialsMinter.forClass(testMinterClass); - assertInstanceOf(testMinterClass, minter); - } catch (OIDCClientCredentialsMinter.OIDCClientCredentialsMinterException e) { - fail("Creating minter from test implementation name should not throw an OIDCClientCredentialsMinterException"); - } - } - - @Test - void testCreateMinterFromName() { - String testMinterName = TestMinter.class.getName(); - try { - OIDCClientCredentialsMinter.forName(testMinterName); - } catch (OIDCClientCredentialsMinter.OIDCClientCredentialsMinterException e) { - fail("Creating minter from test implementation name should not throw an OIDCClientCredentialsMinterException"); - } - } - - @Test - void testCreateMinterFromFakeImplNameThrowsException() { - String defaultMinterName = "absolutely.not.a.valid.Implementation"; - try { - OIDCClientCredentialsMinter.forName(defaultMinterName); - } catch (OIDCClientCredentialsMinter.OIDCClientCredentialsMinterException e) { - return; - } - fail("Creating minter from not existent implementation name should throw an OIDCClientCredentialsMinterException"); - } - - @Test - void testGetExpiryDateFromExpiresInLongLived() { - LocalDateTime someTimeBefore = LocalDateTime.now().plusSeconds(9000); - LocalDateTime someTimeAfter = LocalDateTime.now().plusSeconds(11000); - Optional expiryDate = getExpiryDateFromExpiresIn(10000); - assertTrue(expiryDate.isPresent()); - assertTrue(someTimeBefore.isBefore(expiryDate.get())); - assertTrue(someTimeAfter.isAfter(expiryDate.get())); - } - - @Test - void testGetAbsentExpiryDateFromExpiresInShortLived() { - Optional expiryDate = getExpiryDateFromExpiresIn(0); - assertTrue(expiryDate.isEmpty()); - } - - @Test - void bearerHeaderExpiryScenarios() { - Optional someTimeInTheFuture = Optional.of(LocalDateTime.now().plusSeconds(10000)); - var bearerHeader = new OIDCClientCredentialsMinter.BearerHeader("header", someTimeInTheFuture); - assertFalse(bearerHeader.isExpired()); - - Optional someTimeInThePast = Optional.of(LocalDateTime.now().minusSeconds(10000)); - bearerHeader = new OIDCClientCredentialsMinter.BearerHeader("header", someTimeInThePast); - assertTrue(bearerHeader.isExpired()); - - Optional noExpiryTime = Optional.empty(); - bearerHeader = new OIDCClientCredentialsMinter.BearerHeader("header", noExpiryTime); - assertTrue(bearerHeader.isExpired()); - } - - static class TestMinter extends OIDCClientCredentialsMinter { - public TestMinter() { - } - - @Override - public BearerHeader authenticateAndRetrieveAuthorizationHeader(Config.OIDCClientCredentialsConfig clientConfig) throws OIDCClientCredentialsMinterException { - return null; - } - } -} diff --git a/src/test/java/org/project_kessel/inventory/client/authn/oidc/client/nimbus/NimbusOIDCClientCredentialsMinterTest.java b/src/test/java/org/project_kessel/inventory/client/authn/oidc/client/nimbus/NimbusOIDCClientCredentialsMinterTest.java deleted file mode 100644 index 5902435..0000000 --- a/src/test/java/org/project_kessel/inventory/client/authn/oidc/client/nimbus/NimbusOIDCClientCredentialsMinterTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.project_kessel.inventory.client.authn.oidc.client.nimbus; - -import org.junit.jupiter.api.Test; -import org.project_kessel.inventory.client.InventoryGrpcClientsManagerTest; -import org.project_kessel.inventory.client.authn.oidc.client.OIDCClientCredentialsMinter; -import org.project_kessel.inventory.client.fake.FakeIdp; - - -import static org.junit.jupiter.api.Assertions.*; -import static org.project_kessel.inventory.client.InventoryGrpcClientsManagerTest.dummyAuthConfigWithGoodOIDCClientCredentials; - -public class NimbusOIDCClientCredentialsMinterTest { - - @Test - void shouldReturnBearerHeaderWhenIdPAuthenticates() { - var minter = new NimbusOIDCClientCredentialsMinter(); - var config = InventoryGrpcClientsManagerTest.dummyAuthConfigWithGoodOIDCClientCredentials().clientCredentialsConfig(); - OIDCClientCredentialsMinter.BearerHeader bearerHeader = null; - try { - FakeIdp fakeIdp = new FakeIdp(8090); - fakeIdp.start(); - bearerHeader = minter.authenticateAndRetrieveAuthorizationHeader(config.get()); - fakeIdp.stop(); - } catch (OIDCClientCredentialsMinter.OIDCClientCredentialsMinterException e) { - fail("Should not throw exception if authn is successful."); - } - - assertNotNull(bearerHeader); - assertEquals("Bearer blah", bearerHeader.getAuthorizationHeader()); - } - - @Test - void shouldThrowExceptionWhenIdPAuthenticationFails() { - var minter = new NimbusOIDCClientCredentialsMinter(); - var config = InventoryGrpcClientsManagerTest.dummyAuthConfigWithGoodOIDCClientCredentials().clientCredentialsConfig(); - FakeIdp fakeIdp = new FakeIdp(8090, false); - try { - fakeIdp.start(); - minter.authenticateAndRetrieveAuthorizationHeader(config.get()); - fail("Should throw exception if authn is not successful."); - } catch (OIDCClientCredentialsMinter.OIDCClientCredentialsMinterException e) { - // success - } catch(Exception e) { - fail("OIDCClientCredentialsMinterException expected."); - } finally { - fakeIdp.stop(); - } - } -} diff --git a/src/test/java/org/project_kessel/inventory/client/fake/FakeIdp.java b/src/test/java/org/project_kessel/inventory/client/fake/FakeIdp.java deleted file mode 100644 index 8febfd0..0000000 --- a/src/test/java/org/project_kessel/inventory/client/fake/FakeIdp.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.project_kessel.inventory.client.fake; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.InetSocketAddress; - -/** - * Super-fake Idp that supports a hard-coded well-known discovery endpoint and a corresponding fake token endpoint. - * Does not use TLS. - */ -public class FakeIdp { - private final int port; - private final boolean alwaysSucceedOrFailAuthn; - HttpServer server = null; - - public FakeIdp(int port) { - this(port, true); - } - - public FakeIdp(int port, boolean alwaysSucceedOrFailAuthn) { - this.port = port; - this.alwaysSucceedOrFailAuthn = alwaysSucceedOrFailAuthn; - } - - public void start() { - try { - server = HttpServer.create(new InetSocketAddress(port), 0); - } catch (IOException e) { - throw new RuntimeException(e); - } - server.createContext("/.well-known/openid-configuration", new WellKnownHandler()); - if(alwaysSucceedOrFailAuthn) { - server.createContext("/token", new TokenHandler()); - } else { - server.createContext("/token", new UnauthorizedHandler()); - } - - server.setExecutor(null); // creates a default executor - server.start(); - } - - public void stop() { - server.stop(0); - } - - static class TokenHandler implements HttpHandler { - @Override - public void handle(HttpExchange t) throws IOException { - String response = "{\n" + - " \"iss\": \"http://localhost:8090/\",\n" + - " \"aud\": \"us\",\n" + - " \"sub\": \"usr_123\",\n" + - " \"scope\": \"read write\",\n" + - " \"iat\": 1458785796,\n" + - " \"exp\": 1458872196,\n" + - " \"token_type\": \"Bearer\",\n" + - " \"access_token\": \"blah\"\n" + - "}"; - t.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8"); - t.sendResponseHeaders(200, response.length()); - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } - } - - static class UnauthorizedHandler implements HttpHandler { - @Override - public void handle(HttpExchange t) throws IOException { - String response = "{\"error_description\":\"Access denied by resource owner or authorization server\",\"error\":\"access_denied\"}"; - - // https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint (3.1.3.4. Token Error Response) - t.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8"); - t.sendResponseHeaders(400, response.length()); - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } - } - - static class WellKnownHandler implements HttpHandler { - @Override - public void handle(HttpExchange t) throws IOException { - String response = "{\n" + - "\t\"issuer\":\"http://localhost:8090\",\n" + - "\t\"authorization_endpoint\":\"http://localhost:8090/protocol/openid-connect/auth\",\n" + - "\t\"token_endpoint\":\"http://localhost:8090/token\",\n" + - "\t\"introspection_endpoint\":\"http://localhost:8090/token/introspect\",\n" + - "\t\"jwks_uri\":\"http://localhost:8090/certs\",\n" + - "\t\"response_types_supported\":[\"code\",\"none\",\"id_token\",\"token\",\"id_token token\",\"code id_token\",\"code token\",\"code id_token token\"],\n" + - "\t\"token_endpoint_auth_methods_supported\":[\"private_key_jwt\",\"client_secret_basic\",\"client_secret_post\",\"tls_client_auth\",\"client_secret_jwt\"],\n" + - "\t\"subject_types_supported\":[\"public\",\"pairwise\"]\n" + - "}"; - t.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8"); - t.sendResponseHeaders(200, response.length()); - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } - } -} diff --git a/src/test/java/org/project_kessel/inventory/client/fake/GrpcServerSpy.java b/src/test/java/org/project_kessel/inventory/client/fake/GrpcServerSpy.java deleted file mode 100644 index f73e489..0000000 --- a/src/test/java/org/project_kessel/inventory/client/fake/GrpcServerSpy.java +++ /dev/null @@ -1,167 +0,0 @@ -package org.project_kessel.inventory.client.fake; - -import io.grpc.*; -import io.grpc.Metadata; -import io.grpc.stub.StreamObserver; -import org.project_kessel.api.inventory.v1beta1.*; - - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.Objects; -import java.util.concurrent.TimeUnit; - -public class GrpcServerSpy extends Server { - private final Server server; - - public GrpcServerSpy(int port, boolean tlsEnabled, ServerInterceptor interceptor, BindableService... services) { - ServerBuilder serverBuilder = ServerBuilder.forPort(port); - if (tlsEnabled) { - URL certsUrl = Thread.currentThread().getContextClassLoader().getResource("certs/test.crt"); - URL keyUrl = Thread.currentThread().getContextClassLoader().getResource("certs/test.key"); - File certFile = new File(Objects.requireNonNull(certsUrl).getPath()); - File keyFile = new File(Objects.requireNonNull(keyUrl).getPath()); - serverBuilder.useTransportSecurity(certFile, keyFile); - } - if (interceptor != null) { - serverBuilder.intercept(interceptor); - } - for (BindableService service : services) { - serverBuilder.addService(service); - } - server = serverBuilder.build(); - } - - public static ServerCallDetails runAgainstTemporaryServerWithDummyServices(int port, Call grpcCallFunction) { - return runAgainstTemporaryServerWithDummyServicesTlsSelect(port, false, grpcCallFunction); - } - - public static ServerCallDetails runAgainstTemporaryTlsServerWithDummyServices(int port, Call grpcCallFunction) { - return runAgainstTemporaryServerWithDummyServicesTlsSelect(port, true, grpcCallFunction); - } - - private static ServerCallDetails runAgainstTemporaryServerWithDummyServicesTlsSelect(int port, boolean tlsEnabled, Call grpcCallFunction) { - var dummyRhelHostService = new KesselRhelHostServiceGrpc.KesselRhelHostServiceImplBase() { - @Override - public void createRhelHost(CreateRhelHostRequest request, StreamObserver responseObserver) { - super.createRhelHost(request, responseObserver); - } - }; - - var dummyNotificationService = new KesselNotificationsIntegrationServiceGrpc.KesselNotificationsIntegrationServiceImplBase() { - - @Override - public void createNotificationsIntegration(CreateNotificationsIntegrationRequest request, StreamObserver responseObserver) { - super.createNotificationsIntegration(request, responseObserver); - } - }; - - return runAgainstTemporaryServerTlsSelect(port, tlsEnabled, grpcCallFunction, dummyRhelHostService, dummyNotificationService); - } - - public static ServerCallDetails runAgainstTemporaryServer(int port, Call grpcCallFunction, BindableService... services) { - return runAgainstTemporaryServerTlsSelect(port, false, grpcCallFunction, services); - } - - public static ServerCallDetails runAgainstTemporaryTlsServer(int port, Call grpcCallFunction, BindableService... services) { - return runAgainstTemporaryServerTlsSelect(port, true, grpcCallFunction, services); - } - - private static ServerCallDetails runAgainstTemporaryServerTlsSelect(int port, boolean tlsEnabled, Call grpcCallFunction, BindableService... services) { - final ServerCallDetails serverCallDetails = new ServerCallDetails(); - - var spyInterceptor = new ServerInterceptor() { - @Override - public ServerCall.Listener interceptCall(ServerCall call, Metadata headers, ServerCallHandler next) { - serverCallDetails.setCall(call); - serverCallDetails.setMetadata(headers); - return next.startCall(call, headers); - } - }; - - FakeIdp fakeIdp = new FakeIdp(8090); - var serverSpy = new GrpcServerSpy(port, tlsEnabled, spyInterceptor, services); - - try { - fakeIdp.start(); - serverSpy.start(); - grpcCallFunction.call(); - serverSpy.shutdown(); - fakeIdp.stop(); - - return serverCallDetails; - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - serverSpy.shutdown(); - fakeIdp.stop(); - } - } - - @Override - public Server start() throws IOException { - server.start(); - return this; - } - - @Override - public Server shutdown() { - server.shutdown(); - return this; - } - - @Override - public Server shutdownNow() { - server.shutdownNow(); - return this; - } - - @Override - public boolean isShutdown() { - return server.isShutdown(); - } - - @Override - public boolean isTerminated() { - return server.isTerminated(); - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - return server.awaitTermination(timeout, unit); - } - - @Override - public void awaitTermination() throws InterruptedException { - server.awaitTermination(); - } - - public interface Call { - void call(); - } - - public static class ServerCallDetails { - private ServerCall call; - private Metadata metadata; - - public ServerCallDetails() { - } - - public ServerCall getCall() { - return call; - } - - public Metadata getMetadata() { - return metadata; - } - - public void setCall(ServerCall call) { - this.call = call; - } - - public void setMetadata(Metadata metadata) { - this.metadata = metadata; - } - } -} diff --git a/src/test/java/org/project_kessel/inventory/client/util/CertUtil.java b/src/test/java/org/project_kessel/inventory/client/util/CertUtil.java deleted file mode 100644 index 33eb709..0000000 --- a/src/test/java/org/project_kessel/inventory/client/util/CertUtil.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.project_kessel.inventory.client.util; - -import java.io.*; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; - -public class CertUtil { - private static final char[] passphrase = "changeit".toCharArray(); - private static final String selfSignedAlias = "selfsigned"; - private static final String certFileName = "certs/test.crt"; - - public static void addTestCACertToTrustStore() { - try { - var keystore = loadKeystoreFromJdk(); - if (keystore.containsAlias(selfSignedAlias)) { - return; - } - - try(InputStream certIn = Thread.currentThread().getContextClassLoader().getResourceAsStream(certFileName); - BufferedInputStream bis = new BufferedInputStream(certIn)) { - - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - while (bis.available() > 0) { - Certificate cert = cf.generateCertificate(bis); - keystore.setCertificateEntry(selfSignedAlias, cert); - } - - saveKeystoreToJdk(keystore); - } - } catch (CertificateException | KeyStoreException | IOException | NullPointerException e) { - throw new RuntimeException(e); - } - } - - public static void removeTestCACertFromKeystore() { - var keystore = loadKeystoreFromJdk(); - try { - keystore.deleteEntry(selfSignedAlias); - saveKeystoreToJdk(keystore); - } catch (KeyStoreException e) { - throw new RuntimeException(e); - } - } - - public static KeyStore loadKeystoreFromJdk() { - try (InputStream localCertIn = new FileInputStream(getCertFile())) { - KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); - keystore.load(localCertIn, passphrase); - return keystore; - } catch (IOException | CertificateException | KeyStoreException | NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - public static void saveKeystoreToJdk(KeyStore keystore) { - try (OutputStream out = new FileOutputStream(getCertFile())) { - keystore.store(out, passphrase); - } catch (IOException | CertificateException | KeyStoreException | NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - private static File getCertFile() { - final char sep = File.separatorChar; - File dir = new File(System.getProperty("java.home") + sep + "lib" + sep + "security"); - return new File(dir, "cacerts"); - } -}