migrate sdnr features to phosphorus
[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.jolokia.osgi.security.Authenticator;
45 import org.json.JSONArray;
46 import org.junit.BeforeClass;
47 import org.junit.Test;
48 import org.onap.ccsdk.features.sdnr.wt.common.http.BaseHTTPClient;
49 import org.onap.ccsdk.features.sdnr.wt.common.test.ServletOutputStreamToByteArrayOutputStream;
50 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.Config;
51 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.CustomObjectMapper;
52 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.InvalidConfigurationException;
53 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlPolicy;
54 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload;
55 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.AuthHttpServlet;
56 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.HeadersOnlyHttpServletRequest;
57 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.TokenCreator;
58 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.test.helper.OdlJsonMapper;
59 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.test.helper.OdlXmlMapper;
60 import org.opendaylight.aaa.api.IdMService;
61 import org.apache.shiro.authc.BearerToken;
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             fail(e.getMessage());
97         }
98         servlet.setDataBroker(dataBroker);
99         servlet.setOdlAuthenticator(odlAuthenticator);
100         servlet.setOdlIdentityService(odlIdentityService);
101         servlet.setShiroConfiguration(shiroConfiguration);
102     }
103
104     private static DataBroker loadDynamicMdsalAuthDataBroker() {
105         DataBroker dataBroker = mock(DataBroker.class);
106         ReadTransaction rotx = mock(ReadTransaction.class);
107         InstanceIdentifier<Policies> iif = InstanceIdentifier.create(HttpAuthorization.class).child(Policies.class);
108         try {
109             when(rotx.read(LogicalDatastoreType.CONFIGURATION, iif))
110                     .thenReturn(loadDataBrokerFile(MDSALDYNAUTHFILENAME, Policies.class));
111         } catch (IOException e) {
112             fail("problem init databroker read" + e.getMessage());
113         }
114         when(dataBroker.newReadOnlyTransaction()).thenReturn(rotx);
115         return dataBroker;
116     }
117
118     private static <T> FluentFuture<Optional<T>> loadDataBrokerFile(String fn, Class<T> clazz) throws IOException {
119         return FluentFutures.immediateFluentFuture(Optional.ofNullable(readJson(new File(fn), clazz)));
120     }
121
122     private static ShiroConfiguration loadShiroConfig(String filename)
123             throws JsonParseException, JsonMappingException, IOException {
124         OdlXmlMapper mapper = new OdlXmlMapper();
125         return mapper.readValue(new File(filename), ShiroConfigurationBuilder.class).build();
126     }
127
128     private static Config createConfigFile() throws IOException, InvalidConfigurationException {
129         return Config.getInstance(TESTCONFIGFILE);
130
131     }
132
133     @Test
134     public void testValidLoginRedirect() {
135
136         HttpServletRequest req = mock(HttpServletRequest.class);
137         when(req.getRequestURI()).thenReturn("/oauth/login/keycloak");
138         HttpServletResponse resp = mock(HttpServletResponse.class);
139         try {
140             servlet.doGet(req, resp);
141         } catch (ServletException | IOException e) {
142             fail(e.getMessage());
143         }
144         verify(resp).setStatus(302);
145         verify(resp).setHeader("Location",
146                 "http://10.20.11.160:8080/auth/realms/onap/protocol/openid-connect/auth?client_id=odlux.app&response"
147                         + "_type=code&scope=openid&redirect_uri=http%3A%2F%2Fnasp.diasf.de%2Foauth%2Fredirect%2Fkeycloak");
148     }
149
150     @Test
151     public void testInValidLoginRedirect() {
152
153         HttpServletRequest req = mock(HttpServletRequest.class);
154         when(req.getRequestURI()).thenReturn("/oauth/login/unknownproviderid");
155         HttpServletResponse resp = mock(HttpServletResponse.class);
156         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
157         try {
158             when(resp.getOutputStream()).thenReturn(printOut);
159             servlet.doGet(req, resp);
160         } catch (ServletException | IOException e) {
161             fail(e.getMessage());
162         }
163         verify(resp).setStatus(404);
164     }
165
166     @Test
167     public void testValidLogin() {
168
169         HttpServletRequest req = mock(HttpServletRequest.class);
170         when(req.getRequestURI()).thenReturn("/oauth/login");
171         when(req.getParameter("username")).thenReturn("admin");
172         when(req.getParameter("password")).thenReturn("admin");
173         when(odlAuthenticator.authenticate(any(HeadersOnlyHttpServletRequest.class))).thenReturn(true);
174         HttpServletResponse resp = mock(HttpServletResponse.class);
175         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
176         try {
177             when(resp.getOutputStream()).thenReturn(printOut);
178             servlet.doPost(req, resp);
179         } catch (ServletException | IOException e) {
180             fail(e.getMessage());
181         }
182         verify(resp).setStatus(200);
183     }
184
185     @Test
186     public void testGetProviders() {
187
188         HttpServletRequest req = mock(HttpServletRequest.class);
189         when(req.getRequestURI()).thenReturn("/oauth/providers");
190         HttpServletResponse resp = mock(HttpServletResponse.class);
191         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
192         try {
193             when(resp.getOutputStream()).thenReturn(printOut);
194             servlet.doGet(req, resp);
195         } catch (ServletException | IOException e) {
196             fail(e.getMessage());
197         }
198         verify(resp).setStatus(200);
199         String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
200         System.out.println(responseBody);
201         JSONArray a = new JSONArray(responseBody);
202         assertEquals(1, a.length());
203         assertEquals("keycloak", a.getJSONObject(0).getString("id"));
204         assertEquals("OSNL Keycloak Provider", a.getJSONObject(0).getString("title"));
205         assertEquals("/oauth/login/keycloak", a.getJSONObject(0).getString("loginUrl"));
206
207     }
208
209     @Test
210     public void testPoliciesAnon() {
211
212         HttpServletRequest req = mock(HttpServletRequest.class);
213         when(req.getRequestURI()).thenReturn("/oauth/policies");
214         HttpServletResponse resp = mock(HttpServletResponse.class);
215         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
216         try {
217             when(resp.getOutputStream()).thenReturn(printOut);
218             servlet.doGet(req, resp);
219         } catch (ServletException | IOException e) {
220             fail(e.getMessage());
221         }
222         verify(resp).setStatus(200);
223         String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
224         System.out.println(responseBody);
225         OdlPolicy[] anonPolicies = null;
226         try {
227             anonPolicies = readJson(responseBody, OdlPolicy[].class);
228         } catch (JsonProcessingException e) {
229             fail("unable to read anon policies response");
230         }
231         assertEquals(9, anonPolicies.length);
232         OdlPolicy pApidoc = find(anonPolicies, "/apidoc/**");
233         assertNotNull(pApidoc);
234         assertAllEquals(false, pApidoc);
235         OdlPolicy pOauth = find(anonPolicies, "/oauth/**");
236         assertNotNull(pOauth);
237         assertAllEquals(true, pOauth);
238         OdlPolicy pRestconf = find(anonPolicies, "/rests/**");
239         assertNotNull(pRestconf);
240         assertAllEquals(false, pRestconf);
241     }
242
243     @Test
244     public void testPoliciesBasicAuth() {
245
246         HttpServletRequest req = mock(HttpServletRequest.class);
247         when(req.getRequestURI()).thenReturn("/oauth/policies");
248         when(req.getHeader("Authorization")).thenReturn(BaseHTTPClient.getAuthorizationHeaderValue("admin", "admin"));
249         when(odlIdentityService.listRoles("admin@sdn", "sdn")).thenReturn(Arrays.asList("admin"));
250         HttpServletResponse resp = mock(HttpServletResponse.class);
251         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
252         try {
253             when(resp.getOutputStream()).thenReturn(printOut);
254             servlet.doGet(req, resp);
255         } catch (ServletException | IOException e) {
256             fail(e.getMessage());
257         }
258         verify(resp).setStatus(200);
259         String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
260         System.out.println(responseBody);
261         OdlPolicy[] anonPolicies = null;
262         try {
263             anonPolicies = readJson(responseBody, OdlPolicy[].class);
264         } catch (JsonProcessingException e) {
265             fail("unable to read anon policies response");
266         }
267         assertEquals(9, anonPolicies.length);
268         OdlPolicy pApidoc = find(anonPolicies, "/apidoc/**");
269         assertNotNull(pApidoc);
270         assertAllEquals(true, pApidoc);
271         OdlPolicy pOauth = find(anonPolicies, "/oauth/**");
272         assertNotNull(pOauth);
273         assertAllEquals(true, pOauth);
274         OdlPolicy pRestconf = find(anonPolicies, "/rests/**");
275         assertNotNull(pRestconf);
276         assertAllEquals(true, pRestconf);
277     }
278
279     @Test
280     public void testPoliciesBearer() {
281         HttpServletRequest req = mock(HttpServletRequest.class);
282         when(req.getRequestURI()).thenReturn("/oauth/policies");
283         String token = createToken("admin", Arrays.asList("admin", "provision")).getToken();
284         when(req.getHeader("Authorization")).thenReturn(String.format("Bearer %s", token));
285         HttpServletResponse resp = mock(HttpServletResponse.class);
286         ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
287         try {
288             when(resp.getOutputStream()).thenReturn(printOut);
289             servlet.doGet(req, resp);
290         } catch (ServletException | IOException e) {
291             fail(e.getMessage());
292         }
293         verify(resp).setStatus(200);
294         String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
295         System.out.println(responseBody);
296         OdlPolicy[] anonPolicies = null;
297         try {
298             anonPolicies = readJson(responseBody, OdlPolicy[].class);
299         } catch (JsonProcessingException e) {
300             fail("unable to read anon policies response");
301         }
302         assertEquals(9, anonPolicies.length);
303         OdlPolicy pApidoc = find(anonPolicies, "/apidoc/**");
304         assertNotNull(pApidoc);
305         assertAllEquals(false, pApidoc);
306         OdlPolicy pOauth = find(anonPolicies, "/oauth/**");
307         assertNotNull(pOauth);
308         assertAllEquals(true, pOauth);
309         OdlPolicy pRestconf = find(anonPolicies, "/rests/**");
310         assertNotNull(pRestconf);
311         assertAllEquals(true, pRestconf);
312     }
313
314     private static BearerToken createToken(String username, List<String> roles) {
315         UserTokenPayload data = new UserTokenPayload();
316         data.setPreferredUsername(username);
317         data.setFamilyName("");
318         data.setGivenName(username);
319         data.setExp(tokenCreator.getDefaultExp());
320         data.setRoles(roles);
321         return tokenCreator.createNewJWT(data);
322     }
323
324     private static void assertAllEquals(boolean b, OdlPolicy p) {
325         assertEquals(b, p.getMethods().isGet());
326         assertEquals(b, p.getMethods().isPost());
327         assertEquals(b, p.getMethods().isPut());
328         assertEquals(b, p.getMethods().isDelete());
329         assertEquals(b, p.getMethods().isPatch());
330     }
331
332     private static OdlPolicy find(OdlPolicy[] policies, String path) {
333         for (OdlPolicy p : policies) {
334             if (path.equals(p.getPath())) {
335                 return p;
336             }
337         }
338         return null;
339     }
340
341     private static <T> T readJson(String data, Class<T> clazz) throws JsonMappingException, JsonProcessingException {
342         CustomObjectMapper mapper = new CustomObjectMapper();
343         return mapper.readValue(data, clazz);
344     }
345
346     private static <T> T readJson(File file, Class<T> clazz) throws IOException {
347         OdlJsonMapper mapper = new OdlJsonMapper();
348         return mapper.readValue(file, clazz);
349     }
350
351     private static class TestServlet extends AuthHttpServlet {
352
353         private static final long serialVersionUID = 1L;
354
355         public TestServlet() throws IllegalArgumentException, Exception {
356             super();
357         }
358
359         @Override
360         public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
361             super.doGet(req, resp);
362         }
363
364         @Override
365         public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
366             super.doPost(req, resp);
367         }
368     }
369 }