View Javadoc

1   package com.atlassian.asap.core.keys.publickey;
2   
3   import com.atlassian.asap.api.exception.CannotRetrieveKeyException;
4   import com.atlassian.asap.core.exception.PublicKeyNotFoundException;
5   import com.atlassian.asap.core.exception.PublicKeyRetrievalException;
6   import com.atlassian.asap.core.keys.PemReader;
7   import com.atlassian.asap.core.validator.ValidatedKeyId;
8   import org.apache.http.HttpEntity;
9   import org.apache.http.HttpHeaders;
10  import org.apache.http.HttpRequestInterceptor;
11  import org.apache.http.HttpResponse;
12  import org.apache.http.StatusLine;
13  import org.apache.http.client.HttpClient;
14  import org.apache.http.client.methods.HttpGet;
15  import org.apache.http.message.BasicHeader;
16  import org.junit.Before;
17  import org.junit.Test;
18  import org.junit.runner.RunWith;
19  import org.mockito.ArgumentCaptor;
20  import org.mockito.Captor;
21  import org.mockito.Mock;
22  import org.mockito.runners.MockitoJUnitRunner;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.Reader;
28  import java.net.URI;
29  import java.security.PublicKey;
30  import java.security.interfaces.RSAPublicKey;
31  
32  import static org.junit.Assert.assertEquals;
33  import static org.mockito.Matchers.any;
34  import static org.mockito.Mockito.verify;
35  import static org.mockito.Mockito.when;
36  
37  @RunWith(MockitoJUnitRunner.class)
38  public class HttpPublicKeyProviderTest {
39      private static final BasicHeader CONTENT_TYPE_X_PEM_FILE = new BasicHeader("Content-Type", "application/x-pem-file");
40      private static final URI BASE_URL = URI.create("https://example.test/");
41  
42      @Mock
43      private PemReader pemReader;
44  
45      @Mock
46      private HttpClient httpClient;
47  
48      @Mock
49      private HttpResponse response;
50  
51      @Mock
52      private HttpEntity entity;
53  
54      @Mock
55      private StatusLine statusLine;
56  
57      @Mock
58      private InputStream inputStream;
59  
60      @Mock
61      private RSAPublicKey publicKey;
62  
63      @Captor
64      private ArgumentCaptor<HttpGet> httpGetCaptor;
65  
66      private HttpPublicKeyProvider publicKeyProvider;
67  
68      @Before
69      public void setUp() throws Exception {
70          publicKeyProvider = new HttpPublicKeyProvider(BASE_URL, httpClient, pemReader);
71          when(response.getEntity()).thenReturn(entity);
72          when(response.getStatusLine()).thenReturn(statusLine);
73          when(entity.getContent()).thenReturn(inputStream);
74  
75      }
76  
77      @Test(expected = PublicKeyRetrievalException.class)
78      public void shouldFailIfHttpRequestFails() throws Exception {
79          when(httpClient.execute(any(HttpGet.class))).thenThrow(new IOException("Error retrieving public key"));
80  
81          publicKeyProvider.getKey(ValidatedKeyId.validate("some-key-id"));
82      }
83  
84      @Test(expected = PublicKeyRetrievalException.class)
85      public void shouldFailIfKeyRepoReturnsInternalError() throws Exception {
86          when(httpClient.execute(any(HttpGet.class))).thenReturn(response);
87          when(response.getEntity()).thenReturn(null); // override default programming
88          when(statusLine.getStatusCode()).thenReturn(500);
89  
90          publicKeyProvider.getKey(ValidatedKeyId.validate("some-key-id"));
91      }
92  
93      @Test(expected = PublicKeyNotFoundException.class)
94      public void shouldReturnNoneIfPublicKeyIsNotFound() throws Exception {
95          when(httpClient.execute(any(HttpGet.class))).thenReturn(response);
96          when(response.getEntity()).thenReturn(null); // override default programming
97          when(statusLine.getStatusCode()).thenReturn(404);
98  
99          publicKeyProvider.getKey(ValidatedKeyId.validate("some-key-id"));
100     }
101 
102     @Test(expected = CannotRetrieveKeyException.class)
103     public void shouldFailIfPublicKeyDoesNotParse() throws Exception {
104         when(httpClient.execute(any(HttpGet.class))).thenReturn(response);
105         when(statusLine.getStatusCode()).thenReturn(200);
106         when(entity.getContentType()).thenReturn(CONTENT_TYPE_X_PEM_FILE);
107         when(pemReader.readPublicKey(any(Reader.class))).thenThrow(CannotRetrieveKeyException.class);
108 
109         publicKeyProvider.getKey(ValidatedKeyId.validate("some-key-id"));
110     }
111 
112     @Test(expected = CannotRetrieveKeyException.class)
113     public void shouldFailIfContentTypeIsNotPem() throws Exception {
114         when(httpClient.execute(any(HttpGet.class))).thenReturn(response);
115         when(statusLine.getStatusCode()).thenReturn(200);
116         when(entity.getContentType()).thenReturn(new BasicHeader("Content-Type", "application/not-pem"));
117 
118         publicKeyProvider.getKey(ValidatedKeyId.validate("some-key-id"));
119     }
120 
121     @Test
122     public void shouldReturnParsedPublicKey() throws Exception {
123         when(httpClient.execute(any(HttpGet.class))).thenReturn(response);
124         when(statusLine.getStatusCode()).thenReturn(200);
125         when(entity.getContentType()).thenReturn(CONTENT_TYPE_X_PEM_FILE);
126         when(pemReader.readPublicKey(any(InputStreamReader.class))).thenReturn(publicKey);
127 
128         PublicKey pk = publicKeyProvider.getKey(ValidatedKeyId.validate("some-key-id"));
129         assertEquals(publicKey, pk);
130     }
131 
132     @Test
133     public void shouldIncludeAcceptHeaderInTheRequest() throws Exception {
134         when(httpClient.execute(any(HttpGet.class))).thenReturn(response);
135         when(statusLine.getStatusCode()).thenReturn(200);
136         when(entity.getContentType()).thenReturn(CONTENT_TYPE_X_PEM_FILE);
137         when(pemReader.readPublicKey(any(InputStreamReader.class))).thenReturn(publicKey);
138 
139         publicKeyProvider.getKey(ValidatedKeyId.validate("some-key-id"));
140 
141         verify(httpClient).execute(httpGetCaptor.capture());
142         assertEquals(HttpPublicKeyProvider.ACCEPT_HEADER_VALUE,
143                 httpGetCaptor.getValue().getFirstHeader(HttpHeaders.ACCEPT).getValue());
144     }
145 
146     @Test
147     public void shouldComposeTheBaseUrlAndTheKeyId() throws Exception {
148         when(httpClient.execute(any(HttpGet.class))).thenReturn(response);
149         when(statusLine.getStatusCode()).thenReturn(200);
150         when(entity.getContentType()).thenReturn(CONTENT_TYPE_X_PEM_FILE);
151         when(pemReader.readPublicKey(any(InputStreamReader.class))).thenReturn(publicKey);
152 
153         publicKeyProvider.getKey(ValidatedKeyId.validate("some-key-id"));
154 
155         verify(httpClient).execute(httpGetCaptor.capture());
156         assertEquals(URI.create(BASE_URL + "some-key-id"), httpGetCaptor.getValue().getURI());
157     }
158 
159     @Test(expected = IllegalArgumentException.class)
160     public void shouldRejectSchemelessBaseUrl() throws Exception {
161         new HttpPublicKeyProvider(URI.create("///bar/"), httpClient, pemReader);
162     }
163 
164     @Test(expected = IllegalArgumentException.class)
165     public void shouldRejectSchemesOtherThanHttps() throws Exception {
166         new HttpPublicKeyProvider(URI.create("file:///bar/"), httpClient, pemReader);
167     }
168 
169     @Test(expected = IllegalArgumentException.class)
170     public void shouldRejectInsecureHttp() throws Exception {
171         new HttpPublicKeyProvider(URI.create("http://example.test/"), httpClient, pemReader);
172     }
173 
174     @Test(expected = IllegalArgumentException.class)
175     public void shouldRejectBaseUrlWithoutTrailingSlash() throws Exception {
176         new HttpPublicKeyProvider(URI.create("https://example.test/bar"), httpClient, pemReader);
177     }
178 
179     @Test
180     public void shouldAllowExtendingHttpClient() {
181         // This test is a bit silly, but it shows an example use of the API (shrug).
182         HttpRequestInterceptor interceptor = (request, context) -> {
183             // do something here
184         };
185         HttpClient client = HttpPublicKeyProvider.defaultHttpClientBuilder()
186                 .addInterceptorFirst(interceptor)
187                 .build();
188         new HttpPublicKeyProvider(BASE_URL, client, pemReader);
189     }
190 }