/**
* Copyright 2016 StreamSets Inc.
*
* Licensed under the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.streamsets.lib.security.http;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.streamsets.datacollector.util.Configuration;
import com.streamsets.testing.NetworkUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.UUID;
public class TestDisconnectedSSOManager {
private String dataDir;
@Before
public void setup() throws Exception {
File dir = new File("target", UUID.randomUUID().toString());
Assert.assertTrue(dir.mkdirs());
File disconnectedCredentials = new File(dir, DisconnectedSSOManager.DISCONNECTED_SSO_AUTHENTICATION_FILE);
Configuration conf = new Configuration();
conf.set(PasswordHasher.ITERATIONS_KEY, 1);
PasswordHasher hasher = new PasswordHasher(conf);
DisconnectedSecurityInfo info = new DisconnectedSecurityInfo();
info.addEntry("admin@org",
hasher.getPasswordHash("admin@org", "admin"),
ImmutableList.of("datacollector:admin", "user"),
Collections.<String>emptyList()
);
info.addEntry("guest@org",
hasher.getPasswordHash("guest@org", "guest"),
ImmutableList.of("datacollector:guest", "user"),
Collections.<String>emptyList()
);
info.toJsonFile(disconnectedCredentials);
dataDir = dir.getAbsolutePath();
}
@Test
public void testLifecyle() throws Exception {
DisconnectedSSOManager manager = new DisconnectedSSOManager(dataDir, new Configuration());
Assert.assertNotNull(manager.getAuthentication());
Assert.assertNotNull(manager.getSsoService());
Assert.assertFalse(manager.isEnabled());
manager.setEnabled(true);
Assert.assertTrue(manager.isEnabled());
ServletContextHandler contextHandler = Mockito.mock(ServletContextHandler.class);
manager.registerResources(contextHandler);
ArgumentCaptor<ServletHolder> servletHolder = ArgumentCaptor.forClass(ServletHolder.class);
ArgumentCaptor<String> path = ArgumentCaptor.forClass(String.class);
Mockito.verify(contextHandler, Mockito.times(3)).addServlet(servletHolder.capture(), path.capture());
Assert.assertEquals(DisconnectedSSOManager.SECURITY_RESOURCES, path.getAllValues().get(0));
Assert.assertEquals(DisconnectedLoginServlet.URL_PATH, path.getAllValues().get(1));
Assert.assertEquals(DisconnectedLogoutServlet.URL_PATH, path.getAllValues().get(2));
ArgumentCaptor<String> attrName = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<Object> attrValue = ArgumentCaptor.forClass(Object.class);
Mockito.verify(contextHandler, Mockito.times(2)).setAttribute(attrName.capture(), attrValue.capture());
Assert.assertEquals(
DisconnectedSSOManager.DISCONNECTED_SSO_AUTHENTICATION_HANDLER_ATTR,
attrName.getAllValues().get(0)
);
Assert.assertEquals(DisconnectedSSOManager.DISCONNECTED_SSO_SERVICE_ATTR, attrName.getAllValues().get(1));
Assert.assertTrue(attrValue.getAllValues().get(0) instanceof AuthenticationResourceHandler);
Assert.assertTrue(attrValue.getAllValues().get(1) instanceof DisconnectedSSOService);
}
@Test
public void testResources() throws Exception {
DisconnectedSSOManager manager = new DisconnectedSSOManager(dataDir, new Configuration());
int port = NetworkUtils.getRandomPort();
Server server = new Server(port);
ServletContextHandler contextHandler = new ServletContextHandler();
contextHandler.setContextPath("/");
manager.registerResources(contextHandler);
//dummy login page to assert forwarding
contextHandler.addServlet(new ServletHolder(new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(HttpServletResponse.SC_OK);
}
}), DisconnectedLoginServlet.DISCONNECTED_LOGIN_HTML);
server.setHandler(contextHandler);
server.start();
String baseUrl = "http://localhost:" + port;
try {
//disable, all resources return UNAVAILABLE
HttpURLConnection conn = (HttpURLConnection) new URL(baseUrl + "/security/login").openConnection();
Assert.assertEquals(HttpURLConnection.HTTP_UNAVAILABLE, conn.getResponseCode());
conn = (HttpURLConnection) new URL(baseUrl + "/security/_logout").openConnection();
Assert.assertEquals(HttpURLConnection.HTTP_UNAVAILABLE, conn.getResponseCode());
conn = (HttpURLConnection) new URL(baseUrl + "/security/public-rest/v1/authentication/login").openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
Assert.assertEquals(HttpURLConnection.HTTP_UNAVAILABLE, conn.getResponseCode());
//enabled, resources should work
manager.setEnabled(true);
LoginJson login = new LoginJson();
login.setUserName("admin@org");
login.setPassword("admin");
conn = (HttpURLConnection) new URL(baseUrl + "/security/login").openConnection();
conn.setRequestProperty(SSOConstants.X_REST_CALL, "foo");
Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
conn = (HttpURLConnection) new URL(baseUrl + "/security/_logout").openConnection();
Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
//login OK
conn = (HttpURLConnection) new URL(baseUrl + "/security/public-rest/v1/authentication/login").openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
new ObjectMapper().writeValue(conn.getOutputStream(), login);
Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
//login INCORRECT
login.setPassword("invalid");
conn = (HttpURLConnection) new URL(baseUrl + "/security/public-rest/v1/authentication/login").openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
new ObjectMapper().writeValue(conn.getOutputStream(), login);
Assert.assertEquals(HttpURLConnection.HTTP_FORBIDDEN, conn.getResponseCode());
} finally {
server.stop();
}
}
}