2 * ============LICENSE_START=======================================================
3 * ONAP : ccsdk features
4 * ================================================================================
5 * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 package org.onap.ccsdk.features.sdnr.wt.oauthprovider.test;
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;
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;
73 public class TestAuthHttpServlet {
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")));
88 public static void init() throws IllegalArgumentException, Exception {
91 Config config = createConfigFile();
92 tokenCreator = TokenCreator.getInstance(config);
93 servlet = new TestServlet();
94 shiroConfiguration = loadShiroConfig(TESTSHIROCONFIGFILE);
95 } catch (IOException | InvalidConfigurationException e) {
99 servlet.setDataBroker(dataBroker);
100 servlet.setOdlAuthenticator(odlAuthenticator);
101 servlet.setOdlIdentityService(odlIdentityService);
102 servlet.setShiroConfiguration(shiroConfiguration);
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);
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());
115 when(dataBroker.newReadOnlyTransaction()).thenReturn(rotx);
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)));
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();
129 private static Config createConfigFile() throws IOException, InvalidConfigurationException {
130 return Config.getInstance(TESTCONFIGFILE);
135 public void testValidLoginRedirect() {
137 HttpServletRequest req = mock(HttpServletRequest.class);
138 when(req.getRequestURI()).thenReturn("/oauth/login/keycloak");
139 HttpServletResponse resp = mock(HttpServletResponse.class);
141 servlet.doGet(req, resp);
142 } catch (ServletException | IOException e) {
143 fail(e.getMessage());
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");
152 public void testInValidLoginRedirect() {
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();
159 when(resp.getOutputStream()).thenReturn(printOut);
160 servlet.doGet(req, resp);
161 } catch (ServletException | IOException e) {
162 fail(e.getMessage());
164 verify(resp).setStatus(404);
168 public void testValidLogin() {
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();
178 when(resp.getOutputStream()).thenReturn(printOut);
179 servlet.doPost(req, resp);
180 } catch (ServletException | IOException e) {
181 fail(e.getMessage());
183 verify(resp).setStatus(200);
187 public void testGetProviders() {
189 HttpServletRequest req = mock(HttpServletRequest.class);
190 when(req.getRequestURI()).thenReturn("/oauth/providers");
191 HttpServletResponse resp = mock(HttpServletResponse.class);
192 ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
194 when(resp.getOutputStream()).thenReturn(printOut);
195 servlet.doGet(req, resp);
196 } catch (ServletException | IOException e) {
197 fail(e.getMessage());
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"));
211 public void testPoliciesAnon() {
213 HttpServletRequest req = mock(HttpServletRequest.class);
214 when(req.getRequestURI()).thenReturn("/oauth/policies");
215 HttpServletResponse resp = mock(HttpServletResponse.class);
216 ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
218 when(resp.getOutputStream()).thenReturn(printOut);
219 servlet.doGet(req, resp);
220 } catch (ServletException | IOException e) {
221 fail(e.getMessage());
223 verify(resp).setStatus(200);
224 String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
225 System.out.println(responseBody);
226 OdlPolicy[] anonPolicies = null;
228 anonPolicies = readJson(responseBody, OdlPolicy[].class);
229 } catch (JsonProcessingException e) {
230 fail("unable to read anon policies response");
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);
245 public void testPoliciesBasicAuth() {
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();
254 when(resp.getOutputStream()).thenReturn(printOut);
255 servlet.doGet(req, resp);
256 } catch (ServletException | IOException e) {
257 fail(e.getMessage());
259 verify(resp).setStatus(200);
260 String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
261 System.out.println(responseBody);
262 OdlPolicy[] anonPolicies = null;
264 anonPolicies = readJson(responseBody, OdlPolicy[].class);
265 } catch (JsonProcessingException e) {
266 fail("unable to read anon policies response");
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);
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();
289 when(resp.getOutputStream()).thenReturn(printOut);
290 servlet.doGet(req, resp);
291 } catch (ServletException | IOException e) {
292 fail(e.getMessage());
294 verify(resp).setStatus(200);
295 String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
296 System.out.println(responseBody);
297 OdlPolicy[] anonPolicies = null;
299 anonPolicies = readJson(responseBody, OdlPolicy[].class);
300 } catch (JsonProcessingException e) {
301 fail("unable to read anon policies response");
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);
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);
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());
333 private static OdlPolicy find(OdlPolicy[] policies, String path) {
334 for (OdlPolicy p : policies) {
335 if (path.equals(p.getPath())) {
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);
347 private static <T> T readJson(File file, Class<T> clazz) throws IOException {
348 OdlJsonMapper mapper = new OdlJsonMapper();
349 return mapper.readValue(file, clazz);
352 private static class TestServlet extends AuthHttpServlet {
354 private static final long serialVersionUID = 1L;
356 public TestServlet() throws IllegalArgumentException, Exception {
361 public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
362 super.doGet(req, resp);
366 public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
367 super.doPost(req, resp);