1 package com.atlassian.asap.core.validator;
2
3 import com.atlassian.asap.api.JwsHeader;
4 import com.atlassian.asap.api.JwtClaims;
5 import com.atlassian.asap.api.SigningAlgorithm;
6 import com.atlassian.asap.core.exception.InvalidClaimException;
7 import com.atlassian.asap.core.exception.JwtParseException;
8 import com.atlassian.asap.core.exception.PublicKeyRetrievalException;
9 import com.atlassian.asap.core.exception.SignatureMismatchException;
10 import com.atlassian.asap.core.keys.KeyProvider;
11 import com.atlassian.asap.core.parser.JwtParser;
12 import com.atlassian.asap.core.parser.VerifiableJwt;
13 import org.junit.Before;
14 import org.junit.Test;
15 import org.junit.runner.RunWith;
16 import org.mockito.Mock;
17 import org.mockito.runners.MockitoJUnitRunner;
18
19 import java.security.PublicKey;
20 import java.util.Collections;
21 import java.util.Optional;
22
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.fail;
25 import static org.mockito.Mockito.doThrow;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28
29 @RunWith(MockitoJUnitRunner.class)
30 public class JwtValidatorImplTest {
31 private static final String ISSUER = "issuer";
32 private static final String SERIALIZED_JWT = "some serialized jwt";
33 private static final String RESOURCE_SERVER_AUDIENCE = "theResourceServerAudience";
34
35 @Mock
36 private KeyProvider<PublicKey> publicKeyProvider;
37 @Mock
38 private JwtParser jwtParser;
39 @Mock
40 private VerifiableJwt jwt;
41 @Mock
42 private JwsHeader jwsHeader;
43 @Mock
44 private JwtClaims jwtClaims;
45 @Mock
46 private PublicKey publicKey;
47 @Mock
48 private JwtClaimsValidator jwtClaimsValidator;
49
50 private JwtValidator jwtValidator;
51
52 @Before
53 public void setUp() throws Exception {
54 when(jwsHeader.getAlgorithm()).thenReturn(SigningAlgorithm.RS256);
55 when(jwt.getHeader()).thenReturn(jwsHeader);
56 when(jwt.getClaims()).thenReturn(jwtClaims);
57 when(jwsHeader.getKeyId()).thenReturn("public-key");
58
59 jwtValidator = new JwtValidatorImpl(
60 publicKeyProvider,
61 jwtParser,
62 jwtClaimsValidator,
63 RESOURCE_SERVER_AUDIENCE
64 );
65 }
66
67 @Test(expected = JwtParseException.class)
68 public void shouldFailIfJwtUnparseable() throws Exception {
69 when(jwtParser.parse(SERIALIZED_JWT)).thenThrow(new JwtParseException("Can't parse jwt"));
70 jwtValidator.readAndValidate(SERIALIZED_JWT);
71 }
72
73 @Test(expected = PublicKeyRetrievalException.class)
74 public void shouldFailIfPublicKeyNotRetrievable() throws Exception {
75 String issuer = "an issuer without a public key";
76 when(jwt.getClaims().getIssuer()).thenReturn(issuer);
77
78 when(jwtParser.parse(SERIALIZED_JWT)).thenReturn(jwt);
79 final ValidatedKeyId validatedKeyId = ValidatedKeyId.validate("public-key");
80 when(publicKeyProvider.getKey(validatedKeyId)).thenThrow(new PublicKeyRetrievalException("Can't retrieve public key", validatedKeyId, null));
81
82 jwtValidator.readAndValidate(SERIALIZED_JWT);
83 }
84
85 @Test
86 public void shouldFailIfJwtNotVerified() throws Exception {
87 String issuer = "issuer";
88
89 when(jwt.getClaims().getIssuer()).thenReturn(issuer);
90 when(publicKeyProvider.getKey(ValidatedKeyId.validate("public-key"))).thenReturn(publicKey);
91 when(jwtParser.parse(SERIALIZED_JWT)).thenReturn(jwt);
92 doThrow(new SignatureMismatchException("Couldn't verifySignature jwt")).when(jwt).verifySignature(publicKey);
93
94 try {
95 jwtValidator.readAndValidate(SERIALIZED_JWT);
96 fail("Should have thrown");
97 } catch (SignatureMismatchException ex) {
98 verify(jwt).verifySignature(publicKey);
99 }
100 }
101
102 @Test(expected = InvalidClaimException.class)
103 public void shouldFailIfClaimsMismatch() throws Exception {
104 String issuer = "issuer";
105
106 when(jwt.getClaims().getIssuer()).thenReturn(issuer);
107 when(publicKeyProvider.getKey(ValidatedKeyId.validate("public-key"))).thenReturn(publicKey);
108 when(jwtParser.parse(SERIALIZED_JWT)).thenReturn(jwt);
109 doThrow(new InvalidClaimException(JwtClaims.RegisteredClaim.JWT_ID, "Bad Claim"))
110 .when(jwtClaimsValidator).validate(jwt, Collections.singleton(RESOURCE_SERVER_AUDIENCE));
111
112 jwtValidator.readAndValidate(SERIALIZED_JWT);
113 }
114
115 @Test
116 public void shouldReturnTrueIfVerificationSucceeds() throws Exception {
117 String issuer = "an issuer";
118
119 when(jwt.getClaims().getIssuer()).thenReturn(issuer);
120 when(publicKeyProvider.getKey(ValidatedKeyId.validate(jwsHeader.getKeyId()))).thenReturn(publicKey);
121 when(jwtParser.parse(SERIALIZED_JWT)).thenReturn(jwt);
122
123 assertEquals(jwt, jwtValidator.readAndValidate(SERIALIZED_JWT));
124 verify(jwt).verifySignature(publicKey);
125 verify(jwtClaimsValidator).validate(jwt, Collections.singleton(RESOURCE_SERVER_AUDIENCE));
126 }
127
128 @Test
129 public void shouldDelegateDetermineIssuerToParser() {
130 when(jwtParser.determineUnverifiedIssuer(SERIALIZED_JWT)).thenReturn(Optional.of(ISSUER));
131
132 Optional<String> issuer = jwtValidator.determineUnverifiedIssuer(SERIALIZED_JWT);
133
134 assertEquals(ISSUER, issuer.get());
135 }
136 }