Merge lp:~nwilliams/akiban-server/security-service-login-service into lp:~akiban-technologies/akiban-server/trunk

Proposed by Nathan Williams
Status: Merged
Approved by: Thomas Jones-Low
Approved revision: 2638
Merged at revision: 2634
Proposed branch: lp:~nwilliams/akiban-server/security-service-login-service
Merge into: lp:~akiban-technologies/akiban-server/trunk
Diff against target: 540 lines (+256/-118)
9 files modified
src/main/java/com/akiban/http/HttpConductorImpl.java (+8/-47)
src/main/java/com/akiban/http/SecurityServiceLoginService.java (+72/-0)
src/main/java/com/akiban/server/service/security/SecurityServiceImpl.java (+6/-4)
src/main/java/com/akiban/server/service/security/User.java (+17/-1)
src/main/resources/com/akiban/http/basic.properties (+0/-33)
src/main/resources/com/akiban/http/digest.properties (+0/-33)
src/main/resources/com/akiban/server/service/config/configuration-defaults.properties (+3/-0)
src/test/java/com/akiban/http/HttpThreadedLoginIT.java (+147/-0)
src/test/java/com/akiban/server/service/security/SecurityServiceIT.java (+3/-0)
To merge this branch: bzr merge lp:~nwilliams/akiban-server/security-service-login-service
Reviewer Review Type Date Requested Status
Thomas Jones-Low Approve
Review via email: mp+160229@code.launchpad.net

Description of the change

Replace SingleThreadedJDBCService with new one backed directly by the SecurityService.

The JDBCService is racey and doesn't work with the requirements of our EmbeddedJDBCService so just ditch it.

Expose more of the information off of the User (password hashes, all roles). It wasn't a direct concern, but the new LoginService also has a cache time so the performance should be identical.

The test failed quite consistently for the 5, 10, and 20 thread cases before the fix. Now passes as desired.

To post a comment you must log in.
2638. By Nathan Williams

Remove unneeded import

Revision history for this message
Thomas Jones-Low (tjoneslo) wrote :

