Skip to content

Commit

Permalink
Change machine token to JWT and rewrite auth mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
akorneta committed Apr 11, 2018
1 parent f85c0a2 commit 38ff5c0
Show file tree
Hide file tree
Showing 36 changed files with 2,078 additions and 234 deletions.
Expand Up @@ -65,6 +65,8 @@
<class>org.eclipse.che.multiuser.organization.spi.impl.OrganizationDistributedResourcesImpl</class>

<class>org.eclipse.che.api.workspace.activity.WorkspaceExpiration</class>
<class>org.eclipse.che.multiuser.machine.authentication.server.signature.model.impl.SignatureKeyImpl</class>
<class>org.eclipse.che.multiuser.machine.authentication.server.signature.model.impl.SignatureKeyPairImpl</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>
8 changes: 8 additions & 0 deletions multiuser/keycloak/che-multiuser-keycloak-server/pom.xml
Expand Up @@ -121,6 +121,14 @@
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-keycloak-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-machine-authentication</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-machine-authentication-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-personal-account</artifactId>
Expand Down
Expand Up @@ -10,10 +10,20 @@
*/
package org.eclipse.che.multiuser.keycloak.server;

import static org.eclipse.che.multiuser.machine.authentication.shared.Constants.MACHINE_TOKEN_KIND;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import java.security.PublicKey;
import javax.inject.Inject;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;

/**
* Base abstract class for the Keycloak-related servlet filters.
Expand All @@ -23,10 +33,23 @@
*/
public abstract class AbstractKeycloakFilter implements Filter {

@Inject protected SignatureKeyManager signatureKeyManager;

/** when a request came from a machine with valid token then auth is not required */
protected boolean shouldSkipAuthentication(HttpServletRequest request, String token) {
return (token != null && token.startsWith("machine"))
|| (request.getRequestURI() != null
&& request.getRequestURI().endsWith("api/keycloak/OIDCKeycloak.js"));
if (token == null) {
return false;
}
try {
final PublicKey publicKey = signatureKeyManager.getKeyPair().getPublic();
final Jwt jwt = Jwts.parser().setSigningKey(publicKey).parse(token);
return MACHINE_TOKEN_KIND.equals(jwt.getHeader().get("kind"))
|| (request.getRequestURI() != null
&& request.getRequestURI().endsWith("api/keycloak/OIDCKeycloak.js"));
} catch (ExpiredJwtException | MalformedJwtException | SignatureException ex) {
// given token is not signed by particular signature key so it must be checked in another way
return false;
}
}

@Override
Expand Down
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.keycloak.server;

import static io.jsonwebtoken.SignatureAlgorithm.RS256;
import static org.eclipse.che.multiuser.machine.authentication.shared.Constants.MACHINE_TOKEN_KIND;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

import io.jsonwebtoken.Jwts;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

/**
* Tests {@link AbstractKeycloakFilter}.
*
* @author Anton Korneta
*/
@Listeners(MockitoTestNGListener.class)
public class AbstractKeycloakFilterTest {

@Mock private HttpServletRequest request;
@Mock private SignatureKeyManager signatureKeyManager;

@InjectMocks private TestLoginFilter abstractKeycloakFilter;

private String machineToken;

@BeforeMethod
public void setup() throws Exception {
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
final KeyPair keyPair = kpg.generateKeyPair();
final Map<String, Object> header = new HashMap<>();
header.put("kind", MACHINE_TOKEN_KIND);
machineToken =
Jwts.builder()
.setPayload("payload")
.setHeader(header)
.signWith(RS256, keyPair.getPrivate())
.compact();

when(signatureKeyManager.getKeyPair()).thenReturn(keyPair);
}

@Test
public void testShouldNotSkipAuthWhenNullTokenProvided() {
assertFalse(abstractKeycloakFilter.shouldSkipAuthentication(request, null));
}

@Test
public void testShouldNotSkipAuthWhenProvidedTokenIsNotMachine() {
assertFalse(abstractKeycloakFilter.shouldSkipAuthentication(request, "testToken"));
}

@Test
public void testAuthIsNotNeededWhenMachineTokenProvided() throws Exception {
assertTrue(abstractKeycloakFilter.shouldSkipAuthentication(request, machineToken));
}

static class TestLoginFilter extends AbstractKeycloakFilter {
@Override
public void doFilter(
ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {}
}
}
Expand Up @@ -10,19 +10,26 @@
*/
package org.eclipse.che.multiuser.keycloak.server;

import static io.jsonwebtoken.SignatureAlgorithm.RS512;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.AssertJUnit.assertEquals;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.impl.DefaultClaims;
import io.jsonwebtoken.impl.DefaultHeader;
import io.jsonwebtoken.impl.DefaultJwt;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
Expand All @@ -36,6 +43,8 @@
import org.eclipse.che.commons.subject.SubjectImpl;
import org.eclipse.che.multiuser.api.permission.server.AuthorizedSubject;
import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
import org.eclipse.che.multiuser.machine.authentication.server.signature.SignatureKeyManager;
import org.eclipse.che.multiuser.machine.authentication.shared.Constants;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
Expand All @@ -47,6 +56,7 @@
@Listeners(value = {MockitoTestNGListener.class})
public class KeycloakEnvironmentInitalizationFilterTest {

@Mock private SignatureKeyManager keyManager;
@Mock private KeycloakUserManager userManager;
@Mock private RequestTokenExtractor tokenExtractor;
@Mock private PermissionChecker permissionChecker;
Expand All @@ -66,22 +76,33 @@ public void setUp() throws Exception {
EnvironmentContext.setCurrent(context);
filter =
new KeycloakEnvironmentInitalizationFilter(userManager, tokenExtractor, permissionChecker);
filter.signatureKeyManager = keyManager;
final KeyPair kp = new KeyPair(mock(PublicKey.class), mock(PrivateKey.class));
when(keyManager.getKeyPair()).thenReturn(kp);
}

@Test
public void shouldSkipRequestsWithMachineTokens() throws Exception {

EnvironmentContext context = EnvironmentContext.getCurrent();
// given
when(tokenExtractor.getToken(any(HttpServletRequest.class))).thenReturn("machine123123token");
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
final KeyPair keyPair = kpg.generateKeyPair();
when(keyManager.getKeyPair()).thenReturn(keyPair);
final Map<String, Object> header = new HashMap<>();
header.put("kind", Constants.MACHINE_TOKEN_KIND);
final String token =
Jwts.builder()
.setPayload("payload")
.setHeader(header)
.signWith(RS512, keyPair.getPrivate())
.compact();
when(tokenExtractor.getToken(any(HttpServletRequest.class))).thenReturn(token);

// when
filter.doFilter(request, response, chain);

// then
verify(chain).doFilter(eq(request), eq(response));
verifyNoMoreInteractions(userManager);
verifyNoMoreInteractions(context);
}

@Test
Expand Down
Expand Up @@ -24,10 +24,18 @@
<dto-generator-out-directory>${project.build.directory}/generated-sources/dto/</dto-generator-out-directory>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
Expand All @@ -44,10 +52,6 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-user-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-auth</artifactId>
Expand All @@ -56,6 +60,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-test</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.multiuser</groupId>
<artifactId>che-multiuser-machine-authentication-shared</artifactId>
</dependency>
<dependency>
<groupId>org.everrest</groupId>
<artifactId>everrest-test</artifactId>
Expand Down

0 comments on commit 38ff5c0

Please sign in to comment.