View Javadoc

1   package com.atlassian.asap.it;
2   
3   import com.atlassian.asap.api.Jwt;
4   import com.atlassian.asap.api.JwtBuilder;
5   import com.atlassian.asap.api.SigningAlgorithm;
6   import com.atlassian.asap.api.client.http.AuthorizationHeaderGenerator;
7   import com.atlassian.asap.core.client.http.AuthorizationHeaderGeneratorImpl;
8   import com.atlassian.asap.core.keys.KeyProvider;
9   import com.atlassian.asap.core.keys.PemReader;
10  import com.atlassian.asap.core.keys.publickey.ClasspathPublicKeyProvider;
11  import org.apache.http.HttpHeaders;
12  import org.apache.http.HttpResponse;
13  import org.apache.http.HttpStatus;
14  import org.apache.http.client.HttpClient;
15  import org.apache.http.client.methods.HttpGet;
16  import org.apache.http.client.utils.HttpClientUtils;
17  import org.apache.http.impl.client.HttpClients;
18  import org.junit.After;
19  import org.junit.Test;
20  
21  import javax.annotation.Nullable;
22  import java.io.IOException;
23  import java.net.URI;
24  import java.security.PublicKey;
25  import java.util.Optional;
26  
27  import static org.hamcrest.Matchers.is;
28  import static org.junit.Assert.assertThat;
29  
30  /**
31   * Performs integration testing by making HTTP requests.
32   * Subclasses of this implement the server start/stop and inherit the tests.
33   */
34  public abstract class BaseIntegrationTest {
35      protected static final String AUDIENCE = "test-resource-server";
36  
37      protected static final String ISSUER1 = "issuer1";
38      protected static final String ISSUER2 = "issuer2";
39      protected static final String ISSUER1_RSA_KEY_ID = "issuer1/rsa-key-for-tests";
40      protected static final String ISSUER1_EC_KEY_ID = "issuer1/es256-key-for-tests";
41      protected static final String ISSUER1_ONLY_PRIV_KEY_RSA_KEY_ID = "issuer1/only-private-key-for-tests";
42      protected static final String ISSUER2_RSA_KEY_ID = "issuer2/rsa-key-for-tests";
43      protected static final KeyProvider<PublicKey> PUBLIC_KEY_PROVIDER =
44              new ClasspathPublicKeyProvider("/publickeyrepo/", new PemReader());
45  
46      protected static final String RESOURCE = "resource";
47  
48      protected static final String UNAUTHORIZED_SUBJECT = "unauthorized-subject";
49  
50      private HttpClient httpClient = HttpClients.createDefault();
51  
52      protected abstract URI getUrlForResourceName(String resourceName);
53  
54      @After
55      public void shutdownHttpClient() {
56          HttpClientUtils.closeQuietly(httpClient);
57      }
58  
59      private String generateAuthorizationHeader(Jwt jwt) throws Exception {
60          AuthorizationHeaderGenerator authorizationHeaderGenerator =
61                  AuthorizationHeaderGeneratorImpl.createDefault(URI.create("classpath:///privatekeys/"));
62  
63          return authorizationHeaderGenerator.generateAuthorizationHeader(jwt);
64      }
65  
66      private HttpResponse executeRequestWithJwt(String resourceName, Jwt jwt) throws Exception {
67          String authorizationHeader = generateAuthorizationHeader(jwt);
68          return executeRequestWithAuthorization(resourceName, authorizationHeader);
69      }
70  
71      private HttpResponse executeRequestWithAuthorization(String resourceName,
72                                                           @Nullable String authorizationHeader) throws IOException {
73          HttpGet getRequest = new HttpGet(getUrlForResourceName(resourceName));
74          getRequest.setHeader(HttpHeaders.AUTHORIZATION, authorizationHeader);
75          return httpClient.execute(getRequest);
76      }
77  
78      /**
79       * Verifies that the server rejects requests that lack the authentication header.
80       *
81       * @throws Exception if the test fails to execute
82       */
83      @Test
84      public void shouldRejectRequestWithoutAuthenticationHeader() throws Exception {
85          HttpResponse response = executeRequestWithAuthorization(RESOURCE, null);
86  
87          assertUnauthorized(response);
88      }
89  
90      /**
91       * Verifies that the server rejects requests that do not have a bearer token.
92       *
93       * @throws Exception if the test fails to execute
94       */
95      @Test
96      public void shouldRejectRequestWithBasicAuth() throws Exception {
97          HttpResponse response = executeRequestWithAuthorization(RESOURCE, "Basic foobar");
98  
99          assertUnauthorized(response);
100     }
101 
102     /**
103      * Verifies that the server rejects requests which have an uparseable token.
104      *
105      * @throws Exception if the test fails to execute
106      */
107     @Test
108     public void shouldRejectRequestWithMalformedToken() throws Exception {
109         HttpResponse response = executeRequestWithAuthorization(RESOURCE, "Bearer this-is-not-jwt");
110 
111         assertUnauthorized(response);
112     }
113 
114     /**
115      * Verifies that the server accepts requests with a valid token using RS256.
116      *
117      * @throws Exception if the test fails to execute
118      */
119     @Test
120     public void shouldAcceptRequestWithValidRS256Token() throws Exception {
121         Jwt jwt = JwtBuilder.newJwt()
122                 .keyId(ISSUER1_RSA_KEY_ID)
123                 .issuer(ISSUER1)
124                 .audience(AUDIENCE)
125                 .build();
126 
127         HttpResponse response = executeRequestWithJwt(RESOURCE, jwt);
128 
129         assertOk(response);
130     }
131 
132     /**
133      * Verifies that the server accepts requests with a valid token using PS256.
134      *
135      * @throws Exception if the test fails to execute
136      */
137     @Test
138     public void shouldAcceptRequestWithValidPS256Token() throws Exception {
139         Jwt jwt = JwtBuilder.newJwt()
140                 .algorithm(SigningAlgorithm.PS256)
141                 .keyId(ISSUER1_RSA_KEY_ID)
142                 .issuer(ISSUER1)
143                 .audience(AUDIENCE)
144                 .build();
145 
146         HttpResponse response = executeRequestWithJwt(RESOURCE, jwt);
147 
148         assertOk(response);
149     }
150 
151     /**
152      * Verifies that the server accepts requests with a valid token using ES256.
153      *
154      * @throws Exception if the test fails to execute
155      */
156     @Test
157     public void shouldAcceptRequestWithValidES256Token() throws Exception {
158         Jwt jwt = JwtBuilder.newJwt()
159                 .algorithm(SigningAlgorithm.ES256)
160                 .keyId(ISSUER1_EC_KEY_ID)
161                 .issuer(ISSUER1)
162                 .audience(AUDIENCE)
163                 .build();
164 
165         HttpResponse response = executeRequestWithJwt(RESOURCE, jwt);
166 
167         assertOk(response);
168     }
169 
170     /**
171      * Verifies that the server rejects requests if the subject is not authorized.
172      *
173      * @throws Exception if the test fails to execute
174      */
175     @Test
176     public void shouldRejectRequestWithUnauthorizedSubject() throws Exception {
177         Jwt jwt = JwtBuilder.newJwt()
178                 .keyId(ISSUER1_RSA_KEY_ID)
179                 .issuer(ISSUER1)  // issuer is authorized, but subject is not
180                 .audience(AUDIENCE)
181                 .subject(Optional.of(UNAUTHORIZED_SUBJECT))
182                 .build();
183 
184         HttpResponse response = executeRequestWithJwt(RESOURCE, jwt);
185 
186         assertForbidden(response);
187     }
188 
189     /**
190      * Verifies that the server rejects requests if the public key cannot be found.
191      *
192      * @throws Exception if the test fails to execute
193      */
194     @Test
195     public void shouldRejectRequestIfPublicKeyCannotBeFound() throws Exception {
196         Jwt jwt = JwtBuilder.newJwt()
197                 .keyId(ISSUER1_ONLY_PRIV_KEY_RSA_KEY_ID)
198                 .issuer(ISSUER1)
199                 .audience(AUDIENCE)
200                 .build();
201 
202         HttpResponse response = executeRequestWithJwt(RESOURCE, jwt);
203 
204         assertUnauthorized(response);
205     }
206 
207     /**
208      * Verifies that the server rejects requests if the issuer is not authorized.
209      *
210      * @throws Exception if the test fails to execute
211      */
212     @Test
213     public void shouldRejectRequestWithUnauthorizedIssuer() throws Exception {
214         Jwt jwt = JwtBuilder.newJwt()
215                 .keyId(ISSUER2_RSA_KEY_ID)
216                 .issuer(ISSUER2)
217                 .audience(AUDIENCE)
218                 .subject(Optional.of(ISSUER1)) // subject is authorized, but issuer is not
219                 .build();
220 
221         HttpResponse response = executeRequestWithJwt(RESOURCE, jwt);
222 
223         assertForbidden(response);
224     }
225 
226     private static void assertOk(HttpResponse response) {
227         assertThat(response.getStatusLine().getStatusCode(), is(HttpStatus.SC_OK));
228     }
229 
230     private static void assertUnauthorized(HttpResponse response) {
231         assertThat(response.getStatusLine().getStatusCode(), is(HttpStatus.SC_UNAUTHORIZED));
232     }
233 
234     private static void assertForbidden(HttpResponse response) {
235         assertThat(response.getStatusLine().getStatusCode(), is(HttpStatus.SC_FORBIDDEN));
236     }
237 }