diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java index d962e73f361..e2790519db9 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisioner.java @@ -43,7 +43,6 @@ import java.util.Set; import javax.inject.Inject; import javax.inject.Named; -import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.model.workspace.config.MachineConfig; import org.eclipse.che.api.core.model.workspace.config.ServerConfig; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; @@ -52,6 +51,7 @@ import org.eclipse.che.api.workspace.server.spi.environment.InternalMachineConfig; import org.eclipse.che.commons.lang.Size; import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager; +import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManagerException; import org.eclipse.che.workspace.infrastructure.kubernetes.environment.KubernetesEnvironment; import org.eclipse.che.workspace.infrastructure.kubernetes.server.ServerServiceBuilder; import org.eclipse.che.workspace.infrastructure.kubernetes.server.secure.jwtproxy.factory.JwtProxyConfigBuilderFactory; @@ -211,8 +211,8 @@ private void ensureJwtProxyInjected(KubernetesEnvironment k8sEnv) throws Infrast KeyPair keyPair; try { - keyPair = signatureKeyManager.getKeyPair(identity.getWorkspaceId()); - } catch (ServerException e) { + keyPair = signatureKeyManager.getOrCreateKeyPair(identity.getWorkspaceId()); + } catch (SignatureKeyManagerException e) { throw new InternalInfrastructureException( "Signature key pair for machine authentication cannot be retrieved. Reason: " + e.getMessage()); diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisionerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisionerTest.java index c851f95564d..5bbaf043036 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisionerTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/server/secure/jwtproxy/JwtProxyProvisionerTest.java @@ -75,7 +75,8 @@ public class JwtProxyProvisionerTest { @BeforeMethod public void setUp() throws Exception { - when(signatureKeyManager.getKeyPair(anyString())).thenReturn(new KeyPair(publicKey, null)); + when(signatureKeyManager.getOrCreateKeyPair(anyString())) + .thenReturn(new KeyPair(publicKey, null)); when(publicKey.getEncoded()).thenReturn("publickey".getBytes()); when(configBuilderFactory.create(any())) diff --git a/multiuser/keycloak/che-multiuser-keycloak-server/src/test/java/org/eclipse/che/multiuser/keycloak/server/KeycloakEnvironmentInitalizationFilterTest.java b/multiuser/keycloak/che-multiuser-keycloak-server/src/test/java/org/eclipse/che/multiuser/keycloak/server/KeycloakEnvironmentInitalizationFilterTest.java index 66a86cf0880..14f74516141 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-server/src/test/java/org/eclipse/che/multiuser/keycloak/server/KeycloakEnvironmentInitalizationFilterTest.java +++ b/multiuser/keycloak/che-multiuser-keycloak-server/src/test/java/org/eclipse/che/multiuser/keycloak/server/KeycloakEnvironmentInitalizationFilterTest.java @@ -85,7 +85,7 @@ public void setUp() throws Exception { parser.setAccessible(true); parser.set(filter, jwtParser); final KeyPair kp = new KeyPair(mock(PublicKey.class), mock(PrivateKey.class)); - when(keyManager.getKeyPair(anyString())).thenReturn(kp); + when(keyManager.getOrCreateKeyPair(anyString())).thenReturn(kp); } @Test diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/pom.xml b/multiuser/machine-auth/che-multiuser-machine-authentication/pom.xml index 48776f1c227..3017149ebfc 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/pom.xml +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/pom.xml @@ -90,10 +90,6 @@ org.eclipse.che.core che-core-api-workspace-shared - - org.eclipse.che.core - che-core-commons-annotations - org.eclipse.che.core che-core-commons-auth diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineLoginFilter.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineLoginFilter.java index be7dad2fb77..1d5bbd3db4a 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineLoginFilter.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineLoginFilter.java @@ -43,6 +43,8 @@ import org.eclipse.che.commons.subject.Subject; import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.multiuser.api.permission.server.PermissionChecker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Handles requests that comes from machines with specific machine token. @@ -53,6 +55,8 @@ @Singleton public class MachineLoginFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(MachineLoginFilter.class); + private final RequestTokenExtractor tokenExtractor; private final UserManager userManager; private final PermissionChecker permissionChecker; diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineSigningKeyResolver.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineSigningKeyResolver.java index d108bbedb17..c8abeaf7306 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineSigningKeyResolver.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineSigningKeyResolver.java @@ -21,8 +21,8 @@ import java.security.Key; import javax.inject.Inject; import javax.inject.Singleton; -import org.eclipse.che.api.core.ServerException; import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager; +import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManagerException; /** Resolves signing key pair based on workspace Id claim of token. */ @Singleton @@ -46,9 +46,9 @@ public Key resolveSigningKey(JwsHeader header, Claims claims) { "Unable to fetch signature key pair: no workspace id present in token"); } try { - return keyManager.getKeyPair(wsId).getPublic(); - } catch (ServerException e) { - throw new JwtException("Unable to fetch signature key pair:" + e.getMessage()); + return keyManager.getOrCreateKeyPair(wsId).getPublic(); + } catch (SignatureKeyManagerException e) { + throw new JwtException("Unable to fetch signature key pair:" + e.getMessage(), e); } } } diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineTokenRegistry.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineTokenRegistry.java index 5deb41b95ad..41faaf151bd 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineTokenRegistry.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineTokenRegistry.java @@ -33,7 +33,9 @@ import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.model.user.User; import org.eclipse.che.api.user.server.UserManager; +import org.eclipse.che.api.workspace.server.token.MachineTokenException; import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager; +import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManagerException; import org.eclipse.che.multiuser.machine.authentication.shared.Constants; /** @@ -66,9 +68,9 @@ public MachineTokenRegistry(SignatureKeyManager signatureKeyManager, UserManager * @param userId id of user to get token * @param workspaceId id of workspace to get token * @return machine security token for for given user and workspace - * @throws IllegalStateException when user with given id not found or any errors occurs + * @throws MachineTokenException when user with given id not found or any errors occurs */ - public String getOrCreateToken(String userId, String workspaceId) { + public String getOrCreateToken(String userId, String workspaceId) throws MachineTokenException { lock.writeLock().lock(); try { final Map wsRow = tokens.row(workspaceId); @@ -77,41 +79,43 @@ public String getOrCreateToken(String userId, String workspaceId) { token = createToken(userId, workspaceId); } return token; - } catch (NotFoundException | ServerException ex) { - throw new IllegalStateException( - format( - "Failed to generate machine token for user '%s' and workspace '%s'. Cause: '%s'", - userId, workspaceId, ex.getMessage()), - ex); } finally { lock.writeLock().unlock(); } } /** Creates new token with given data. */ - private String createToken(String userId, String workspaceId) - throws NotFoundException, ServerException { - final PrivateKey privateKey = signatureKeyManager.getKeyPair(workspaceId).getPrivate(); - final User user = userManager.getById(userId); - final Map header = new HashMap<>(2); - header.put("kind", MACHINE_TOKEN_KIND); - header.put("kid", workspaceId); - final Map claims = new HashMap<>(); - // to ensure that each token is unique - claims.put(Claims.ID, UUID.randomUUID().toString()); - claims.put(Constants.USER_ID_CLAIM, userId); - claims.put(Constants.USER_NAME_CLAIM, user.getName()); - claims.put(Constants.WORKSPACE_ID_CLAIM, workspaceId); - // jwtproxy required claims - claims.put(Claims.ISSUER, "wsmaster"); - claims.put(Claims.AUDIENCE, workspaceId); - claims.put(Claims.EXPIRATION, Instant.now().plus(365, DAYS).getEpochSecond()); - claims.put(Claims.NOT_BEFORE, -1); // always - claims.put(Claims.ISSUED_AT, Instant.now().getEpochSecond()); - final String token = - Jwts.builder().setClaims(claims).setHeader(header).signWith(RS256, privateKey).compact(); - tokens.put(workspaceId, userId, token); - return token; + private String createToken(String userId, String workspaceId) throws MachineTokenException { + try { + final PrivateKey privateKey = + signatureKeyManager.getOrCreateKeyPair(workspaceId).getPrivate(); + final User user = userManager.getById(userId); + final Map header = new HashMap<>(2); + header.put("kind", MACHINE_TOKEN_KIND); + header.put("kid", workspaceId); + final Map claims = new HashMap<>(); + // to ensure that each token is unique + claims.put(Claims.ID, UUID.randomUUID().toString()); + claims.put(Constants.USER_ID_CLAIM, userId); + claims.put(Constants.USER_NAME_CLAIM, user.getName()); + claims.put(Constants.WORKSPACE_ID_CLAIM, workspaceId); + // jwtproxy required claims + claims.put(Claims.ISSUER, "wsmaster"); + claims.put(Claims.AUDIENCE, workspaceId); + claims.put(Claims.EXPIRATION, Instant.now().plus(365, DAYS).getEpochSecond()); + claims.put(Claims.NOT_BEFORE, -1); // always + claims.put(Claims.ISSUED_AT, Instant.now().getEpochSecond()); + final String token = + Jwts.builder().setClaims(claims).setHeader(header).signWith(RS256, privateKey).compact(); + tokens.put(workspaceId, userId, token); + return token; + } catch (SignatureKeyManagerException | NotFoundException | ServerException ex) { + throw new MachineTokenException( + format( + "Failed to generate machine token for user '%s' and workspace '%s'. Cause: '%s'", + userId, workspaceId, ex.getMessage()), + ex); + } } /** diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManager.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManager.java index 3a8b1e937a1..dcf0f0e374e 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManager.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManager.java @@ -15,9 +15,6 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -28,8 +25,6 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; @@ -40,7 +35,6 @@ import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.core.notification.EventSubscriber; import org.eclipse.che.api.workspace.shared.dto.event.WorkspaceStatusEvent; -import org.eclipse.che.commons.annotation.Nullable; import org.eclipse.che.core.db.DBInitializer; import org.eclipse.che.multiuser.machine.authentication.server.signature.model.impl.SignatureKeyPairImpl; import org.eclipse.che.multiuser.machine.authentication.server.signature.spi.SignatureKeyDao; @@ -72,8 +66,6 @@ public class SignatureKeyManager { @SuppressWarnings("unused") private DBInitializer dbInitializer; - private LoadingCache cachedPair; - @Inject public SignatureKeyManager( @Named("che.auth.signature_key_size") int keySize, @@ -84,19 +76,6 @@ public SignatureKeyManager( this.algorithm = algorithm; this.eventService = eventService; this.signatureKeyDao = signatureKeyDao; - - this.cachedPair = - CacheBuilder.newBuilder() - .maximumSize(100) - .expireAfterAccess(2, TimeUnit.HOURS) - .build( - new CacheLoader() { - @Override - public KeyPair load(String key) throws Exception { - return loadKeyPair(key); - } - }); - this.workspaceEventsSubscriber = new EventSubscriber() { @Override @@ -108,21 +87,34 @@ public void onEvent(WorkspaceStatusEvent event) { }; } - /** Returns cached instance of {@link KeyPair} or null when failed to load key pair. */ - @Nullable - public KeyPair getKeyPair(String workspaceId) throws ServerException { + /** + * Returns instance of {@link KeyPair} for given workspace. + * + * @throws SignatureKeyManagerException when stored keypair is incorrect (e.g. has bad algorithm + * or keyspec) or other error + */ + public KeyPair getOrCreateKeyPair(String workspaceId) throws SignatureKeyManagerException { + SignatureKeyPair keyPair; try { - return cachedPair.get(workspaceId); - } catch (ExecutionException e) { - throw new ServerException(e.getCause()); + try { + keyPair = signatureKeyDao.get(workspaceId); + } catch (NotFoundException e) { + keyPair = generateKeyPair(workspaceId); + } + } catch (NoSuchAlgorithmException | ServerException | ConflictException ex) { + LOG.error( + "Failed to load signature keys for ws {}. Cause: {}", workspaceId, ex.getMessage()); + throw new SignatureKeyManagerException(ex.getMessage(), ex); } + return toJavaKeyPair(keyPair); } /** Removes key pair from cache and DB. */ - public void removeKeyPair(String workspaceId) { + @VisibleForTesting + void removeKeyPair(String workspaceId) { try { - cachedPair.invalidate(workspaceId); signatureKeyDao.remove(workspaceId); + LOG.debug("Removed signature key pair for ws id {}.", workspaceId); } catch (ServerException e) { LOG.error( "Unable to cleanup machine token signature keypairs for ws {}. Cause: {}", @@ -131,48 +123,43 @@ public void removeKeyPair(String workspaceId) { } } - /** Loads signature key pair if no existing keys found then stores a newly generated key pair. */ - @PostConstruct @VisibleForTesting - KeyPair loadKeyPair(String workspaceId) throws ServerException, ConflictException { + SignatureKeyPair generateKeyPair(String workspaceId) + throws NoSuchAlgorithmException, ServerException, ConflictException { try { - return toJavaKeyPair(signatureKeyDao.get(workspaceId)); - } catch (NotFoundException nfe) { - try { - return toJavaKeyPair(signatureKeyDao.create(generateKeyPair(workspaceId))); - } catch (ConflictException | ServerException ex) { - LOG.error( - "Failed to store signature keys for ws {}. Cause: {}", workspaceId, ex.getMessage()); - throw ex; - } - } catch (ServerException ex) { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm); + kpg.initialize(keySize); + final KeyPair pair = kpg.generateKeyPair(); + final SignatureKeyPairImpl kp = + new SignatureKeyPairImpl(workspaceId, pair.getPublic(), pair.getPrivate()); + LOG.debug( + "Generated signature key pair with ws id {} and algorithm {}.", + kp.getWorkspaceId(), + algorithm); + return signatureKeyDao.create(kp); + } catch (NoSuchAlgorithmException | ConflictException | ServerException ex) { LOG.error( - "Failed to load signature keys for ws {}. Cause: {}", workspaceId, ex.getMessage()); + "Unable to generate signature keypair for ws {}. Cause: {}", + workspaceId, + ex.getMessage()); throw ex; } } - @VisibleForTesting - SignatureKeyPairImpl generateKeyPair(String workspaceId) throws ServerException { - final KeyPairGenerator kpg; - try { - kpg = KeyPairGenerator.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - throw new ServerException(ex.getMessage(), ex); + /** Returns key spec by key format and encoded data. */ + private EncodedKeySpec getKeySpec(SignatureKey key) { + switch (key.getFormat()) { + case PKCS_8: + return new PKCS8EncodedKeySpec(key.getEncoded()); + case X_509: + return new X509EncodedKeySpec(key.getEncoded()); + default: + throw new IllegalArgumentException( + String.format("Unsupported key spec '%s' for signature keys", key.getFormat())); } - kpg.initialize(keySize); - final KeyPair pair = kpg.generateKeyPair(); - final SignatureKeyPairImpl kp = - new SignatureKeyPairImpl(workspaceId, pair.getPublic(), pair.getPrivate()); - LOG.debug( - "Generated signature key pair with ws id {} and algorithm {}.", - kp.getWorkspaceId(), - algorithm); - return kp; } - /** Converts {@link SignatureKeyPair} to {@link KeyPair}. */ - public static KeyPair toJavaKeyPair(SignatureKeyPair keyPair) throws ServerException { + private KeyPair toJavaKeyPair(SignatureKeyPair keyPair) throws SignatureKeyManagerException { try { final PrivateKey privateKey = KeyFactory.getInstance(keyPair.getPrivateKey().getAlgorithm()) @@ -183,20 +170,7 @@ public static KeyPair toJavaKeyPair(SignatureKeyPair keyPair) throws ServerExcep return new KeyPair(publicKey, privateKey); } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { LOG.error("Failed to convert signature key pair to Java keys. Cause: {}", ex.getMessage()); - throw new ServerException("Failed to convert signature key pair to Java keys."); - } - } - - /** Returns key spec by key format and encoded data. */ - private static EncodedKeySpec getKeySpec(SignatureKey key) { - switch (key.getFormat()) { - case PKCS_8: - return new PKCS8EncodedKeySpec(key.getEncoded()); - case X_509: - return new X509EncodedKeySpec(key.getEncoded()); - default: - throw new IllegalArgumentException( - String.format("Unsupported key spec '%s' for signature keys", key.getFormat())); + throw new SignatureKeyManagerException("Failed to convert signature key pair to Java keys."); } } diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManagerException.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManagerException.java new file mode 100644 index 00000000000..a4d7c972f39 --- /dev/null +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManagerException.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.multiuser.machine.authentication.server.signature; + +public class SignatureKeyManagerException extends Exception { + public SignatureKeyManagerException(String message) { + super(message); + } + + public SignatureKeyManagerException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignaturePublicKeyEnvProvider.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignaturePublicKeyEnvProvider.java index 54874aa99e3..c4944a72a72 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignaturePublicKeyEnvProvider.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignaturePublicKeyEnvProvider.java @@ -15,7 +15,6 @@ import java.util.Base64; import javax.inject.Inject; -import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity; import org.eclipse.che.api.workspace.server.spi.InfrastructureException; import org.eclipse.che.api.workspace.server.spi.provision.env.EnvVarProvider; @@ -44,10 +43,10 @@ public Pair get(RuntimeIdentity runtimeIdentity) throws Infrastr Base64.getEncoder() .encode( keyManager - .getKeyPair(runtimeIdentity.getWorkspaceId()) + .getOrCreateKeyPair(runtimeIdentity.getWorkspaceId()) .getPublic() .getEncoded()))); - } catch (ServerException e) { + } catch (SignatureKeyManagerException e) { throw new InfrastructureException( "Signature key pair for machine authentication cannot be retrieved. Reason: " + e.getMessage()); diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/jpa/JpaSignatureKeyDao.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/jpa/JpaSignatureKeyDao.java index b3d60106282..fdf20a8483d 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/jpa/JpaSignatureKeyDao.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/signature/jpa/JpaSignatureKeyDao.java @@ -29,6 +29,7 @@ import org.eclipse.che.api.workspace.server.event.BeforeWorkspaceRemovedEvent; import org.eclipse.che.core.db.cascade.CascadeEventSubscriber; import org.eclipse.che.core.db.jpa.DuplicateKeyException; +import org.eclipse.che.core.db.jpa.IntegrityConstraintViolationException; import org.eclipse.che.multiuser.machine.authentication.server.signature.model.impl.SignatureKeyPairImpl; import org.eclipse.che.multiuser.machine.authentication.server.signature.spi.SignatureKeyDao; @@ -53,6 +54,12 @@ public SignatureKeyPairImpl create(SignatureKeyPairImpl keyPair) requireNonNull(keyPair, "Required non-null key pair"); try { doCreate(keyPair); + } catch (IntegrityConstraintViolationException x) { + throw new ConflictException( + format( + "Unable to create signature key pair because referenced workspace with id '%s' doesn't exist", + keyPair.getWorkspaceId())); + } catch (DuplicateKeyException dkEx) { throw new ConflictException( format("Signature key pair for workspace '%s' already exists", keyPair.getWorkspaceId())); diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/MachineLoginFilterTest.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/MachineLoginFilterTest.java index a894a0d0039..136b024c94d 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/MachineLoginFilterTest.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/MachineLoginFilterTest.java @@ -107,7 +107,7 @@ private void setUp() throws Exception { permissionCheckerMock); when(tokenExtractorMock.getToken(any(HttpServletRequest.class))).thenReturn(token); - when(keyManagerMock.getKeyPair(eq(WORKSPACE_ID))).thenReturn(keyPair); + when(keyManagerMock.getOrCreateKeyPair(eq(WORKSPACE_ID))).thenReturn(keyPair); when(userMock.getName()).thenReturn(SUBJECT.getUserName()); when(userManagerMock.getById(SUBJECT.getUserId())).thenReturn(userMock); @@ -117,7 +117,7 @@ private void setUp() throws Exception { public void testProcessRequestWithValidToken() throws Exception { machineLoginFilter.doFilter(getRequestMock(), responseMock, chainMock); - verify(keyManagerMock).getKeyPair(eq(WORKSPACE_ID)); + verify(keyManagerMock).getOrCreateKeyPair(eq(WORKSPACE_ID)); verify(userManagerMock).getById(anyString()); verifyZeroInteractions(responseMock); } @@ -128,7 +128,7 @@ public void testNotProceedRequestWhenSignatureCheckIsFailed() throws Exception { final KeyPairGenerator kpg = KeyPairGenerator.getInstance(SIGNATURE_ALGORITHM); kpg.initialize(KEY_SIZE); final KeyPair pair = kpg.generateKeyPair(); - when(keyManagerMock.getKeyPair(eq(WORKSPACE_ID))).thenReturn(pair); + when(keyManagerMock.getOrCreateKeyPair(eq(WORKSPACE_ID))).thenReturn(pair); machineLoginFilter.doFilter(requestMock, responseMock, chainMock); @@ -186,7 +186,7 @@ public void testSetErrorInResponseWhenNoUserFoundForProvidedToken() throws Excep machineLoginFilter.doFilter(getRequestMock(), responseMock, chainMock); - verify(keyManagerMock).getKeyPair(eq(WORKSPACE_ID)); + verify(keyManagerMock).getOrCreateKeyPair(eq(WORKSPACE_ID)); verify(userManagerMock).getById(anyString()); verify(responseMock) .sendError( @@ -200,7 +200,7 @@ public void testSetErrorInResponseWhenUnableToGetUserForProvidedToken() throws E machineLoginFilter.doFilter(getRequestMock(), responseMock, chainMock); - verify(keyManagerMock).getKeyPair(eq(WORKSPACE_ID)); + verify(keyManagerMock).getOrCreateKeyPair(eq(WORKSPACE_ID)); verify(userManagerMock).getById(anyString()); verify(responseMock) .sendError( diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/MachineTokenRegistryTest.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/MachineTokenRegistryTest.java index 98ecd5fe80a..e56152c9a15 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/MachineTokenRegistryTest.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/MachineTokenRegistryTest.java @@ -31,6 +31,8 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.user.User; import org.eclipse.che.api.user.server.UserManager; +import org.eclipse.che.api.workspace.server.WorkspaceManager; +import org.eclipse.che.api.workspace.server.token.MachineTokenException; import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager; import org.mockito.Mock; @@ -58,6 +60,7 @@ public class MachineTokenRegistryTest { @Mock private SignatureKeyManager signatureKeyManager; @Mock private UserManager userManager; + @Mock private WorkspaceManager workspaceManager; private KeyPair keyPair; @@ -69,7 +72,7 @@ private void setUp() throws Exception { keyPair = kpg.generateKeyPair(); mockUser(USER_ID, USER_NAME); - when(signatureKeyManager.getKeyPair(anyString())).thenReturn(keyPair); + when(signatureKeyManager.getOrCreateKeyPair(anyString())).thenReturn(keyPair); } @Test @@ -89,11 +92,11 @@ public void testCreatesNewTokenWhenNoPreviouslyCreatedFound() throws Exception { assertEquals(subject.getUserName(), USER_NAME); assertEquals(claims.get(WORKSPACE_ID_CLAIM, String.class), WORKSPACE_ID); verify(userManager).getById(USER_ID); - verify(signatureKeyManager).getKeyPair(anyString()); + verify(signatureKeyManager).getOrCreateKeyPair(anyString()); assertNotNull(generatedToken); } - @Test(expectedExceptions = IllegalStateException.class) + @Test(expectedExceptions = MachineTokenException.class) public void testThrowsIllegalStateExceptionWhenTryToGetTokenForNonExistingUser() throws Exception { when(userManager.getById(anyString())).thenThrow(new NotFoundException("User not found")); diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManagerTest.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManagerTest.java index 6c58d9c0bc5..2f968ebc5b8 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManagerTest.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/signature/SignatureKeyManagerTest.java @@ -11,11 +11,9 @@ */ package org.eclipse.che.multiuser.machine.authentication.server.signature; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -25,8 +23,6 @@ import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; -import org.eclipse.che.api.core.NotFoundException; -import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.model.workspace.WorkspaceStatus; import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.core.notification.EventSubscriber; @@ -38,7 +34,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.stubbing.Answer; import org.mockito.testng.MockitoTestNGListener; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Listeners; @@ -72,82 +67,43 @@ public void createEntities() throws Exception { } @Test - public void testLoadSignatureKeys() throws Exception { - String wsId = "WS_id_1"; - final SignatureKeyPairImpl kp = newKeyPair(wsId); - when(signatureKeyDao.get(anyString())).thenReturn(kp); - - signatureKeyManager.loadKeyPair(wsId); - - final KeyPair cachedPair = signatureKeyManager.getKeyPair(wsId); - assertNotNull(cachedPair); - assertKeys(cachedPair.getPublic(), kp.getPublicKey()); - assertKeys(cachedPair.getPrivate(), kp.getPrivateKey()); - } - - @Test - public void testTriesToLoadKeysOnGettingKeyPairAndNoCachedKeyPair() throws Exception { - String wsId = "WS_id_1"; - final SignatureKeyPairImpl kp = newKeyPair(wsId); - when(signatureKeyDao.create(any(SignatureKeyPairImpl.class))).thenReturn(kp); - when(signatureKeyDao.get(anyString())).thenThrow(new NotFoundException("not found")); - - signatureKeyManager.getKeyPair("ws1"); - - verify(signatureKeyDao).get(anyString()); - verify(signatureKeyDao).create(any(SignatureKeyPairImpl.class)); - } - - @Test - public void testGeneratesNewKeyPairWhenNoExistingKeyPairFound() throws Exception { - doThrow(NotFoundException.class).when(signatureKeyDao).get(anyString()); - when(signatureKeyDao.create(any(SignatureKeyPairImpl.class))) - .thenAnswer((Answer) invoke -> invoke.getArgument(0)); - - final KeyPair cachedPair = signatureKeyManager.getKeyPair("ws1"); - - verify(signatureKeyDao).get(anyString()); - verify(signatureKeyDao).create(any(SignatureKeyPairImpl.class)); - assertNotNull(cachedPair); - } - - @Test(expectedExceptions = ServerException.class) - public void testThrowsExceptionWhenFailedToLoadAndGenerateKeys() throws Exception { - doThrow(NotFoundException.class).when(signatureKeyDao).get(anyString()); - when(signatureKeyDao.create(any(SignatureKeyPairImpl.class))) - .thenThrow(new ServerException("unexpected end of stack")); + public void shouldRemoveKeyPairOnWorkspaceStop() throws Exception { + final String wsId = "ws123"; + signatureKeyManager.subscribe(); + verify(eventService).subscribe(captor.capture()); + final EventSubscriber subscriber = captor.getValue(); - signatureKeyManager.getKeyPair("ws1"); + subscriber.onEvent( + DtoFactory.newDto(WorkspaceStatusEvent.class) + .withStatus(WorkspaceStatus.STOPPED) + .withWorkspaceId(wsId)); - verify(signatureKeyDao).get(anyString()); - verify(signatureKeyDao).create(any(SignatureKeyPairImpl.class)); + verify(signatureKeyDao, times(1)).remove(eq(wsId)); } - @Test(expectedExceptions = ServerException.class) - public void testThrowsExceptionWhenAlgorithmIsNotSupported() throws Exception { + @Test(expectedExceptions = SignatureKeyManagerException.class) + public void shouldThrowsExceptionWhenAlgorithmIsNotSupported() throws Exception { final SignatureKeyImpl publicKey = new SignatureKeyImpl(new byte[] {}, "ECDH", "PKCS#15"); final SignatureKeyImpl privateKey = new SignatureKeyImpl(new byte[] {}, "ECDH", "PKCS#3"); final SignatureKeyPairImpl kp = new SignatureKeyPairImpl("id_" + 1, publicKey, privateKey); doReturn(kp).when(signatureKeyDao).get(anyString()); - signatureKeyManager.getKeyPair("ws1"); + signatureKeyManager.getOrCreateKeyPair("ws1"); verify(signatureKeyDao).get(anyString()); } @Test - public void shouldRemoveKeyPairOnWorkspaceStop() throws Exception { - final String wsId = "ws123"; - signatureKeyManager.subscribe(); - verify(eventService).subscribe(captor.capture()); - final EventSubscriber subscriber = captor.getValue(); + public void shouldReturnSignatureKeys() throws Exception { + String wsId = "WS_id_1"; + final SignatureKeyPairImpl kp = newKeyPair(wsId); + when(signatureKeyDao.get(anyString())).thenReturn(kp); - subscriber.onEvent( - DtoFactory.newDto(WorkspaceStatusEvent.class) - .withStatus(WorkspaceStatus.STOPPED) - .withWorkspaceId(wsId)); + final KeyPair cachedPair = signatureKeyManager.getOrCreateKeyPair(wsId); - verify(signatureKeyDao, times(1)).remove(eq(wsId)); + assertNotNull(cachedPair); + assertKeys(cachedPair.getPublic(), kp.getPublicKey()); + assertKeys(cachedPair.getPrivate(), kp.getPrivateKey()); } private SignatureKeyPairImpl newKeyPair(String id) { diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/signature/spi/tck/SignatureKeyDaoTest.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/signature/spi/tck/SignatureKeyDaoTest.java index 18f1774ff6b..516635ef114 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/signature/spi/tck/SignatureKeyDaoTest.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/test/java/org/eclipse/che/multiuser/machine/authentication/server/signature/spi/tck/SignatureKeyDaoTest.java @@ -121,6 +121,15 @@ public void throwsConflictExceptionWhenCreatingSignatureKeyPair() throws Excepti dao.create(signKeyPair); } + @Test( + expectedExceptions = ConflictException.class, + expectedExceptionsMessageRegExp = + "Unable to create signature key pair because referenced workspace with id '.*' doesn't exist") + public void throwsConflictExceptionWhenCreatingKeyPairNotExistedWs() throws Exception { + + dao.create(newKeyPair("wrong_ws")); + } + @Test(expectedExceptions = NotFoundException.class) public void throwsNoResultExceptionWhenSearchingWrongWorkspace() throws Exception { dao.get("unknown");