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;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assert.fail;
28 import org.junit.Ignore;
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 import com.fasterxml.jackson.core.JsonParseException;
34 import com.fasterxml.jackson.core.JsonProcessingException;
35 import com.fasterxml.jackson.databind.JsonMappingException;
36 import com.google.common.util.concurrent.FluentFuture;
38 import java.io.IOException;
39 import java.nio.charset.StandardCharsets;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.Optional;
43 import javax.servlet.ServletException;
44 import javax.servlet.http.HttpServletRequest;
45 import javax.servlet.http.HttpServletResponse;
46 import org.jolokia.osgi.security.Authenticator;
47 import org.json.JSONArray;
48 import org.junit.BeforeClass;
49 import org.junit.Test;
50 import org.mockito.internal.matchers.Any;
51 import org.onap.ccsdk.features.sdnr.wt.common.http.BaseHTTPClient;
52 import org.onap.ccsdk.features.sdnr.wt.common.test.ServletOutputStreamToByteArrayOutputStream;
53 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.Config;
54 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.CustomObjectMapper;
55 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.InvalidConfigurationException;
56 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.OdlPolicy;
57 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.data.UserTokenPayload;
58 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.AuthHttpServlet;
59 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.http.HeadersOnlyHttpServletRequest;
60 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.providers.TokenCreator;
61 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.test.helper.OdlJsonMapper;
62 import org.onap.ccsdk.features.sdnr.wt.oauthprovider.test.helper.OdlXmlMapper;
63 import org.opendaylight.aaa.api.Claim;
64 import org.opendaylight.aaa.api.IdMService;
65 import org.apache.shiro.authc.BearerToken;
66 import org.opendaylight.aaa.api.PasswordCredentialAuth;
67 import org.opendaylight.aaa.api.PasswordCredentials;
68 import org.opendaylight.aaa.shiro.web.env.AAAShiroWebEnvironment;
69 import org.opendaylight.mdsal.binding.api.DataBroker;
70 import org.opendaylight.mdsal.binding.api.ReadTransaction;
71 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.ShiroConfiguration;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.ShiroConfigurationBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.HttpAuthorization;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.aaa.rev161214.http.authorization.Policies;
76 import org.opendaylight.yangtools.util.concurrent.FluentFutures;
77 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
80 public class TestAuthHttpServlet {
82 private static final String TESTCONFIGFILE = TestConfig.TEST_CONFIG_FILENAME;
83 private static final String TESTSHIROCONFIGFILE = "src/test/resources/aaa-app-config.test.xml";
84 private static final String MDSALDYNAUTHFILENAME = "src/test/resources/mdsalDynAuthData.json";
85 private static TestServlet servlet;
86 private static DataBroker dataBroker = loadDynamicMdsalAuthDataBroker();
87 private static Authenticator odlAuthenticator = mock(Authenticator.class);
88 private static IdMService odlIdentityService = mock(IdMService.class);
89 private static PasswordCredentialAuth passwordCredentialAuth;
90 private static TokenCreator tokenCreator;
91 // private static final HttpServletRequest authreq = new HeadersOnlyHttpServletRequest(
92 // Map.of("Authorization", BaseHTTPClient.getAuthorizationHeaderValue("admin@sdn", "admin")));
95 public static void init() throws IllegalArgumentException, Exception {
98 Config config = createConfigFile();
99 tokenCreator = TokenCreator.getInstance(config);
100 servlet = new TestServlet();
101 } catch (IOException | InvalidConfigurationException e) {
102 fail(e.getMessage());
104 servlet.setDataBroker(dataBroker);
105 passwordCredentialAuth = mock(PasswordCredentialAuth.class);
107 servlet.setPasswordCredentialAuth(passwordCredentialAuth);
110 private static DataBroker loadDynamicMdsalAuthDataBroker() {
111 DataBroker dataBroker = mock(DataBroker.class);
112 ReadTransaction rotx = mock(ReadTransaction.class);
113 InstanceIdentifier<Policies> iif = InstanceIdentifier.create(HttpAuthorization.class).child(Policies.class);
115 when(rotx.read(LogicalDatastoreType.CONFIGURATION, iif))
116 .thenReturn(loadDataBrokerFile(MDSALDYNAUTHFILENAME, Policies.class));
117 } catch (IOException e) {
118 fail("problem init databroker read" + e.getMessage());
120 when(dataBroker.newReadOnlyTransaction()).thenReturn(rotx);
124 private static <T> FluentFuture<Optional<T>> loadDataBrokerFile(String fn, Class<T> clazz) throws IOException {
125 return FluentFutures.immediateFluentFuture(Optional.ofNullable(readJson(new File(fn), clazz)));
128 private static ShiroConfiguration loadShiroConfig(String filename)
129 throws JsonParseException, JsonMappingException, IOException {
130 OdlXmlMapper mapper = new OdlXmlMapper();
131 return mapper.readValue(new File(filename), ShiroConfigurationBuilder.class).build();
134 private static Config createConfigFile() throws IOException, InvalidConfigurationException {
135 return Config.getInstance(TESTCONFIGFILE);
140 public void testValidLoginRedirect() {
142 HttpServletRequest req = mock(HttpServletRequest.class);
143 when(req.getRequestURI()).thenReturn("/oauth/login/keycloak");
144 HttpServletResponse resp = mock(HttpServletResponse.class);
146 servlet.doGet(req, resp);
147 } catch (ServletException | IOException e) {
148 fail(e.getMessage());
150 verify(resp).setStatus(302);
151 verify(resp).setHeader("Location",
152 "http://10.20.11.160:8080/auth/realms/onap/protocol/openid-connect/auth?client_id=odlux.app&response"
153 + "_type=code&scope=openid&redirect_uri=http%3A%2F%2Fnasp.diasf.de%2Foauth%2Fredirect%2Fkeycloak");
157 public void testInValidLoginRedirect() {
159 HttpServletRequest req = mock(HttpServletRequest.class);
160 when(req.getRequestURI()).thenReturn("/oauth/login/unknownproviderid");
161 HttpServletResponse resp = mock(HttpServletResponse.class);
162 ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
164 when(resp.getOutputStream()).thenReturn(printOut);
165 servlet.doGet(req, resp);
166 } catch (ServletException | IOException e) {
167 fail(e.getMessage());
169 verify(resp).setStatus(404);
173 public void testValidLogin() {
175 HttpServletRequest req = mock(HttpServletRequest.class);
176 when(req.getRequestURI()).thenReturn("/oauth/login");
177 when(req.getParameter("username")).thenReturn("admin");
178 when(req.getParameter("password")).thenReturn("admin");
179 Claim claim = new Claim() {
181 public String clientId() {
186 public String userId() {
191 public String user() {
196 public String domain() {
201 public Set<String> roles() {
202 return Set.of("admin");
205 when(passwordCredentialAuth.authenticate(any(PasswordCredentials.class))).thenReturn(claim);
206 HttpServletResponse resp = mock(HttpServletResponse.class);
207 ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
209 when(resp.getOutputStream()).thenReturn(printOut);
210 servlet.doPost(req, resp);
211 } catch (ServletException | IOException e) {
212 fail(e.getMessage());
214 verify(resp).setStatus(200);
218 public void testGetProviders() {
220 HttpServletRequest req = mock(HttpServletRequest.class);
221 when(req.getRequestURI()).thenReturn("/oauth/providers");
222 HttpServletResponse resp = mock(HttpServletResponse.class);
223 ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
225 when(resp.getOutputStream()).thenReturn(printOut);
226 servlet.doGet(req, resp);
227 } catch (ServletException | IOException e) {
228 fail(e.getMessage());
230 verify(resp).setStatus(200);
231 String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
232 System.out.println(responseBody);
233 JSONArray a = new JSONArray(responseBody);
234 assertEquals(1, a.length());
235 assertEquals("keycloak", a.getJSONObject(0).getString("id"));
236 assertEquals("OSNL Keycloak Provider", a.getJSONObject(0).getString("title"));
237 assertEquals("/oauth/login/keycloak", a.getJSONObject(0).getString("loginUrl"));
245 public void testPoliciesAnon() {
247 HttpServletRequest req = mock(HttpServletRequest.class);
248 when(req.getRequestURI()).thenReturn("/oauth/policies");
249 HttpServletResponse resp = mock(HttpServletResponse.class);
250 ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
252 when(resp.getOutputStream()).thenReturn(printOut);
253 servlet.doGet(req, resp);
254 } catch (ServletException | IOException e) {
255 fail(e.getMessage());
257 verify(resp).setStatus(200);
258 String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
259 System.out.println(responseBody);
260 OdlPolicy[] anonPolicies = null;
262 anonPolicies = readJson(responseBody, OdlPolicy[].class);
263 } catch (JsonProcessingException e) {
264 fail("unable to read anon policies response");
266 assertEquals(9, anonPolicies.length);
267 OdlPolicy pApidoc = find(anonPolicies, "/apidoc/**");
268 assertNotNull(pApidoc);
269 assertAllEquals(false, 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(false, pRestconf);
279 public void testPoliciesBasicAuth() {
281 HttpServletRequest req = mock(HttpServletRequest.class);
282 when(req.getRequestURI()).thenReturn("/oauth/policies");
283 when(req.getHeader("Authorization")).thenReturn(BaseHTTPClient.getAuthorizationHeaderValue("admin", "admin"));
284 when(odlIdentityService.listRoles("admin@sdn", "sdn")).thenReturn(Arrays.asList("admin"));
285 HttpServletResponse resp = mock(HttpServletResponse.class);
286 ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
288 when(resp.getOutputStream()).thenReturn(printOut);
289 servlet.doGet(req, resp);
290 } catch (ServletException | IOException e) {
291 fail(e.getMessage());
293 verify(resp).setStatus(200);
294 String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
295 System.out.println(responseBody);
296 OdlPolicy[] anonPolicies = null;
298 anonPolicies = readJson(responseBody, OdlPolicy[].class);
299 } catch (JsonProcessingException e) {
300 fail("unable to read anon policies response");
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(false, pRestconf);
315 public void testPoliciesBearer() {
316 HttpServletRequest req = mock(HttpServletRequest.class);
317 when(req.getRequestURI()).thenReturn("/oauth/policies");
318 String token = createToken("admin", Arrays.asList("admin", "provision")).getToken();
319 when(req.getHeader("Authorization")).thenReturn(String.format("Bearer %s", token));
320 HttpServletResponse resp = mock(HttpServletResponse.class);
321 ServletOutputStreamToByteArrayOutputStream printOut = new ServletOutputStreamToByteArrayOutputStream();
323 when(resp.getOutputStream()).thenReturn(printOut);
324 servlet.doGet(req, resp);
325 } catch (ServletException | IOException e) {
326 fail(e.getMessage());
328 verify(resp).setStatus(200);
329 String responseBody = printOut.getByteArrayOutputStream().toString(StandardCharsets.UTF_8);
330 System.out.println(responseBody);
331 OdlPolicy[] anonPolicies = null;
333 anonPolicies = readJson(responseBody, OdlPolicy[].class);
334 } catch (JsonProcessingException e) {
335 fail("unable to read anon policies response");
337 assertEquals(9, anonPolicies.length);
338 OdlPolicy pApidoc = find(anonPolicies, "/apidoc/**");
339 assertNotNull(pApidoc);
340 assertAllEquals(false, pApidoc);
341 OdlPolicy pOauth = find(anonPolicies, "/oauth/**");
342 assertNotNull(pOauth);
343 assertAllEquals(true, pOauth);
344 OdlPolicy pRestconf = find(anonPolicies, "/rests/**");
345 assertNotNull(pRestconf);
346 assertAllEquals(true, pRestconf);
349 private static BearerToken createToken(String username, List<String> roles) {
350 UserTokenPayload data = new UserTokenPayload();
351 data.setPreferredUsername(username);
352 data.setFamilyName("");
353 data.setGivenName(username);
354 data.setExp(tokenCreator.getDefaultExp());
355 data.setRoles(roles);
356 return tokenCreator.createNewJWT(data);
359 private static void assertAllEquals(boolean b, OdlPolicy p) {
360 assertEquals(b, p.getMethods().isGet());
361 assertEquals(b, p.getMethods().isPost());
362 assertEquals(b, p.getMethods().isPut());
363 assertEquals(b, p.getMethods().isDelete());
364 assertEquals(b, p.getMethods().isPatch());
367 private static OdlPolicy find(OdlPolicy[] policies, String path) {
368 for (OdlPolicy p : policies) {
369 if (path.equals(p.getPath())) {
376 private static <T> T readJson(String data, Class<T> clazz) throws JsonMappingException, JsonProcessingException {
377 CustomObjectMapper mapper = new CustomObjectMapper();
378 return mapper.readValue(data, clazz);
381 private static <T> T readJson(File file, Class<T> clazz) throws IOException {
382 OdlJsonMapper mapper = new OdlJsonMapper();
383 return mapper.readValue(file, clazz);
386 private static class TestServlet extends AuthHttpServlet {
388 private static final long serialVersionUID = 1L;
390 public TestServlet() throws IllegalArgumentException, Exception {
391 super(TESTSHIROCONFIGFILE);
395 public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
396 super.doGet(req, resp);
400 public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
401 super.doPost(req, resp);