Well, this will certainly make user process tracking simpler.
Otherwise looks as described.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/main/java/com/akiban/http/HttpConductorImpl.java'
--- src/main/java/com/akiban/http/HttpConductorImpl.java 2013-04-14 16:11:04 +0000
+++ src/main/java/com/akiban/http/HttpConductorImpl.java 2013-04-22 23:01:29 +0000
@@ -17,19 +17,18 @@
1717
18package com.akiban.http;18package com.akiban.http;
1919
20import com.akiban.server.error.AkibanInternalException;
21import com.akiban.server.service.Service;20import com.akiban.server.service.Service;
22import com.akiban.server.service.config.ConfigurationService;21import com.akiban.server.service.config.ConfigurationService;
23import com.akiban.server.service.monitor.MonitorService;22import com.akiban.server.service.monitor.MonitorService;
24import com.akiban.server.service.monitor.ServerMonitor;23import com.akiban.server.service.monitor.ServerMonitor;
25import com.akiban.server.service.security.SecurityService;24import com.akiban.server.service.security.SecurityService;
25import com.akiban.sql.embedded.EmbeddedJDBCService;
26import com.google.inject.Inject;26import com.google.inject.Inject;
27import org.eclipse.jetty.servlets.CrossOriginFilter;27import org.eclipse.jetty.servlets.CrossOriginFilter;
28import org.eclipse.jetty.util.security.Constraint;28import org.eclipse.jetty.util.security.Constraint;
29import org.eclipse.jetty.security.Authenticator;29import org.eclipse.jetty.security.Authenticator;
30import org.eclipse.jetty.security.ConstraintMapping;30import org.eclipse.jetty.security.ConstraintMapping;
31import org.eclipse.jetty.security.ConstraintSecurityHandler;31import org.eclipse.jetty.security.ConstraintSecurityHandler;
32import org.eclipse.jetty.security.JDBCLoginService;
33import org.eclipse.jetty.security.LoginService;32import org.eclipse.jetty.security.LoginService;
34import org.eclipse.jetty.security.authentication.BasicAuthenticator;33import org.eclipse.jetty.security.authentication.BasicAuthenticator;
35import org.eclipse.jetty.security.authentication.DigestAuthenticator;34import org.eclipse.jetty.security.authentication.DigestAuthenticator;
@@ -45,8 +44,6 @@
4544
46import javax.servlet.FilterRegistration;45import javax.servlet.FilterRegistration;
47import javax.servlet.ServletException;46import javax.servlet.ServletException;
48import java.io.IOException;
49import java.lang.reflect.Method;
50import java.util.Collections;47import java.util.Collections;
51import java.util.HashSet;48import java.util.HashSet;
52import java.util.Set;49import java.util.Set;
@@ -59,6 +56,7 @@
59 private static final String CONFIG_PORT_PROPERTY = CONFIG_HTTP_PREFIX + "port";56 private static final String CONFIG_PORT_PROPERTY = CONFIG_HTTP_PREFIX + "port";
60 private static final String CONFIG_SSL_PROPERTY = CONFIG_HTTP_PREFIX + "ssl";57 private static final String CONFIG_SSL_PROPERTY = CONFIG_HTTP_PREFIX + "ssl";
61 private static final String CONFIG_LOGIN_PROPERTY = CONFIG_HTTP_PREFIX + "login";58 private static final String CONFIG_LOGIN_PROPERTY = CONFIG_HTTP_PREFIX + "login";
59 private static final String CONFIG_LOGIN_CACHE_SECONDS = CONFIG_HTTP_PREFIX + "login_cache_seconds";
62 private static final String CONFIG_XORIGIN_PREFIX = CONFIG_HTTP_PREFIX + "cross_origin.";60 private static final String CONFIG_XORIGIN_PREFIX = CONFIG_HTTP_PREFIX + "cross_origin.";
63 private static final String CONFIG_XORIGIN_ENABLED = CONFIG_XORIGIN_PREFIX + "enabled";61 private static final String CONFIG_XORIGIN_ENABLED = CONFIG_XORIGIN_PREFIX + "enabled";
64 private static final String CONFIG_XORIGIN_ORIGINS = CONFIG_XORIGIN_PREFIX + "allowed_origins";62 private static final String CONFIG_XORIGIN_ORIGINS = CONFIG_XORIGIN_PREFIX + "allowed_origins";
@@ -152,6 +150,7 @@
152 String portProperty = configurationService.getProperty(CONFIG_PORT_PROPERTY);150 String portProperty = configurationService.getProperty(CONFIG_PORT_PROPERTY);
153 String sslProperty = configurationService.getProperty(CONFIG_SSL_PROPERTY);151 String sslProperty = configurationService.getProperty(CONFIG_SSL_PROPERTY);
154 String loginProperty = configurationService.getProperty(CONFIG_LOGIN_PROPERTY);152 String loginProperty = configurationService.getProperty(CONFIG_LOGIN_PROPERTY);
153 int loginCacheSeconds = Integer.parseInt(configurationService.getProperty(CONFIG_LOGIN_CACHE_SECONDS));
155 int portLocal;154 int portLocal;
156 boolean ssl;155 boolean ssl;
157 AuthenticationType login;156 AuthenticationType login;
@@ -210,20 +209,20 @@
210 localServer.setHandler(localHandlerList);209 localServer.setHandler(localHandlerList);
211 }210 }
212 else {211 else {
213 String resource;212 SecurityServiceLoginService.CredentialType credentialType;
214 Authenticator authenticator;213 Authenticator authenticator;
215 switch (login) {214 switch (login) {
216 case BASIC:215 case BASIC:
217 resource = "basic.properties";216 credentialType = SecurityServiceLoginService.CredentialType.BASIC;
218 authenticator = new BasicAuthenticator();217 authenticator = new BasicAuthenticator();
219 break;218 break;
220 case DIGEST:219 case DIGEST:
221 resource = "digest.properties";220 credentialType = SecurityServiceLoginService.CredentialType.DIGEST;
222 authenticator = new DigestAuthenticator();221 authenticator = new DigestAuthenticator();
223 break;222 break;
224 default:223 default:
225 assert false : "Unexpected authentication type " + login;224 assert false : "Unexpected authentication type " + login;
226 resource = null;225 credentialType = null;
227 authenticator = null;226 authenticator = null;
228 }227 }
229 Constraint constraint = new Constraint(authenticator.getAuthMethod(),228 Constraint constraint = new Constraint(authenticator.getAuthMethod(),
@@ -238,9 +237,7 @@
238 sh.setAuthenticator(authenticator);237 sh.setAuthenticator(authenticator);
239 sh.setConstraintMappings(Collections.singletonList(cm));238 sh.setConstraintMappings(Collections.singletonList(cm));
240239
241 LoginService loginService =240 LoginService loginService = new SecurityServiceLoginService(securityService, credentialType, loginCacheSeconds);
242 new SingleThreadJDBCLoginService(SecurityService.REALM,
243 HttpConductorImpl.class.getResource(resource).toString());
244 sh.setLoginService(loginService);241 sh.setLoginService(loginService);
245242
246 sh.setHandler(localHandlerList);243 sh.setHandler(localHandlerList);
@@ -315,42 +312,6 @@
315 return result;312 return result;
316 }313 }
317314
318 // Embedded JDBC is single-threaded, but login service assumes it
319 // is thread-safe. Also, it does not close its ResultSet, which
320 // leaves a transaction active. So just close connection around
321 // each use. Login service is already
322 // synchronized. Unfortunately, this needs a private method.
323 static class SingleThreadJDBCLoginService extends JDBCLoginService {
324 private Method closeMethod;
325
326 public SingleThreadJDBCLoginService(String name, String config)
327 throws IOException {
328 super(name, config);
329 try {
330 closeMethod = JDBCLoginService.class.getDeclaredMethod("closeConnection", null);
331 closeMethod.setAccessible(true);
332 }
333 catch (Exception ex) {
334 throw new AkibanInternalException("Cannot get JDBC close method", ex);
335 }
336 }
337
338 @Override
339 protected org.eclipse.jetty.server.UserIdentity loadUser(String username) {
340 try {
341 return super.loadUser(username);
342 }
343 finally {
344 try {
345 closeMethod.invoke(this);
346 }
347 catch (Exception ex) {
348 logger.warn("Cannot call JDBC close method", ex);
349 }
350 }
351 }
352 }
353
354 private class ConnectionMonitor implements ServerMonitor {315 private class ConnectionMonitor implements ServerMonitor {
355 public static final String SERVER_TYPE = "REST";316 public static final String SERVER_TYPE = "REST";
356 private final SelectChannelConnector connector;317 private final SelectChannelConnector connector;
357318
=== added file 'src/main/java/com/akiban/http/SecurityServiceLoginService.java'
--- src/main/java/com/akiban/http/SecurityServiceLoginService.java 1970-01-01 00:00:00 +0000
+++ src/main/java/com/akiban/http/SecurityServiceLoginService.java 2013-04-22 23:01:29 +0000
@@ -0,0 +1,72 @@
1/**
2 * Copyright (C) 2009-2013 Akiban Technologies, Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18package com.akiban.http;
19
20import java.util.List;
21
22import com.akiban.server.service.security.SecurityService;
23import com.akiban.server.service.security.User;
24import com.akiban.util.ArgumentValidation;
25import org.eclipse.jetty.security.MappedLoginService;
26import org.eclipse.jetty.server.UserIdentity;
27import org.eclipse.jetty.util.security.Credential;
28
29public class SecurityServiceLoginService extends MappedLoginService
30{
31 public enum CredentialType { BASIC, DIGEST }
32
33 private final SecurityService securityService;
34 private final CredentialType credentialType;
35 private final long cacheMillis;
36 private volatile long lastCachePurge;
37
38 public SecurityServiceLoginService(SecurityService securityService, CredentialType credentialType, int cacheSeconds) {
39 ArgumentValidation.isGTE("cacheSeconds", cacheSeconds, 0);
40 if(credentialType != CredentialType.BASIC && credentialType != CredentialType.DIGEST) {
41 throw new IllegalArgumentException("Unknown credential: " + credentialType);
42 }
43 this.securityService = securityService;
44 this.credentialType = credentialType;
45 this.cacheMillis = cacheSeconds * 1000;
46 }
47
48 @Override
49 public UserIdentity login(String username, Object credentials) {
50 long now = System.currentTimeMillis();
51 if((now - lastCachePurge) > cacheMillis) {
52 super._users.clear();
53 lastCachePurge = now;
54 }
55 return super.login(username, credentials);
56 }
57
58 @Override
59 protected void loadUsers() {
60 }
61
62 @Override
63 protected UserIdentity loadUser(String username) {
64 User user = securityService.getUser(username);
65 if(user != null) {
66 String password = (credentialType == CredentialType.BASIC) ? user.getBasicPassword() : user.getDigestPassword();
67 List<String> roles = user.getRoles();
68 return putUser(username, Credential.getCredential(password), roles.toArray(new String[roles.size()]));
69 }
70 return null;
71 }
72}
073
=== modified file 'src/main/java/com/akiban/server/service/security/SecurityServiceImpl.java'
--- src/main/java/com/akiban/server/service/security/SecurityServiceImpl.java 2013-03-27 10:45:17 +0000
+++ src/main/java/com/akiban/server/service/security/SecurityServiceImpl.java 2013-04-22 23:01:29 +0000
@@ -204,7 +204,7 @@
204 roles.add(rs1.getString(2));204 roles.add(rs1.getString(2));
205 }205 }
206 rs1.close();206 rs1.close();
207 return new User(rs.getInt(1), rs.getString(2), roles);207 return new User(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4), roles);
208 }208 }
209209
210 @Override210 @Override
@@ -212,12 +212,14 @@
212 int id;212 int id;
213 Connection conn = null;213 Connection conn = null;
214 PreparedStatement stmt = null;214 PreparedStatement stmt = null;
215 String basicPassword = basicPassword(password);
216 String digestPassword = digestPassword(name, password);
215 try {217 try {
216 conn = openConnection();218 conn = openConnection();
217 stmt = conn.prepareStatement(ADD_USER_SQL);219 stmt = conn.prepareStatement(ADD_USER_SQL);
218 stmt.setString(1, name);220 stmt.setString(1, name);
219 stmt.setString(2, basicPassword(password));221 stmt.setString(2, basicPassword);
220 stmt.setString(3, digestPassword(name, password));222 stmt.setString(3, digestPassword);
221 stmt.setString(4, md5Password(name, password));223 stmt.setString(4, md5Password(name, password));
222 int nrows = stmt.executeUpdate();224 int nrows = stmt.executeUpdate();
223 if (nrows != 1) {225 if (nrows != 1) {
@@ -250,7 +252,7 @@
250 finally {252 finally {
251 cleanup(conn, stmt);253 cleanup(conn, stmt);
252 }254 }
253 return new User(id, name, new ArrayList<>(roles));255 return new User(id, name, basicPassword, digestPassword, new ArrayList<>(roles));
254 }256 }
255257
256 @Override258 @Override
257259
=== modified file 'src/main/java/com/akiban/server/service/security/User.java'
--- src/main/java/com/akiban/server/service/security/User.java 2013-03-22 20:05:57 +0000
+++ src/main/java/com/akiban/server/service/security/User.java 2013-04-22 23:01:29 +0000
@@ -25,11 +25,15 @@
25{25{
26 private final int id;26 private final int id;
27 private final String name;27 private final String name;
28 private final String basicPassword;
29 private final String digestPassword;
28 private final List<String> roles;30 private final List<String> roles;
2931
30 protected User(int id, String name, List<String> roles) {32 protected User(int id, String name, String basicPassword, String digestPassword, List<String> roles) {
31 this.id = id;33 this.id = id;
32 this.name = name;34 this.name = name;
35 this.basicPassword = basicPassword;
36 this.digestPassword = digestPassword;
33 this.roles = roles;37 this.roles = roles;
34 }38 }
3539
@@ -42,6 +46,18 @@
42 return name;46 return name;
43 }47 }
4448
49 public String getBasicPassword() {
50 return basicPassword;
51 }
52
53 public String getDigestPassword() {
54 return digestPassword;
55 }
56
57 public List<String> getRoles() {
58 return roles;
59 }
60
45 public boolean hasRole(String role) {61 public boolean hasRole(String role) {
46 return roles.contains(role);62 return roles.contains(role);
47 }63 }
4864
=== removed file 'src/main/resources/com/akiban/http/basic.properties'
--- src/main/resources/com/akiban/http/basic.properties 2013-03-22 20:05:57 +0000
+++ src/main/resources/com/akiban/http/basic.properties 1970-01-01 00:00:00 +0000
@@ -1,33 +0,0 @@
1#
2# Copyright (C) 2009-2013 Akiban Technologies, Inc.
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16#
17
18# Driver doesn't matter (already loaded), but must have no-arg ctor.
19jdbcdriver=java.lang.Object
20url=jdbc:default:connection
21username=akiban
22password=akiban
23usertable=security_schema.users
24usertablekey=id
25usertableuserfield=name
26usertablepasswordfield=password_basic
27roletable=security_schema.roles
28roletablekey=id
29roletablerolefield=name
30userroletable=security_schema.user_roles
31userroletableuserkey=user_id
32userroletablerolekey=role_id
33cachetime=300
340
=== removed file 'src/main/resources/com/akiban/http/digest.properties'
--- src/main/resources/com/akiban/http/digest.properties 2013-03-22 20:05:57 +0000
+++ src/main/resources/com/akiban/http/digest.properties 1970-01-01 00:00:00 +0000
@@ -1,33 +0,0 @@
1#
2# Copyright (C) 2009-2013 Akiban Technologies, Inc.
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16#
17
18# Driver doesn't matter (already loaded), but must have no-arg ctor.
19jdbcdriver=java.lang.Object
20url=jdbc:default:connection
21username=akiban
22password=akiban
23usertable=security_schema.users
24usertablekey=id
25usertableuserfield=name
26usertablepasswordfield=password_digest
27roletable=security_schema.roles
28roletablekey=id
29roletablerolefield=name
30userroletable=security_schema.user_roles
31userroletableuserkey=user_id
32userroletablerolekey=role_id
33cachetime=300
340
=== modified file 'src/main/resources/com/akiban/server/service/config/configuration-defaults.properties'
--- src/main/resources/com/akiban/server/service/config/configuration-defaults.properties 2013-04-15 16:20:25 +0000
+++ src/main/resources/com/akiban/server/service/config/configuration-defaults.properties 2013-04-22 23:01:29 +0000
@@ -82,6 +82,9 @@
82akserver.http.ssl=false82akserver.http.ssl=false
83akserver.http.login=none83akserver.http.login=none
8484
85# How long to save credential look-ups
86akserver.http.login_cache_seconds = 300
87
85# If enabled, use a CrossOriginFilter and construct it with these defaults.88# If enabled, use a CrossOriginFilter and construct it with these defaults.
86akserver.http.cross_origin.enabled = true89akserver.http.cross_origin.enabled = true
87akserver.http.cross_origin.allowed_origins = *90akserver.http.cross_origin.allowed_origins = *
8891
=== added file 'src/test/java/com/akiban/http/HttpThreadedLoginIT.java'
--- src/test/java/com/akiban/http/HttpThreadedLoginIT.java 1970-01-01 00:00:00 +0000
+++ src/test/java/com/akiban/http/HttpThreadedLoginIT.java 2013-04-22 23:01:29 +0000
@@ -0,0 +1,147 @@
1/**
2 * Copyright (C) 2009-2013 Akiban Technologies, Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18package com.akiban.http;
19
20import com.akiban.rest.RestService;
21import com.akiban.rest.RestServiceImpl;
22import com.akiban.server.service.servicemanager.GuicedServiceManager;
23import com.akiban.server.test.it.ITBase;
24import com.akiban.sql.embedded.EmbeddedJDBCService;
25import com.akiban.sql.embedded.EmbeddedJDBCServiceImpl;
26
27import org.apache.http.HttpResponse;
28import org.apache.http.HttpStatus;
29import org.apache.http.client.HttpClient;
30import org.apache.http.client.methods.HttpGet;
31import org.apache.http.impl.client.DefaultHttpClient;
32import org.apache.http.util.EntityUtils;
33import org.junit.Test;
34import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36
37import static org.junit.Assert.*;
38
39import java.net.URI;
40import java.util.Collections;
41import java.util.HashMap;
42import java.util.Map;
43
44public class HttpThreadedLoginIT extends ITBase
45{
46 private static final Logger LOG = LoggerFactory.getLogger(HttpThreadedLoginIT.class);
47
48 @Override
49 protected GuicedServiceManager.BindingsConfigurationProvider serviceBindingsProvider() {
50 return super.serviceBindingsProvider()
51 .bindAndRequire(RestService.class, RestServiceImpl.class);
52 }
53
54 @Override
55 protected Map<String, String> startupConfigProperties() {
56 Map<String, String> properties = new HashMap<>();
57 properties.put("akserver.http.login", "basic");
58 return properties;
59 }
60
61 private static int openRestURL(String userInfo, int port, String path) throws Exception {
62 HttpClient client = new DefaultHttpClient();
63 URI uri = new URI("http", userInfo, "localhost", port, path, null, null);
64 HttpGet get = new HttpGet(uri);
65 HttpResponse response = client.execute(get);
66 int code = response.getStatusLine().getStatusCode();
67 EntityUtils.consume(response.getEntity());
68 client.getConnectionManager().shutdown();
69 return code;
70 }
71
72 @Test
73 public void oneThread() throws InterruptedException {
74 run(1);
75 }
76
77 @Test
78 public void fiveThreads() throws InterruptedException {
79 run(5);
80 }
81
82 @Test
83 public void tenThreads() throws InterruptedException {
84 run(10);
85 }
86
87 @Test
88 public void twentyThreads() throws InterruptedException {
89 run(20);
90 }
91
92
93 private void run(int count) throws InterruptedException {
94 final int port = serviceManager().getServiceByClass(HttpConductor.class).getPort();
95 final String context = serviceManager().getServiceByClass(RestService.class).getContextPath();
96 final String path = context + "/version";
97 final UncaughtHandler uncaughtHandler = new UncaughtHandler();
98
99 Thread threads[] = new Thread[count];
100 for(int i = 0; i < count; ++i) {
101 threads[i] = new Thread(new TestRunnable(port, path, i), "Thread"+i);
102 threads[i].setUncaughtExceptionHandler(uncaughtHandler);
103 }
104 for(int i = 0; i < count; ++i) {
105 threads[i].start();
106 }
107 for(int i = 0; i < count; ++i) {
108 threads[i].join();
109 }
110
111 for(Throwable entry : uncaughtHandler.uncaught.values()) {
112 LOG.error("Uncaught exception", entry);
113 }
114 assertEquals("uncaught exception count", 0, uncaughtHandler.uncaught.size());
115 }
116
117 private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
118 public final Map<Thread,Throwable> uncaught = Collections.synchronizedMap(new HashMap<Thread,Throwable>());
119
120 @Override
121 public void uncaughtException(Thread t, Throwable e) {
122 uncaught.put(t, e);
123 }
124 }
125
126 private static class TestRunnable implements Runnable {
127 private final int port;
128 private final String url;
129 private final int userNum;
130
131 public TestRunnable(int port, String url, int userNum) {
132 this.port = port;
133 this.url = url;
134 this.userNum = userNum;
135 }
136
137 @Override
138 public void run() {
139 String userInfo = String.format("user_%d:password", userNum);
140 try {
141 assertEquals(userInfo, HttpStatus.SC_UNAUTHORIZED, openRestURL(userInfo, port, url));
142 } catch(Exception e) {
143 throw new RuntimeException(e);
144 }
145 }
146 }
147}
0148
=== modified file 'src/test/java/com/akiban/server/service/security/SecurityServiceIT.java'
--- src/test/java/com/akiban/server/service/security/SecurityServiceIT.java 2013-04-07 07:28:15 +0000
+++ src/test/java/com/akiban/server/service/security/SecurityServiceIT.java 2013-04-22 23:01:29 +0000
@@ -101,6 +101,9 @@
101 assertNotNull("user found", user);101 assertNotNull("user found", user);
102 assertTrue("user has role", user.hasRole("rest-user"));102 assertTrue("user has role", user.hasRole("rest-user"));
103 assertFalse("user does not have role", user.hasRole("admin"));103 assertFalse("user does not have role", user.hasRole("admin"));
104 assertEquals("users roles", "[rest-user]", user.getRoles().toString());
105 assertEquals("user password basic", "MD5:5F4DCC3B5AA765D61D8327DEB882CF99", user.getBasicPassword());
106 assertEquals("user password digest", "MD5:740C53C6C7EE87EEEE489E8CE4116048", user.getDigestPassword());
104 }107 }
105108
106 @Test109 @Test

Subscribers

People subscribed via source and target branches