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