ab6dc4ec2431a8179b0f53622d6b9bd39046fb21
[ccsdk/features.git] / sdnr / wt / oauth-provider / provider-jar / src / test / java / org / onap / ccsdk / features / sdnr / wt / oauthprovider / test / TestAuthHttpServlet.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.oauthprovider.test;
23
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.fail;
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31 import com.fasterxml.jackson.core.JsonParseException;
32 import com.fasterxml.jackson.core.JsonProcessingException;
33 import com.fasterxml.jackson.databind.JsonMappingException;
34 import com.google.common.util.concurrent.FluentFuture;
35 import java.io.File;
36 import java.io.IOException;
37 import java.nio.charset.StandardCharsets;
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.Optional;
41 import javax.servlet.ServletException;
42 import javax.servlet.http.HttpServletRequest;
43 import javax.servlet.http.HttpServletResponse;
44 import org.apache.shiro.authc.BearerToken;
45 import org.jolokia.osgi.security.Authenticator;
46 import org.json.JSONArray;
47 import org.junit.BeforeClass;
48 import org.junit.Test;
49 import org.onap.ccsdk.features.sdnr.wt.common.http.BaseHTTPClient;
50 import org.onap.ccsdk.features.sdnr.wt.common.test.ServletOutputStreamToByteArrayOutputStream;
51 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.Config;
52 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.CustomObjectMapper;
53 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.InvalidConfigurationException;
54 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlPolicy;
55 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload;
56 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.AuthHttpServlet;
57 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.HeadersOnlyHttpServletRequest;
58 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.TokenCreator;
59 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.test.helper.OdlJsonMapper;
60 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.test.helper.OdlXmlMapper;
61 import org.opendaylight.aaa.api.IdMService;
62 import org.opendaylight.mdsal.binding.api.DataBroker;
63 import org.opendaylight.mdsal.binding.api.ReadTransaction;
64 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.ShiroConfiguration;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.ShiroConfigurationBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.HttpAuthorization;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.Policies;
69 import org.opendaylight.yangtools.util.concurrent.FluentFutures;
70 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
71
72
73 public class TestAuthHttpServlet {
74
75     private static final String TESTCONFIGFILE = TestConfig.TEST_CONFIG_FILENAME;
76     private static final String TESTSHIROCONFIGFILE = "src/test/resources/aaa-app-config.test.xml";
77     private static final String MDSALDYNAUTHFILENAME = "src/test/resources/mdsalDynAuthData.json";
78     private static TestServlet servlet;
79     private static DataBroker dataBroker = loadDynamicMdsalAuthDataBroker();
80     private static Authenticator odlAuthenticator = mock(Authenticator.class);
81     private static IdMService odlIdentityService = mock(IdMService.class);
82     private static ShiroConfiguration shiroConfiguration = null;
83     private static TokenCreator tokenCreator;
84 //    private static final HttpServletRequest authreq = new HeadersOnlyHttpServletRequest(
85 //            Map.of("Authorization", BaseHTTPClient.getAuthorizationHeaderValue("admin@sdn", "admin")));
86
87     @BeforeClass
88     public static void init() throws IllegalArgumentException, Exception {
89
90         try {
91             Config config = createConfigFile();
92             tokenCreator = TokenCreator.getInstance(config);
93             servlet = new TestServlet();
94             shiroConfiguration = loadShiroConfig(TESTSHIROCONFIGFILE);
95         } catch (IOException | InvalidConfigurationException e) {
96             e.printStackTrace();
97             fail(e.getMessage());
98         }
99         servlet.setDataBroker(dataBroker);
100         servlet.setOdlAuthenticator(odlAuthenticator);
101         servlet.setOdlIdentityService(odlIdentityService);
102         servlet.setShiroConfiguration(shiroConfiguration);
103     }
104
105     private static DataBroker loadDynamicMdsalAuthDataBroker() {
106         DataBroker dataBroker = mock(DataBroker.class);
107         ReadTransaction rotx = mock(ReadTransaction.class);
108         InstanceIdentifier<Policies> iif = InstanceIdentifier.create(HttpAuthorization.class).child(Policies.class);
109         try {
110             when(rotx.read(LogicalDatastoreType.CONFIGURATION, iif))
111                     .thenReturn(loadDataBrokerFile(MDSALDYNAUTHFILENAME, Policies.class));
112         } catch (IOException e) {
113             fail("problem init databroker read" + e.getMessage());
114         }
115         when(dataBroker.newReadOnlyTransaction()).thenReturn(rotx);
116         return dataBroker;
117     }
118
119     private static <T> FluentFuture<Optional<T>> loadDataBrokerFile(String fn, Class<T> clazz) throws IOException {
120         return FluentFutures.immediateFluentFuture(Optional.ofNullable(readJson(new File(fn), clazz)));
121     }
122
123     private static ShiroConfiguration loadShiroConfig(String filename)
124             throws JsonParseException, JsonMappingException, IOException {
125         OdlXmlMapper mapper = new OdlXmlMapper();
126         return mapper.readValue(new File(filename), ShiroConfigurationBuilder.class).build();
127     }
128
129     private static Config createConfigFile() throws IOException, InvalidConfigurationException {
130         return Config.getInstance(TESTCONFIGFILE);
131
132     }
133
134     @Test
135     public void testValidLoginRedirect() {
136
137         HttpServletRequest req = mock(HttpServletRequest.class);
138         when(req.getRequestURI()).thenReturn("/oauth/login/keycloak");
139         HttpServletResponse resp = mock(HttpServletResponse.class);
140         try {
141             servlet.doGet(req, resp);
142         } catch (ServletException | IOException e) {
143             fail(e.getMessage());
144         }
145         verify(resp).setStatus(302);
146         verify(resp).setHeader("Location",
147                 "http://10.20.11.160:8080/auth/realms/onap/protocol/openid-connect/auth?client_id=odlux.app&response"
148                         + "_type=code&scope=openid&redirect_uri=http%3A%2F%2Fnasp.diasf.de%2Foauth%2Fredirect%2Fkeycloak");
149     }
150
151     @Test
152     public void testInValidLoginRedirect() {
153
154         HttpServletRequest req = mock(HttpServletRequest.class);
155         when(req.getRequestURI()).thenReturn("/oauth/login/unknownproviderid");
156         HttpServletResponse resp = mock(HttpServletResponse.class);
157         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
158         try {
159             when(resp.getOutputStream()).thenReturn(printOut);
160             servlet.doGet(req, resp);
161         } catch (ServletException | IOException e) {
162             fail(e.getMessage());
163         }
164         verify(resp).setStatus(404);
165     }
166
167     @Test
168     public void testValidLogin() {
169
170         HttpServletRequest req = mock(HttpServletRequest.class);
171         when(req.getRequestURI()).thenReturn("/oauth/login");
172         when(req.getParameter("username")).thenReturn("admin");
173         when(req.getParameter("password")).thenReturn("admin");
174         when(odlAuthenticator.authenticate(any(HeadersOnlyHttpServletRequest.class))).thenReturn(true);
175         HttpServletResponse resp = mock(HttpServletResponse.class);
176         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
177         try {
178             when(resp.getOutputStream()).thenReturn(printOut);
179             servlet.doPost(req, resp);
180         } catch (ServletException | IOException e) {
181             fail(e.getMessage());
182         }
183         verify(resp).setStatus(200);
184     }
185
186     @Test
187     public void testGetProviders() {
188
189         HttpServletRequest req = mock(HttpServletRequest.class);
190         when(req.getRequestURI()).thenReturn("/oauth/providers");
191         HttpServletResponse resp = mock(HttpServletResponse.class);
192         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
193         try {
194             when(resp.getOutputStream()).thenReturn(printOut);
195             servlet.doGet(req, resp);
196         } catch (ServletException | IOException e) {
197             fail(e.getMessage());
198         }
199         verify(resp).setStatus(200);
200         String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
201         System.out.println(responseBody);
202         JSONArray a = new JSONArray(responseBody);
203         assertEquals(1, a.length());
204         assertEquals("keycloak", a.getJSONObject(0).getString("id"));
205         assertEquals("OSNL Keycloak Provider", a.getJSONObject(0).getString("title"));
206         assertEquals("/oauth/login/keycloak", a.getJSONObject(0).getString("loginUrl"));
207
208     }
209
210     @Test
211     public void testPoliciesAnon() {
212
213         HttpServletRequest req = mock(HttpServletRequest.class);
214         when(req.getRequestURI()).thenReturn("/oauth/policies");
215         HttpServletResponse resp = mock(HttpServletResponse.class);
216         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
217         try {
218             when(resp.getOutputStream()).thenReturn(printOut);
219             servlet.doGet(req, resp);
220         } catch (ServletException | IOException e) {
221             fail(e.getMessage());
222         }
223         verify(resp).setStatus(200);
224         String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
225         System.out.println(responseBody);
226         OdlPolicy[] anonPolicies = null;
227         try {
228             anonPolicies = readJson(responseBody, OdlPolicy[].class);
229         } catch (JsonProcessingException e) {
230             fail("unable to read anon policies response");
231         }
232         assertEquals(9, anonPolicies.length);
233         OdlPolicy pApidoc = find(anonPolicies, "/apidoc/**");
234         assertNotNull(pApidoc);
235         assertAllEquals(false, pApidoc);
236         OdlPolicy pOauth = find(anonPolicies, "/oauth/**");
237         assertNotNull(pOauth);
238         assertAllEquals(true, pOauth);
239         OdlPolicy pRestconf = find(anonPolicies, "/rests/**");
240         assertNotNull(pRestconf);
241         assertAllEquals(false, pRestconf);
242     }
243
244     @Test
245     public void testPoliciesBasicAuth() {
246
247         HttpServletRequest req = mock(HttpServletRequest.class);
248         when(req.getRequestURI()).thenReturn("/oauth/policies");
249         when(req.getHeader("Authorization")).thenReturn(BaseHTTPClient.getAuthorizationHeaderValue("admin", "admin"));
250         when(odlIdentityService.listRoles("admin@sdn", "sdn")).thenReturn(Arrays.asList("admin"));
251         HttpServletResponse resp = mock(HttpServletResponse.class);
252         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
253         try {
254             when(resp.getOutputStream()).thenReturn(printOut);
255             servlet.doGet(req, resp);
256         } catch (ServletException | IOException e) {
257             fail(e.getMessage());
258         }
259         verify(resp).setStatus(200);
260         String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
261         System.out.println(responseBody);
262         OdlPolicy[] anonPolicies = null;
263         try {
264             anonPolicies = readJson(responseBody, OdlPolicy[].class);
265         } catch (JsonProcessingException e) {
266             fail("unable to read anon policies response");
267         }
268         assertEquals(9, anonPolicies.length);
269         OdlPolicy pApidoc = find(anonPolicies, "/apidoc/**");
270         assertNotNull(pApidoc);
271         assertAllEquals(true, pApidoc);
272         OdlPolicy pOauth = find(anonPolicies, "/oauth/**");
273         assertNotNull(pOauth);
274         assertAllEquals(true, pOauth);
275         OdlPolicy pRestconf = find(anonPolicies, "/rests/**");
276         assertNotNull(pRestconf);
277         assertAllEquals(true, pRestconf);
278     }
279
280     @Test
281     public void testPoliciesBearer() {
282         HttpServletRequest req = mock(HttpServletRequest.class);
283         when(req.getRequestURI()).thenReturn("/oauth/policies");
284         String token = createToken("admin", Arrays.asList("admin", "provision")).getToken();
285         when(req.getHeader("Authorization")).thenReturn(String.format("Bearer %s", token));
286         HttpServletResponse resp = mock(HttpServletResponse.class);
287         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
288         try {
289             when(resp.getOutputStream()).thenReturn(printOut);
290             servlet.doGet(req, resp);
291         } catch (ServletException | IOException e) {
292             fail(e.getMessage());
293         }
294         verify(resp).setStatus(200);
295         String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
296         System.out.println(responseBody);
297         OdlPolicy[] anonPolicies = null;
298         try {
299             anonPolicies = readJson(responseBody, OdlPolicy[].class);
300         } catch (JsonProcessingException e) {
301             fail("unable to read anon policies response");
302         }
303         assertEquals(9, anonPolicies.length);
304         OdlPolicy pApidoc = find(anonPolicies, "/apidoc/**");
305         assertNotNull(pApidoc);
306         assertAllEquals(false, pApidoc);
307         OdlPolicy pOauth = find(anonPolicies, "/oauth/**");
308         assertNotNull(pOauth);
309         assertAllEquals(true, pOauth);
310         OdlPolicy pRestconf = find(anonPolicies, "/rests/**");
311         assertNotNull(pRestconf);
312         assertAllEquals(true, pRestconf);
313     }
314
315     private static BearerToken createToken(String username, List<String> roles) {
316         UserTokenPayload data = new UserTokenPayload();
317         data.setPreferredUsername(username);
318         data.setFamilyName("");
319         data.setGivenName(username);
320         data.setExp(tokenCreator.getDefaultExp());
321         data.setRoles(roles);
322         return tokenCreator.createNewJWT(data);
323     }
324
325     private static void assertAllEquals(boolean b, OdlPolicy p) {
326         assertEquals(b, p.getMethods().isGet());
327         assertEquals(b, p.getMethods().isPost());
328         assertEquals(b, p.getMethods().isPut());
329         assertEquals(b, p.getMethods().isDelete());
330         assertEquals(b, p.getMethods().isPatch());
331     }
332
333     private static OdlPolicy find(OdlPolicy[] policies, String path) {
334         for (OdlPolicy p : policies) {
335             if (path.equals(p.getPath())) {
336                 return p;
337             }
338         }
339         return null;
340     }
341
342     private static <T> T readJson(String data, Class<T> clazz) throws JsonMappingException, JsonProcessingException {
343         CustomObjectMapper mapper = new CustomObjectMapper();
344         return mapper.readValue(data, clazz);
345     }
346
347     private static <T> T readJson(File file, Class<T> clazz) throws IOException {
348         OdlJsonMapper mapper = new OdlJsonMapper();
349         return mapper.readValue(file, clazz);
350     }
351
352     private static class TestServlet extends AuthHttpServlet {
353
354         private static final long serialVersionUID = 1L;
355
356         public TestServlet() throws IllegalArgumentException, Exception {
357             super();
358         }
359
360         @Override
361         public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
362             super.doGet(req, resp);
363         }
364
365         @Override
366         public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
367             super.doPost(req, resp);
368         }
369     }
370 }