1 package com.atlassian.asap.core.server.http;
2
3 import com.atlassian.asap.api.JwsHeader;
4 import com.atlassian.asap.api.Jwt;
5 import com.atlassian.asap.api.JwtClaims;
6 import com.atlassian.asap.api.exception.CannotRetrieveKeyException;
7 import com.atlassian.asap.api.exception.PermanentAuthenticationFailedException;
8 import com.atlassian.asap.api.exception.TransientAuthenticationFailedException;
9 import com.atlassian.asap.api.server.http.RequestAuthenticator;
10 import com.atlassian.asap.core.exception.InvalidClaimException;
11 import com.atlassian.asap.core.exception.MissingRequiredClaimException;
12 import com.atlassian.asap.core.exception.MissingRequiredHeaderException;
13 import com.atlassian.asap.core.exception.SignatureMismatchException;
14 import com.atlassian.asap.core.serializer.JwtSerializer;
15 import com.atlassian.asap.core.validator.JwtValidator;
16 import org.junit.Before;
17 import org.junit.Rule;
18 import org.junit.Test;
19 import org.junit.rules.ExpectedException;
20 import org.junit.runner.RunWith;
21 import org.mockito.Mock;
22 import org.mockito.runners.MockitoJUnitRunner;
23
24 import java.util.Optional;
25
26 import static org.junit.Assert.assertEquals;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.verifyZeroInteractions;
29 import static org.mockito.Mockito.when;
30
31 @RunWith(MockitoJUnitRunner.class)
32 public class RequestAuthenticatorImplTest {
33 private static final String SERIALIZED_JWT = "my.jwt.token";
34
35 @Rule
36 public final ExpectedException expectedException = ExpectedException.none();
37
38 @Mock
39 private JwtValidator jwtValidator;
40 @Mock
41 private JwtSerializer jwtSerializer;
42 @Mock
43 private Jwt jwt;
44
45 private RequestAuthenticator requestAuthenticator;
46
47 @Before
48 public void setUp() throws Exception {
49 requestAuthenticator = new RequestAuthenticatorImpl(jwtValidator);
50 }
51
52 @Test(expected = PermanentAuthenticationFailedException.class)
53 public void shouldFailIfHeaderNotInRightFormat() throws Exception {
54 requestAuthenticator.authenticateRequest("a bad authorisation header");
55 verifyZeroInteractions(jwtValidator);
56 }
57
58 @Test
59 public void shouldFailIfValidationFails() throws Exception {
60 when(jwtValidator.readAndValidate(SERIALIZED_JWT)).thenThrow(new SignatureMismatchException("Couldn't validate token"));
61 when(jwtValidator.determineUnverifiedIssuer(SERIALIZED_JWT)).thenReturn(Optional.of("some-service"));
62
63 expectedException.expect(PermanentAuthenticationFailedException.class);
64 expectedException.expectMessage("Failed to authenticate request from some-service (issuer not verified): SignatureMismatchException");
65
66 requestAuthenticator.authenticateRequest("Bearer " + SERIALIZED_JWT);
67 }
68
69 @Test
70 public void shouldIncludeFailingClaims() throws Exception {
71 when(jwtValidator.readAndValidate(SERIALIZED_JWT)).thenThrow(new InvalidClaimException(JwtClaims.RegisteredClaim.EXPIRY, "Token is expired!"));
72 when(jwtValidator.determineUnverifiedIssuer(SERIALIZED_JWT)).thenReturn(Optional.of("some-service"));
73
74 expectedException.expect(PermanentAuthenticationFailedException.class);
75 expectedException.expectMessage("Failed to authenticate request from some-service (issuer not verified): InvalidClaimException - EXPIRY");
76
77 requestAuthenticator.authenticateRequest("Bearer " + SERIALIZED_JWT);
78 }
79
80 @Test
81 public void shouldIncludeMissingClaim() throws Exception {
82 when(jwtValidator.readAndValidate(SERIALIZED_JWT)).thenThrow(new MissingRequiredClaimException(JwtClaims.RegisteredClaim.EXPIRY));
83 when(jwtValidator.determineUnverifiedIssuer(SERIALIZED_JWT)).thenReturn(Optional.empty());
84
85 expectedException.expect(PermanentAuthenticationFailedException.class);
86 expectedException.expectMessage("Failed to authenticate request from unknown issuer: MissingRequiredClaimException - EXPIRY");
87
88 requestAuthenticator.authenticateRequest("Bearer " + SERIALIZED_JWT);
89 }
90
91 @Test
92 public void shouldIncludeMissingHeader() throws Exception {
93 when(jwtValidator.readAndValidate(SERIALIZED_JWT)).thenThrow(new MissingRequiredHeaderException(JwsHeader.Header.KEY_ID));
94 when(jwtValidator.determineUnverifiedIssuer(SERIALIZED_JWT)).thenReturn(Optional.empty());
95
96 expectedException.expect(PermanentAuthenticationFailedException.class);
97 expectedException.expectMessage("Failed to authenticate request from unknown issuer: MissingRequiredHeaderException - KEY_ID");
98
99 requestAuthenticator.authenticateRequest("Bearer " + SERIALIZED_JWT);
100 }
101
102 @Test
103 public void shouldFailWithTransientErrorIfCannotRetrieveKey() throws Exception {
104 when(jwtValidator.readAndValidate(SERIALIZED_JWT)).thenThrow(new CannotRetrieveKeyException("500 error from key server"));
105 when(jwtValidator.determineUnverifiedIssuer(SERIALIZED_JWT)).thenReturn(Optional.empty());
106
107 expectedException.expect(TransientAuthenticationFailedException.class);
108 expectedException.expectMessage("Failed to retrieve the key required to authenticate request from unknown issuer: 500 error from key server");
109
110 requestAuthenticator.authenticateRequest("Bearer " + SERIALIZED_JWT);
111 }
112
113 @Test
114 public void shouldReturnJwtIfSuccessful() throws Exception {
115 when(jwtValidator.readAndValidate(SERIALIZED_JWT)).thenReturn(jwt);
116
117 Jwt validatedToken = requestAuthenticator.authenticateRequest("Bearer " + SERIALIZED_JWT);
118
119 assertEquals(jwt, validatedToken);
120
121 verify(jwtValidator).readAndValidate(SERIALIZED_JWT);
122 }
123 }