Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 13 additions & 27 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
# Eclipse project files
.project
.classpath
.settings

# IntelliJ IDEA project files and directories
*.iml
*.ipr
*.iws
.idea/

# Geany project file
.geany

# KDevelop project file and directory
.kdev4/
*.kdev4
# everything that starts with dot (hidden files)
.*
# except this file
!.gitignore
# except this file-extention
!.*.yml

# Build targets
/target
*/target

# Report directories
/reports
*/reports
**/target/

# Mac-specific directory that no other operating system needs.
.DS_Store
# logs and reports
*.csv
*.log
*.zip

# JVM crash logs
hs_err_pid*.log
# IntelliJ IDEA project files and directories
*.iml
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ jdk: openjdk8
before_install:
- "./src/main/scripts/ci/before-install.sh"
- "./src/main/scripts/cd/before-deploy.sh"
script: "mvn verify -B"
after_success: "./src/main/scripts/ci/after-success.sh"
deploy:
- provider: script
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
package io.scalecube.security.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.scalecube.security.api.Profile;
import java.util.Map;
import reactor.core.publisher.Mono;

public final class DefaultJwtAuthenticator implements JwtAuthenticator {

private final JwtParser jwtParser;
private final JwtKeyResolver jwtKeyResolver;

public DefaultJwtAuthenticator(JwtKeyResolver jwtKeyResolver) {
jwtParser = Jwts.parser().setSigningKeyResolver(new DefaultSigningKeyResolver(jwtKeyResolver));
this.jwtKeyResolver = jwtKeyResolver;
}

@Override
public Mono<Profile> authenticate(String token) {
return Mono.just(token)
.map(jwtParser::parseClaimsJws)
.map(Jws<Claims>::getBody)
.map(this::profileFromClaims)
.onErrorMap(AuthenticationException::new);
return Mono.defer(
() -> {
String tokenWithoutSignature = token.substring(0, token.lastIndexOf(".") + 1);

JwtParser parser = Jwts.parser();

Jwt<Header, Claims> claims = parser.parseClaimsJwt(tokenWithoutSignature);

return jwtKeyResolver
.resolve((Map<String, Object>) claims.getHeader())
.map(key -> parser.setSigningKey(key).parseClaimsJws(token).getBody())
.map(this::profileFromClaims)
.onErrorMap(AuthenticationException::new);
});
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import java.security.Key;
import java.util.Map;
import reactor.core.publisher.Mono;

@FunctionalInterface
public interface JwtKeyResolver {

Key resolve(Map<String, Object> tokenClaims);
Mono<Key> resolve(Map<String, Object> jtwHeaders);
}
45 changes: 18 additions & 27 deletions jwt/src/test/java/io/scalecube/security/JwtAuthenticatorTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
Expand All @@ -22,6 +23,7 @@
import javax.crypto.spec.SecretKeySpec;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

class JwtAuthenticatorTests {
Expand All @@ -46,7 +48,7 @@ void authenticateAuthenticateUsingKidHeaderPropertyAuthenticationSuccess() {
.signWith(hmacSecretKey)
.compact();

JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> hmacSecretKey);
JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> Mono.just(hmacSecretKey));

StepVerifier.create(sut.authenticate(token))
.assertNext(
Expand All @@ -71,7 +73,7 @@ void authenticateCreateTokenAndAuthenticateHmacAuthenticationSuccess() {
.signWith(hmacSecretKey)
.compact();

JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> hmacSecretKey);
JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> Mono.just(hmacSecretKey));

StepVerifier.create(sut.authenticate(token))
.assertNext(
Expand Down Expand Up @@ -100,9 +102,10 @@ void authenticateValidTokenInvalidHmacSecretAuthenticationFailedExceptionThrown(
JwtAuthenticator sut =
new DefaultJwtAuthenticator(
map ->
new SecretKeySpec(
UUID.randomUUID().toString().getBytes(),
SignatureAlgorithm.HS256.getJcaName()));
Mono.just(
new SecretKeySpec(
UUID.randomUUID().toString().getBytes(),
SignatureAlgorithm.HS256.getJcaName())));
StepVerifier.create(sut.authenticate(token))
.expectErrorSatisfies(
actualException ->
Expand All @@ -128,12 +131,8 @@ void authenticateUsingKidHeaderPropertyKidIsMissingAuthenticationFailsExceptionT
map ->
Optional.ofNullable(map.get("kid"))
.filter(String.class::isInstance)
.map(
s -> {
// Safe to cast to string, use the kid property to fetch the key
return hmacSecretKey;
})
.orElse(null));
.map(s -> Mono.just(hmacSecretKey))
.orElse(Mono.empty()));

StepVerifier.create(sut.authenticate(token))
.expectErrorSatisfies(
Expand All @@ -156,7 +155,7 @@ void authenticateCreateTokenAndValidateRsaAuthenticationSuccess() {
.signWith(keys.getPrivate())
.compact();

JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> keys.getPublic());
JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> Mono.just(keys.getPublic()));

StepVerifier.create(sut.authenticate(token))
.assertNext(
Expand Down Expand Up @@ -184,12 +183,8 @@ void authenticateCreateTokenAndValidateWrongKeyForAlgorithmAuthenticationFailsEx
map ->
Optional.ofNullable(map.get("kid"))
.filter(String.class::isInstance)
.map(
s -> {
// Safe to cast to string, use the kid property to fetch the key
return hmacSecretKey;
})
.orElse(null));
.map(s -> Mono.just(hmacSecretKey))
.orElse(Mono.empty()));

StepVerifier.create(sut.authenticate(token))
.expectErrorSatisfies(
Expand All @@ -208,7 +203,7 @@ void authenticateMissingClaimsInTokenAuthenticationSuccessProfilePropertyIsMissi
.signWith(keys.getPrivate())
.compact();

JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> keys.getPublic());
JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> Mono.just(keys.getPublic()));

