# HG changeset patch # User Severin Gehwolf # Date 1366373350 -7200 # Node ID dff087c3c875446cd6e899893e08316f967e3168 # Parent 7f100b629033afcbfe97549cb45bf1e96dbabeab Fix coverage of authentication/authorization checks. Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-April/006410.html PR952 diff -r 7f100b629033 -r dff087c3c875 integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTest.java Fri May 03 12:39:38 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTest.java Fri Apr 19 14:09:10 2013 +0200 @@ -50,11 +50,13 @@ import java.util.UUID; import org.eclipse.jetty.security.DefaultUserIdentity; +import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.security.MappedLoginService; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.security.Password; import org.eclipse.jetty.webapp.WebAppContext; +import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -70,6 +72,7 @@ import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; import com.redhat.thermostat.vm.classstat.common.model.VmClassStat; import com.redhat.thermostat.web.client.internal.WebStorage; +import com.redhat.thermostat.web.server.auth.Roles; import expectj.Spawn; @@ -89,21 +92,16 @@ storage.expectClose(); assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); - - port = FreePortFinder.findFreePort(new TryPort() { - - @Override - public void tryPort(int port) throws Exception { - startServer(port); - } - }); - registerCategory(); + } + + @After + public void tearDown() throws Exception { + server.stop(); + server.join(); } @AfterClass public static void tearDownOnce() throws Exception { - server.stop(); - server.join(); Spawn storage = spawnThermostat("storage", "--stop"); storage.expect("server shutdown complete"); @@ -112,45 +110,47 @@ assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); } - private static void startServer(int port) throws Exception { + private static void startServer(int port, LoginService loginService) throws Exception { server = new Server(port); ApplicationInfo appInfo = new ApplicationInfo(); String version = appInfo.getMavenVersion(); String warfile = "target/libs/thermostat-web-war-" + version + ".war"; WebAppContext ctx = new WebAppContext(warfile, "/thermostat"); ctx.getSecurityHandler().setAuthMethod("BASIC"); - ctx.getSecurityHandler().setLoginService(new MappedLoginService() { - - @Override - protected void loadUsers() throws IOException { - putUser("testname", new Password("testpasswd"), new String[] { "thermostat-agent" }); - putUser("test-no-role", new Password("testpasswd"), new String[] { "fluff" }); - putUser("test-cmd-channel", new Password("testpasswd"), new String[] { "thermostat-cmd-channel" }); - } - - @Override - protected UserIdentity loadUser(String username) { - if (username.equals("test-cmd-channel")) { - return new DefaultUserIdentity(null, null, new String[] { "thermostat-cmd-channel" }); - } - return new DefaultUserIdentity(null, null, new String[] { "thermostat-agent" }); - } - }); + ctx.getSecurityHandler().setLoginService(loginService); server.setHandler(ctx); server.start(); } - private static void registerCategory() { + private static void connectStorage(String username, String password) { String url = "http://localhost:" + port + "/thermostat/storage"; - StartupConfiguration config = new ConnectionConfiguration(url, "testname", "testpasswd"); + StartupConfiguration config = new ConnectionConfiguration(url, username, password); webStorage = new WebStorage(config); webStorage.setAgentId(new UUID(42, 24)); webStorage.getConnection().connect(); - webStorage.registerCategory(VmClassStatDAO.vmClassStatsCategory); } @Test - public void testPutFind() { + public void authorizedAdd() throws Exception { + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.ACCESS_REALM, + Roles.LOGIN, + Roles.APPEND + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + connectStorage(testuser, password); + webStorage.registerCategory(VmClassStatDAO.vmClassStatsCategory); + Add add = webStorage.createAdd(VmClassStatDAO.vmClassStatsCategory); VmClassStat pojo = new VmClassStat(); pojo.setAgentId("fluff"); @@ -159,7 +159,29 @@ pojo.setVmId(987); add.setPojo(pojo); add.apply(); - + } + + @Test + public void authorizedQuery() throws Exception { + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ, + Roles.LOGIN, + Roles.ACCESS_REALM + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + connectStorage(testuser, password); + webStorage.registerCategory(VmClassStatDAO.vmClassStatsCategory); + Query query = webStorage.createQuery(VmClassStatDAO.vmClassStatsCategory); Cursor cursor = query.execute(); assertTrue(cursor.hasNext()); @@ -172,7 +194,25 @@ } @Test - public void testLoadSave() throws IOException, InterruptedException { + public void authorizedLoadSave() throws Exception { + String[] roleNames = new String[] { + Roles.LOAD_FILE, + Roles.SAVE_FILE, + Roles.ACCESS_REALM, + Roles.LOGIN + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + connectStorage(testuser, password); + byte[] data = "Hello World".getBytes(); webStorage.saveFile("test", new ByteArrayInputStream(data)); // Note: Apparently, saving the file takes a bit. Without this @@ -188,4 +228,30 @@ } assertEquals("Hello World", str.toString()); } + + private static class TestLoginService extends MappedLoginService { + + private final String[] roleNames; + private final String username; + private final String password; + + private TestLoginService(String username, String password, + String[] roleNames) { + this.username = username; + this.password = password; + this.roleNames = roleNames; + } + + @Override + protected void loadUsers() throws IOException { + putUser(username, new Password(password), + roleNames); + } + + @Override + protected UserIdentity loadUser(String username) { + return new DefaultUserIdentity(null, null, + roleNames); + } + } } diff -r 7f100b629033 -r dff087c3c875 web/cmd/src/main/java/com/redhat/thermostat/web/cmd/WebServiceLauncher.java --- a/web/cmd/src/main/java/com/redhat/thermostat/web/cmd/WebServiceLauncher.java Fri May 03 12:39:38 2013 -0400 +++ b/web/cmd/src/main/java/com/redhat/thermostat/web/cmd/WebServiceLauncher.java Fri Apr 19 14:09:10 2013 +0200 @@ -57,6 +57,7 @@ import com.redhat.thermostat.common.utils.HostPortPair; import com.redhat.thermostat.storage.mongodb.MongoStorageProvider; import com.redhat.thermostat.web.server.WebStorageEndPoint; +import com.redhat.thermostat.web.server.auth.Roles; class WebServiceLauncher { @@ -111,7 +112,7 @@ ConstraintMapping constraintMap = new ConstraintMapping(); Constraint constraint = new Constraint(); constraint.setAuthenticate(true); - constraint.setRoles(new String[] { "thermostat-client", "thermostat-agent", "thermostat-cmd-channel" }); + constraint.setRoles(new String[] { Roles.ACCESS_REALM }); constraint.setName("Entire Application"); constraintMap.setPathSpec("/*"); constraintMap.setConstraint(constraint); @@ -119,18 +120,34 @@ secHandler.setRealmName("Thermostat Realm"); secHandler.setAuthMethod("BASIC"); secHandler.addConstraintMapping(constraintMap); - secHandler.addRole("thermostat-agent"); - secHandler.addRole("thermostat-client"); + // inform security handler about all roles + for (String role : Roles.ALL_ROLES) { + secHandler.addRole(role); + } secHandler.setLoginService(new MappedLoginService() { @Override protected void loadUsers() throws IOException { - putUser("thermostat", new Password("thermostat"), new String[] { "thermostat-agent", "thermostat-client", "thermostat-cmd-channel" }); + // Register a thermostat agent user + putUser("thermostat-agent", new Password("agent-tester"), Roles.AGENT_ROLES); + // Same for a client + putUser("thermostat-client", new Password("client-tester"), Roles.CLIENT_ROLES); + // A realm access test user + putUser("thermostat-realm-user", new Password("realm-tester"), new String[] { Roles.ACCESS_REALM }); } @Override protected UserIdentity loadUser(String username) { - return new DefaultUserIdentity(null, null, new String[] { "thermostat-agent", "thermostat-client", "thermostat-cmd-channel" }); + if (username.equals("thermostat-agent")) { + return new DefaultUserIdentity(null, null, Roles.AGENT_ROLES); + } else if (username.equals("thermostat-client")) { + return new DefaultUserIdentity(null, null, Roles.CLIENT_ROLES); + } else if (username.equals("thermostat-realm-user")) { + return new DefaultUserIdentity(null, null, new String[] { Roles.ACCESS_REALM } ); + } else { + // return empty identity + return new DefaultUserIdentity(null, null, new String[0]); + } } }); ctx.setSecurityHandler(secHandler); diff -r 7f100b629033 -r dff087c3c875 web/cmd/src/test/java/com/redhat/thermostat/web/cmd/WebServiceLauncherTest.java --- a/web/cmd/src/test/java/com/redhat/thermostat/web/cmd/WebServiceLauncherTest.java Fri May 03 12:39:38 2013 -0400 +++ b/web/cmd/src/test/java/com/redhat/thermostat/web/cmd/WebServiceLauncherTest.java Fri Apr 19 14:09:10 2013 +0200 @@ -39,6 +39,8 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; @@ -109,9 +111,10 @@ } @Test - @Ignore("server.start() throws NPE for some reason") + @Ignore("server.start() throws NPE since it's a final method declared in class AbstractLifeCycle and Mockito doesn't do final method mocking.") public void verifyStartDoesStartServer() throws Exception { Server server = mock(Server.class); + doNothing().when(server).start(); WebServiceLauncher launcher = new WebServiceLauncher(server); launcher.setIpAddresses(dummyIp); launcher.setStorageURL("mongodb://test.example.org/db"); diff -r 7f100b629033 -r dff087c3c875 web/server/pom.xml --- a/web/server/pom.xml Fri May 03 12:39:38 2013 -0400 +++ b/web/server/pom.xml Fri Apr 19 14:09:10 2013 +0200 @@ -136,7 +136,8 @@ com.redhat.thermostat.web.server Red Hat, Inc. - com.redhat.thermostat.web.server + com.redhat.thermostat.web.server, + com.redhat.thermostat.web.server.auth com.redhat.thermostat.web.server.internal diff -r 7f100b629033 -r dff087c3c875 web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java --- a/web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java Fri May 03 12:39:38 2013 -0400 +++ b/web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java Fri Apr 19 14:09:10 2013 +0200 @@ -87,15 +87,13 @@ import com.redhat.thermostat.web.common.WebQuery; import com.redhat.thermostat.web.common.WebRemove; import com.redhat.thermostat.web.common.WebUpdate; +import com.redhat.thermostat.web.server.auth.Roles; @SuppressWarnings("serial") public class WebStorageEndPoint extends HttpServlet { private static final String TOKEN_MANAGER_TIMEOUT_PARAM = "token-manager-timeout"; private static final String TOKEN_MANAGER_KEY = "token-manager"; - private static final String ROLE_THERMOSTAT_AGENT = "thermostat-agent"; - private static final String ROLE_THERMOSTAT_CLIENT = "thermostat-client"; - private static final String ROLE_CMD_CHANNEL = "thermostat-cmd-channel"; // our strings can contain non-ASCII characters. Use UTF-8 // see also PR 1344 @@ -156,7 +154,10 @@ // to avoid further memory leaks. Connection connection = storage.getConnection(); try { - connection.disconnect(); + // Tests have null connections + if (connection != null) { + connection.disconnect(); + } } finally { storage.shutdown(); } @@ -215,16 +216,28 @@ } private void ping(HttpServletRequest req, HttpServletResponse resp) { + if (! isAuthorized(req, resp, Roles.LOGIN)) { + return; + } + resp.setStatus(HttpServletResponse.SC_OK); } private void purge(HttpServletRequest req, HttpServletResponse resp) { + if (! isAuthorized(req, resp, Roles.PURGE)) { + return; + } + String agentId = req.getParameter("agentId"); storage.purge(agentId); resp.setStatus(HttpServletResponse.SC_OK); } private void loadFile(HttpServletRequest req, HttpServletResponse resp) throws IOException { + if (! isAuthorized(req, resp, Roles.LOAD_FILE)) { + return; + } + String name = req.getParameter("file"); try (InputStream data = storage.loadFile(name)) { OutputStream out = resp.getOutputStream(); @@ -240,6 +253,10 @@ } private void saveFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if (! isAuthorized(req, resp, Roles.SAVE_FILE)) { + return; + } + boolean isMultipart = ServletFileUpload.isMultipartContent(req); if (! isMultipart) { throw new ServletException("expected multipart message"); @@ -264,6 +281,9 @@ } private void getCount(HttpServletRequest req, HttpServletResponse resp) { + if (! isAuthorized(req, resp, Roles.GET_COUNT)) { + return; + } try { String categoryParam = req.getParameter("category"); int categoryId = gson.fromJson(categoryParam, Integer.class); @@ -280,6 +300,10 @@ } private synchronized void registerCategory(HttpServletRequest req, HttpServletResponse resp) throws IOException { + if (! isAuthorized(req, resp, Roles.REGISTER_CATEGORY)) { + return; + } + String categoryName = req.getParameter("name"); String categoryParam = req.getParameter("category"); int id; @@ -303,19 +327,25 @@ } private void putPojo(HttpServletRequest req, HttpServletResponse resp) { - if (! req.isUserInRole(ROLE_THERMOSTAT_AGENT)) { - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } - String insertParam = req.getParameter("insert"); WebInsert insert = gson.fromJson(insertParam, WebInsert.class); int categoryId = insert.getCategoryId(); Category category = getCategoryFromId(categoryId); + Put targetPut = null; + if (insert.isReplace()) { + if (! isAuthorized(req, resp, Roles.REPLACE)) { + return; + } + targetPut = storage.createReplace(category); + } else { + if (! isAuthorized(req, resp, Roles.APPEND)) { + return; + } + targetPut = storage.createAdd(category); + } Class pojoCls = category.getDataClass(); String pojoParam = req.getParameter("pojo"); Pojo pojo = gson.fromJson(pojoParam, pojoCls); - Put targetPut = insert.isReplace() ? storage.createReplace(category) : storage.createAdd(category); targetPut.setPojo(pojo); targetPut.apply(); resp.setStatus(HttpServletResponse.SC_OK); @@ -323,6 +353,10 @@ @SuppressWarnings({ "rawtypes", "unchecked" }) private void removePojo(HttpServletRequest req, HttpServletResponse resp) { + if (! isAuthorized(req, resp, Roles.DELETE)) { + return; + } + String removeParam = req.getParameter("remove"); WebRemove remove = gson.fromJson(removeParam, WebRemove.class); Remove targetRemove = storage.createRemove(); @@ -338,6 +372,10 @@ @SuppressWarnings({ "rawtypes", "unchecked" }) private void updatePojo(HttpServletRequest req, HttpServletResponse resp) { + if (! isAuthorized(req, resp, Roles.UPDATE)) { + return; + } + try { String updateParam = req.getParameter("update"); WebUpdate update = gson.fromJson(updateParam, WebUpdate.class); @@ -374,6 +412,9 @@ @SuppressWarnings({ "rawtypes", "unchecked" }) private void findAll(HttpServletRequest req, HttpServletResponse resp) throws IOException { + if (! isAuthorized(req, resp, Roles.READ)) { + return; + } String queryParam = req.getParameter("query"); WebQuery query = gson.fromJson(queryParam, WebQuery.class); Query targetQuery = constructTargetQuery(query); @@ -385,6 +426,7 @@ writeResponse(resp, resultList.toArray()); } + @SuppressWarnings({ "rawtypes", "unchecked" }) private Query constructTargetQuery(WebQuery query) { int categoryId = query.getCategoryId(); Category category = getCategoryFromId(categoryId); @@ -414,8 +456,7 @@ } private void generateToken(HttpServletRequest req, HttpServletResponse resp) throws IOException { - if (! req.isUserInRole(ROLE_CMD_CHANNEL)) { - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + if (! isAuthorized(req, resp, Roles.CMD_CHANNEL_GENERATE) ) { return; } TokenManager tokenManager = (TokenManager) getServletContext().getAttribute(TOKEN_MANAGER_KEY); @@ -428,8 +469,7 @@ } private void verifyToken(HttpServletRequest req, HttpServletResponse resp) { - if (! req.isUserInRole(ROLE_CMD_CHANNEL)) { - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + if (! isAuthorized(req, resp, Roles.CMD_CHANNEL_VERIFY) ) { return; } TokenManager tokenManager = (TokenManager) getServletContext().getAttribute(TOKEN_MANAGER_KEY); @@ -438,11 +478,20 @@ byte[] token = Base64.decodeBase64(req.getParameter("token")); boolean verified = tokenManager.verifyToken(clientToken, token); if (! verified) { - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + resp.setStatus(HttpServletResponse.SC_FORBIDDEN); } else { resp.setStatus(HttpServletResponse.SC_OK); } } + + private boolean isAuthorized(HttpServletRequest req, HttpServletResponse resp, String role) { + if (req.isUserInRole(role)) { + return true; + } else { + resp.setStatus(HttpServletResponse.SC_FORBIDDEN); + return false; + } + } } diff -r 7f100b629033 -r dff087c3c875 web/server/src/main/java/com/redhat/thermostat/web/server/auth/Roles.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/server/src/main/java/com/redhat/thermostat/web/server/auth/Roles.java Fri Apr 19 14:09:10 2013 +0200 @@ -0,0 +1,77 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.web.server.auth; + +/** + * Roles thermostat knows about. + * + */ +public interface Roles { + + final String APPEND = "thermostat-add"; + final String REPLACE = "thermostat-replace"; + final String UPDATE = "thermostat-update"; + final String DELETE = "thermostat-remove"; + final String READ = "thermostat-query"; + final String GET_COUNT = "thermostat-get-count"; + final String LOAD_FILE = "thermostat-load-file"; + final String SAVE_FILE = "thermostat-save-file"; + final String PURGE = "thermostat-purge"; + final String REGISTER_CATEGORY = "thermostat-register-category"; + final String CMD_CHANNEL_VERIFY = "thermostat-cmdc-verify"; + final String CMD_CHANNEL_GENERATE = "thermostat-cmdc-generate"; + final String LOGIN = "thermostat-login"; + final String ACCESS_REALM = "thermostat-realm"; + + final String[] ALL_ROLES = { + APPEND, REPLACE, UPDATE, DELETE, READ, GET_COUNT, LOAD_FILE, + SAVE_FILE, PURGE, REGISTER_CATEGORY, CMD_CHANNEL_GENERATE, + CMD_CHANNEL_VERIFY, LOGIN, ACCESS_REALM + }; + + final String[] AGENT_ROLES = { + APPEND, REPLACE, UPDATE, DELETE, SAVE_FILE, PURGE, + REGISTER_CATEGORY, CMD_CHANNEL_VERIFY, + LOGIN, ACCESS_REALM + }; + + final String[] CLIENT_ROLES = { + ACCESS_REALM, LOGIN, CMD_CHANNEL_GENERATE, LOAD_FILE, + GET_COUNT, READ, REGISTER_CATEGORY + }; + +} diff -r 7f100b629033 -r dff087c3c875 web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java --- a/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java Fri May 03 12:39:38 2013 -0400 +++ b/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java Fri Apr 19 14:09:10 2013 +0200 @@ -60,6 +60,7 @@ import org.apache.commons.codec.binary.Base64; import org.eclipse.jetty.security.DefaultUserIdentity; +import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.security.MappedLoginService; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.UserIdentity; @@ -94,6 +95,7 @@ import com.redhat.thermostat.web.common.WebQuery; import com.redhat.thermostat.web.common.WebRemove; import com.redhat.thermostat.web.common.WebUpdate; +import com.redhat.thermostat.web.server.auth.Roles; public class WebStorageEndpointTest { @@ -160,37 +162,13 @@ mockStorage = mock(Storage.class); StorageWrapper.setStorage(mockStorage); - port = FreePortFinder.findFreePort(new TryPort() { - - @Override - public void tryPort(int port) throws Exception { - startServer(port); - } - }); - registerCategory(); } - private void startServer(int port) throws Exception { + private void startServer(int port, LoginService loginService) throws Exception { server = new Server(port); WebAppContext ctx = new WebAppContext("src/main/webapp", "/"); ctx.getSecurityHandler().setAuthMethod("BASIC"); - ctx.getSecurityHandler().setLoginService(new MappedLoginService() { - - @Override - protected void loadUsers() throws IOException { - putUser("testname", new Password("testpasswd"), new String[] { "thermostat-agent" }); - putUser("test-no-role", new Password("testpasswd"), new String[] { "fluff" }); - putUser("test-cmd-channel", new Password("testpasswd"), new String[] { "thermostat-cmd-channel" }); - } - - @Override - protected UserIdentity loadUser(String username) { - if (username.equals("test-cmd-channel")) { - return new DefaultUserIdentity(null, null, new String[] { "thermostat-cmd-channel" }); - } - return new DefaultUserIdentity(null, null, new String[] { "thermostat-agent" }); - } - }); + ctx.getSecurityHandler().setLoginService(loginService); server.setHandler(ctx); server.start(); } @@ -202,7 +180,23 @@ } @Test - public void testFindAllPojos() throws IOException { + public void authorizedFindAllPojos() throws Exception { + String[] roleNames = new String[] { + Roles.REGISTER_CATEGORY, + Roles.READ + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + registerCategory(testuser, password); + TestClass expected1 = new TestClass(); expected1.setKey1("fluff1"); expected1.setKey2(42); @@ -222,6 +216,7 @@ URL url = new URL(endpoint + "/find-all"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); + sendAuthorization(conn, testuser, password); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoInput(true); conn.setDoOutput(true); @@ -255,8 +250,23 @@ } @Test - public void testPutPojo() throws IOException { - + public void authorizedReplacePutPojo() throws Exception { + String[] roleNames = new String[] { + Roles.REPLACE, + Roles.REGISTER_CATEGORY + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + registerCategory(testuser, password); + Replace replace = mock(Replace.class); when(mockStorage.createReplace(any(Category.class))).thenReturn(replace); @@ -269,7 +279,7 @@ URL url = new URL(endpoint + "/put-pojo"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); - sendAuthorization(conn, "testname", "testpasswd"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); @@ -296,8 +306,24 @@ } @Test - public void testRemovePojo() throws IOException { - + public void authorizedRemovePojo() throws Exception { + String[] roleNames = new String[] { + Roles.DELETE, + Roles.REGISTER_CATEGORY + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + registerCategory(testuser, password); + + Remove mockRemove = mock(Remove.class); when(mockRemove.from(any(Category.class))).thenReturn(mockRemove); when(mockRemove.where(any(Key.class), any())).thenReturn(mockRemove); @@ -308,6 +334,8 @@ URL url = new URL(endpoint + "/remove-pojo"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); Map,Integer> categoryIds = new HashMap<>(); @@ -328,8 +356,23 @@ } @Test - public void testUpdatePojo() throws IOException { - + public void authorizedUpdatePojo() throws Exception { + String[] roleNames = new String[] { + Roles.UPDATE, + Roles.REGISTER_CATEGORY + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + registerCategory(testuser, password); + Update mockUpdate = mock(Update.class); when(mockStorage.createUpdate(any(Category.class))).thenReturn(mockUpdate); @@ -337,6 +380,8 @@ URL url = new URL(endpoint + "/update-pojo"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); @@ -366,13 +411,30 @@ @Test - public void testGetCount() throws IOException { - + public void authorizedGetCount() throws Exception { + String[] roleNames = new String[] { + Roles.GET_COUNT, + Roles.REGISTER_CATEGORY + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + registerCategory(testuser, password); + when(mockStorage.getCount(category)).thenReturn(12345L); String endpoint = getEndpoint(); URL url = new URL(endpoint + "/get-count"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); @@ -391,11 +453,26 @@ } @Test - public void testSaveFile() throws IOException { + public void authorizedSaveFile() throws Exception { + String[] roleNames = new String[] { + Roles.SAVE_FILE, + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); String endpoint = getEndpoint(); URL url = new URL(endpoint + "/save-file"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=fluff"); conn.setRequestProperty("Transfer-Encoding", "chunked"); @@ -425,8 +502,21 @@ } @Test - public void testLoadFile() throws IOException { - + public void authorizedLoadFile() throws Exception { + String[] roleNames = new String[] { + Roles.LOAD_FILE, + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + byte[] data = "Hello World".getBytes(); InputStream in = new ByteArrayInputStream(data); when(mockStorage.loadFile("fluff")).thenReturn(in); @@ -435,6 +525,8 @@ URL url = new URL(endpoint + "/load-file"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setDoInput(true); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); @@ -455,19 +547,33 @@ } @Test - public void testPurge() throws IOException { + public void authorizedPurge() throws Exception { + String[] roleNames = new String[] { + Roles.PURGE, + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); String endpoint = getEndpoint(); URL url = new URL(endpoint + "/purge"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setRequestMethod("POST"); + sendAuthorization(conn, testuser, password); conn.getOutputStream().write("agentId=fluff".getBytes()); int status = conn.getResponseCode(); assertEquals(200, status); verify(mockStorage).purge("fluff"); } - private void registerCategory() { + private void registerCategory(String username, String password) { try { String endpoint = getEndpoint(); URL url = new URL(endpoint + "/register-category"); @@ -477,6 +583,7 @@ conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestMethod("POST"); + sendAuthorization(conn, username, password); OutputStream out = conn.getOutputStream(); Gson gson = new Gson(); OutputStreamWriter writer = new OutputStreamWriter(out); @@ -500,32 +607,69 @@ } @Test - public void testBasicGenerateToken() throws IOException { - - verifyGenerateToken(); + public void authorizedGenerateToken() throws Exception { + String[] roleNames = new String[] { + Roles.CMD_CHANNEL_GENERATE + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + verifyAuthorizedGenerateToken(testuser, password); } @Test - public void testGenerateTokenWithoutAuth() throws IOException { + public void unauthorizedGenerateToken() throws Exception { + String[] noRoles = new String[0]; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, noRoles); + + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); String endpoint = getEndpoint(); URL url = new URL(endpoint + "/generate-token"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); - sendAuthorization(conn, "test-no-role", "testpasswd"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setDoInput(true); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); out.write("client-token=fluff"); out.flush(); - assertEquals(401, conn.getResponseCode()); + assertEquals(403, conn.getResponseCode()); } @Test - public void testBasicGenerateVerifyToken() throws IOException { - - byte[] token = verifyGenerateToken(); + public void authorizedGenerateVerifyToken() throws Exception { + String[] roleNames = new String[] { + Roles.CMD_CHANNEL_GENERATE, + Roles.CMD_CHANNEL_VERIFY + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + byte[] token = verifyAuthorizedGenerateToken(testuser, password); String endpoint = getEndpoint(); URL url = new URL(endpoint + "/verify-token"); @@ -533,7 +677,7 @@ HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); - sendAuthorization(conn, "test-cmd-channel", "testpasswd"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setDoInput(true); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); @@ -543,9 +687,22 @@ } @Test - public void testTokenTimeout() throws IOException, InterruptedException { - - byte[] token = verifyGenerateToken(); + public void authorizedTokenTimeout() throws Exception { + String[] roleNames = new String[] { + Roles.CMD_CHANNEL_GENERATE, + Roles.CMD_CHANNEL_VERIFY + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); + byte[] token = verifyAuthorizedGenerateToken(testuser, password); Thread.sleep(700); // Timeout is set to 500ms for tests, 700ms should be enough for everybody. ;-) @@ -555,17 +712,30 @@ HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); - sendAuthorization(conn, "test-cmd-channel", "testpasswd"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setDoInput(true); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); out.write("client-token=fluff&token=" + URLEncoder.encode(Base64.encodeBase64String(token), "UTF-8")); out.flush(); - assertEquals(401, conn.getResponseCode()); + assertEquals(403, conn.getResponseCode()); } @Test - public void testVerifyNonExistentToken() throws IOException { + public void authorizedVerifyNonExistentToken() throws Exception { + String[] roleNames = new String[] { + Roles.CMD_CHANNEL_VERIFY + }; + String testuser = "testuser"; + String password = "testpassword"; + final LoginService loginService = new TestLoginService(testuser, password, roleNames); + port = FreePortFinder.findFreePort(new TryPort() { + + @Override + public void tryPort(int port) throws Exception { + startServer(port, loginService); + } + }); byte[] token = "fluff".getBytes(); @@ -575,22 +745,22 @@ HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); - sendAuthorization(conn, "test-cmd-channel", "testpasswd"); + sendAuthorization(conn, testuser, password); conn.setDoOutput(true); conn.setDoInput(true); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); out.write("client-token=fluff&token=" + URLEncoder.encode(Base64.encodeBase64String(token), "UTF-8")); out.flush(); - assertEquals(401, conn.getResponseCode()); + assertEquals(403, conn.getResponseCode()); } - private byte[] verifyGenerateToken() throws IOException { + private byte[] verifyAuthorizedGenerateToken(String username, String password) throws IOException { String endpoint = getEndpoint(); URL url = new URL(endpoint + "/generate-token"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); - sendAuthorization(conn, "test-cmd-channel", "testpasswd"); + sendAuthorization(conn, username, password); conn.setDoOutput(true); conn.setDoInput(true); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); @@ -610,5 +780,31 @@ } return token; } + + private static class TestLoginService extends MappedLoginService { + + private final String[] roleNames; + private final String username; + private final String password; + + private TestLoginService(String username, String password, + String[] roleNames) { + this.username = username; + this.password = password; + this.roleNames = roleNames; + } + + @Override + protected void loadUsers() throws IOException { + putUser(username, new Password(password), + roleNames); + } + + @Override + protected UserIdentity loadUser(String username) { + return new DefaultUserIdentity(null, null, + roleNames); + } + } } diff -r 7f100b629033 -r dff087c3c875 web/war/src/main/webapp/WEB-INF/web.xml --- a/web/war/src/main/webapp/WEB-INF/web.xml Fri May 03 12:39:38 2013 -0400 +++ b/web/war/src/main/webapp/WEB-INF/web.xml Fri Apr 19 14:09:10 2013 +0200 @@ -60,9 +60,7 @@ /* - thermostat-agent - thermostat-client - thermostat-cmd-channel + thermostat-realm @@ -72,13 +70,7 @@ - thermostat-agent - - - thermostat-client - - - thermostat-cmd-channel + thermostat-realm