StepVerifier.create(sut.authenticate(token))
.assertNext(
Expand All @@ -229,12 +224,8 @@ void authenticateUnsignedTokenAuthenticationFailsExceptionThrown() {
map ->
Optional.ofNullable(map.get("kid"))
.filter(String.class::isInstance)
.map(
s -> {
// Safe to cast to string, use the kid property to fetch the key
return hmacSecretKey;
})
.orElse(null));
.map(s -> Mono.just(hmacSecretKey))
.orElse(Mono.empty()));

StepVerifier.create(sut.authenticate(token))
.expectErrorSatisfies(
Expand All @@ -252,7 +243,7 @@ void authenticateKeyResolverReturnNullsAuthenticationFailsExceptionThrown() {
.signWith(keys.getPrivate())
.compact();

JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> null);
JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> Mono.empty());
StepVerifier.create(sut.authenticate(token))
.expectErrorSatisfies(
actualException ->
Expand All @@ -276,7 +267,7 @@ void authenticateAuthenticateExpiredTokenFails() {
.signWith(hmacSecretKey)
.compact();

JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> null);
JwtAuthenticator sut = new DefaultJwtAuthenticator(map -> Mono.empty());
StepVerifier.create(sut.authenticate(token))
.expectErrorSatisfies(
actualException ->
Expand Down
39 changes: 17 additions & 22 deletions jwt/src/test/java/io/scalecube/security/acl/AccessControlTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.scalecube.security.acl;

import static org.junit.jupiter.api.Assertions.assertEquals;

import io.jsonwebtoken.Jwts;
import io.scalecube.security.api.Authenticator;
import io.scalecube.security.jwt.DefaultJwtAuthenticator;
Expand All @@ -10,30 +11,32 @@
import javax.crypto.SecretKey;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

public class AccessControlTest {
class AccessControlTest {

// user permissions
// user permissions
private static final String RESOURCE_READ = "resource/read";
private static final String RESOURCE_CREATE = "resource/create";
private static final String RESOURCE_DELETE = "resource/delete";

// user roles
private static final String OWNER = "owner";
private static final String ADMIN = "admin";
private static final String MEMBER = "member";

private static SecretKey key;
private static DefaultAccessControl accessContorl;
private static DefaultAccessControl accessControl;

@BeforeAll
public static void setUp() throws Exception {
static void setUp() throws Exception {
key = KeyGenerator.getInstance("HmacSHA256").generateKey();

Authenticator authenticator =
new DefaultJwtAuthenticator(m -> "1".equals(m.get("kid")) ? key : null);
new DefaultJwtAuthenticator(m -> "1".equals(m.get("kid")) ? Mono.just(key) : Mono.empty());

accessContorl =
accessControl =
DefaultAccessControl.builder()
.authenticator(authenticator)
.authorizer(
Expand All @@ -46,16 +49,12 @@ public static void setUp() throws Exception {
}

@Test
public void shouldGrantAccess() throws NoSuchAlgorithmException {
void shouldGrantAccess() throws NoSuchAlgorithmException {

String token =
Jwts.builder()
.setHeaderParam("kid", "1")
.claim("roles", OWNER)
.signWith(key)
.compact();
Jwts.builder().setHeaderParam("kid", "1").claim("roles", OWNER).signWith(key).compact();

StepVerifier.create(accessContorl.check(token, RESOURCE_CREATE))
StepVerifier.create(accessControl.check(token, RESOURCE_CREATE))
.assertNext(
profile -> {
assertEquals(profile.claim("roles"), OWNER);
Expand All @@ -64,16 +63,12 @@ public void shouldGrantAccess() throws NoSuchAlgorithmException {
}

@Test
public void shouldDenyAccess() throws NoSuchAlgorithmException {
void shouldDenyAccess() throws NoSuchAlgorithmException {

String token =
Jwts.builder()
.setHeaderParam("kid", "1")
.claim("roles", MEMBER)
.signWith(key)
.compact();
Jwts.builder().setHeaderParam("kid", "1").claim("roles", MEMBER).signWith(key).compact();

StepVerifier.create(accessContorl.check(token, RESOURCE_DELETE))
StepVerifier.create(accessControl.check(token, RESOURCE_DELETE))
.expectError(AccessControlException.class)
.verify();
}
Expand Down