Skip to content

Commit f66c239

Browse files
jwomearahgklohr
authored andcommitted
Fixed a bug where the remote user operations cache always used the same key. (#2483)
1 parent 73d23b2 commit f66c239

File tree

3 files changed

+144
-42
lines changed

3 files changed

+144
-42
lines changed

web-services/security/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,21 @@
253253
<artifactId>weld-core-impl</artifactId>
254254
<scope>test</scope>
255255
</dependency>
256+
<dependency>
257+
<groupId>org.mockito</groupId>
258+
<artifactId>mockito-core</artifactId>
259+
<scope>test</scope>
260+
</dependency>
256261
<dependency>
257262
<groupId>org.springframework</groupId>
258263
<artifactId>spring-expression</artifactId>
259264
<scope>test</scope>
260265
</dependency>
266+
<dependency>
267+
<groupId>org.springframework</groupId>
268+
<artifactId>spring-test</artifactId>
269+
<scope>test</scope>
270+
</dependency>
261271
</dependencies>
262272
<build>
263273
<finalName>${project.artifactId}</finalName>

web-services/security/src/main/java/datawave/security/authorization/remote/RemoteUserOperationsImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public void init() {
5151
}
5252

5353
@Override
54-
@Cacheable(value = "getRemoteUser", key = "{#principal}", cacheManager = "remoteOperationsCacheManager")
54+
@Cacheable(value = "getRemoteUser", key = "{#currentUser}", cacheManager = "remoteOperationsCacheManager")
5555
public ProxiedUserDetails getRemoteUser(ProxiedUserDetails currentUser) throws AuthorizationException {
5656
log.info("Cache fault: Retrieving user for " + currentUser.getPrimaryUser().getDn());
5757
return UserOperations.super.getRemoteUser(currentUser);

web-services/security/src/test/java/datawave/security/authorization/remote/RemoteUserOperationsImplHttpTest.java

Lines changed: 133 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package datawave.security.authorization.remote;
22

33
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNotEquals;
45

56
import java.io.IOException;
67
import java.math.BigInteger;
@@ -9,33 +10,58 @@
910
import java.nio.charset.Charset;
1011
import java.security.KeyPair;
1112
import java.security.KeyPairGenerator;
13+
import java.security.NoSuchAlgorithmException;
1214
import java.security.PrivateKey;
1315
import java.security.SecureRandom;
16+
import java.security.cert.CertificateException;
1417
import java.security.cert.X509Certificate;
1518
import java.time.ZonedDateTime;
19+
import java.util.ArrayList;
1620
import java.util.Arrays;
17-
import java.util.HashMap;
21+
import java.util.Collections;
22+
import java.util.List;
1823

24+
import javax.enterprise.concurrent.ManagedExecutorService;
1925
import javax.security.auth.x500.X500Principal;
2026
import javax.ws.rs.core.MediaType;
2127

28+
import org.apache.accumulo.core.security.Authorizations;
2229
import org.apache.commons.io.IOUtils;
30+
import org.jboss.security.JSSESecurityDomain;
2331
import org.junit.After;
2432
import org.junit.Before;
2533
import org.junit.Test;
34+
import org.junit.runner.RunWith;
35+
import org.mockito.Mockito;
36+
import org.springframework.beans.factory.annotation.Autowired;
37+
import org.springframework.cache.Cache;
38+
import org.springframework.cache.CacheManager;
39+
import org.springframework.cache.annotation.EnableCaching;
40+
import org.springframework.cache.concurrent.ConcurrentMapCache;
41+
import org.springframework.cache.support.SimpleCacheManager;
42+
import org.springframework.context.annotation.Bean;
43+
import org.springframework.context.annotation.Configuration;
44+
import org.springframework.test.context.ContextConfiguration;
45+
import org.springframework.test.context.junit4.SpringRunner;
2646
import org.wildfly.security.x500.cert.X509CertificateBuilder;
2747

2848
import com.fasterxml.jackson.databind.ObjectMapper;
49+
import com.google.common.collect.Sets;
2950
import com.sun.net.httpserver.HttpExchange;
3051
import com.sun.net.httpserver.HttpHandler;
3152
import com.sun.net.httpserver.HttpServer;
3253

3354
import datawave.microservice.query.Query;
3455
import datawave.security.authorization.DatawavePrincipal;
56+
import datawave.security.authorization.DatawaveUser;
57+
import datawave.security.authorization.ProxiedUserDetails;
58+
import datawave.security.authorization.SubjectIssuerDNPair;
59+
import datawave.security.authorization.UserOperations;
3560
import datawave.security.util.DnUtils;
3661
import datawave.user.AuthorizationsListBase;
3762
import datawave.user.DefaultAuthorizationsList;
3863
import datawave.webservice.common.json.DefaultMapperDecorator;
64+
import datawave.webservice.common.json.ObjectMapperDecorator;
3965
import datawave.webservice.common.remote.TestJSSESecurityDomain;
4066
import datawave.webservice.dictionary.data.DataDictionaryBase;
4167
import datawave.webservice.dictionary.data.DescriptionBase;
@@ -54,46 +80,105 @@
5480
import datawave.webservice.result.FacetQueryResponseBase;
5581
import datawave.webservice.result.GenericResponse;
5682

83+
@RunWith(SpringRunner.class)
84+
@ContextConfiguration
5785
public class RemoteUserOperationsImplHttpTest {
5886

59-
private static final int keysize = 2048;
87+
@EnableCaching
88+
@Configuration
89+
static class Config {
90+
91+
@Bean
92+
public CacheManager remoteOperationsCacheManager() {
93+
SimpleCacheManager cacheManager = new SimpleCacheManager();
94+
List<Cache> caches = new ArrayList<Cache>();
95+
caches.add(new ConcurrentMapCache("listEffectiveAuthorizations"));
96+
caches.add(new ConcurrentMapCache("getRemoteUser"));
97+
cacheManager.setCaches(caches);
98+
return cacheManager;
99+
}
100+
101+
@Bean
102+
public ObjectMapperDecorator objectMapperDecorator() {
103+
return new DefaultMapperDecorator();
104+
}
60105

61-
private static final String commonName = "cn=www.test.us";
62-
private static final String alias = "tomcat";
63-
private static final char[] keyPass = "changeit".toCharArray();
106+
@Bean
107+
public ManagedExecutorService executorService() {
108+
return Mockito.mock(ManagedExecutorService.class);
109+
}
110+
111+
@Bean
112+
public JSSESecurityDomain jsseSecurityDomain() throws CertificateException, NoSuchAlgorithmException {
113+
String alias = "tomcat";
114+
char[] keyPass = "changeit".toCharArray();
115+
int keysize = 2048;
116+
String commonName = "cn=www.test.us";
117+
118+
KeyPairGenerator generater = KeyPairGenerator.getInstance("RSA");
119+
generater.initialize(keysize);
120+
KeyPair keypair = generater.generateKeyPair();
121+
PrivateKey privKey = keypair.getPrivate();
122+
final X509Certificate[] chain = new X509Certificate[1];
123+
X500Principal x500Principal = new X500Principal(commonName);
124+
final ZonedDateTime start = ZonedDateTime.now().minusWeeks(1);
125+
final ZonedDateTime until = start.plusYears(1);
126+
X509CertificateBuilder builder = new X509CertificateBuilder().setIssuerDn(x500Principal).setSerialNumber(new BigInteger(10, new SecureRandom()))
127+
.setNotValidBefore(start).setNotValidAfter(until).setSubjectDn(x500Principal).setPublicKey(keypair.getPublic())
128+
.setSigningKey(keypair.getPrivate()).setSignatureAlgorithmName("SHA256withRSA");
129+
chain[0] = builder.build();
130+
131+
return new TestJSSESecurityDomain(alias, privKey, keyPass, chain);
132+
}
133+
134+
@Bean
135+
public HttpServer server() throws IOException {
136+
HttpServer server = HttpServer.create(new InetSocketAddress(PORT), 0);
137+
server.setExecutor(null);
138+
server.start();
139+
return server;
140+
}
141+
142+
@Bean
143+
public RemoteUserOperationsImpl remote(HttpServer server) {
144+
// create a remote event query logic that has our own server behind it
145+
RemoteUserOperationsImpl remote = new RemoteUserOperationsImpl();
146+
remote.setQueryServiceURI("/Security/User/");
147+
remote.setQueryServiceScheme("http");
148+
remote.setQueryServiceHost("localhost");
149+
remote.setQueryServicePort(server.getAddress().getPort());
150+
remote.setResponseObjectFactory(new MockResponseObjectFactory());
151+
return remote;
152+
}
153+
}
64154

65-
private X500Principal x500Principal;
155+
private static final SubjectIssuerDNPair userDN = SubjectIssuerDNPair.of("userDn", "issuerDn");
156+
private static final SubjectIssuerDNPair otherUserDN = SubjectIssuerDNPair.of("otherUserDn", "issuerDn");
157+
private static Authorizations auths = new Authorizations("auth1", "auth2");
66158

67159
private static final int PORT = 0;
68160

161+
private final DatawaveUser user = new DatawaveUser(userDN, DatawaveUser.UserType.USER, Sets.newHashSet(auths.toString().split(",")), null, null, -1L);
162+
private final DatawavePrincipal principal = new DatawavePrincipal((Collections.singleton(user)));
163+
164+
private final DatawaveUser otherUser = new DatawaveUser(otherUserDN, DatawaveUser.UserType.USER, Sets.newHashSet(auths.toString().split(",")), null, null,
165+
-1L);
166+
private final DatawavePrincipal otherPrincipal = new DatawavePrincipal((Collections.singleton(otherUser)));
167+
168+
@Autowired
69169
private HttpServer server;
70170

71-
private RemoteUserOperationsImpl remote;
171+
@Autowired
172+
private UserOperations remote;
173+
174+
private DefaultAuthorizationsList listEffectiveAuthResponse;
72175

73176
@Before
74177
public void setup() throws Exception {
75178
final ObjectMapper objectMapper = new DefaultMapperDecorator().decorate(new ObjectMapper());
76179
System.setProperty(DnUtils.SUBJECT_DN_PATTERN_PROPERTY, ".*ou=server.*");
77-
KeyPairGenerator generater = KeyPairGenerator.getInstance("RSA");
78-
generater.initialize(keysize);
79-
KeyPair keypair = generater.generateKeyPair();
80-
PrivateKey privKey = keypair.getPrivate();
81-
final X509Certificate[] chain = new X509Certificate[1];
82-
x500Principal = new X500Principal(commonName);
83-
final ZonedDateTime start = ZonedDateTime.now().minusWeeks(1);
84-
final ZonedDateTime until = start.plusYears(1);
85-
X509CertificateBuilder builder = new X509CertificateBuilder().setIssuerDn(x500Principal).setSerialNumber(new BigInteger(10, new SecureRandom()))
86-
.setNotValidBefore(start).setNotValidAfter(until).setSubjectDn(x500Principal).setPublicKey(keypair.getPublic())
87-
.setSigningKey(keypair.getPrivate()).setSignatureAlgorithmName("SHA256withRSA");
88-
chain[0] = builder.build();
89-
90-
server = HttpServer.create(new InetSocketAddress(PORT), 0);
91-
server.setExecutor(null);
92-
server.start();
93-
94-
DefaultAuthorizationsList listEffectiveAuthResponse = new DefaultAuthorizationsList();
95-
listEffectiveAuthResponse.setUserAuths("testuserDn", "testissuerDn", Arrays.asList("auth1", "auth2"));
96-
listEffectiveAuthResponse.setAuthMapping(new HashMap<>());
180+
181+
setListEffectiveAuthResponse(userDN, auths);
97182

98183
HttpHandler listEffectiveAuthorizationsHandler = new HttpHandler() {
99184
@Override
@@ -122,17 +207,6 @@ public void handle(HttpExchange exchange) throws IOException {
122207

123208
server.createContext("/Security/User/listEffectiveAuthorizations", listEffectiveAuthorizationsHandler);
124209
server.createContext("/Security/User/flushCachedCredentials", flushHandler);
125-
126-
// create a remote event query logic that has our own server behind it
127-
remote = new RemoteUserOperationsImpl();
128-
remote.setQueryServiceURI("/Security/User/");
129-
remote.setQueryServiceScheme("http");
130-
remote.setQueryServiceHost("localhost");
131-
remote.setQueryServicePort(server.getAddress().getPort());
132-
remote.setExecutorService(null);
133-
remote.setObjectMapperDecorator(new DefaultMapperDecorator());
134-
remote.setResponseObjectFactory(new MockResponseObjectFactory());
135-
remote.setJsseSecurityDomain(new TestJSSESecurityDomain(alias, privKey, keyPass, chain));
136210
}
137211

138212
@After
@@ -142,15 +216,33 @@ public void after() {
142216
}
143217
}
144218

219+
private void setListEffectiveAuthResponse(SubjectIssuerDNPair userDN, Authorizations auths) {
220+
listEffectiveAuthResponse = new DefaultAuthorizationsList();
221+
listEffectiveAuthResponse.setUserAuths(userDN.subjectDN(), userDN.issuerDN(), Arrays.asList(auths.toString().split(",")));
222+
listEffectiveAuthResponse.addAuths(userDN.subjectDN(), userDN.issuerDN(), Arrays.asList(auths.toString().split(",")));
223+
}
224+
145225
@Test
146226
public void testRemoteUserOperations() throws Exception {
147-
DatawavePrincipal principal = new DatawavePrincipal(commonName);
148227

149-
AuthorizationsListBase auths = remote.listEffectiveAuthorizations(principal);
150-
assertEquals(2, auths.getAllAuths().size());
228+
AuthorizationsListBase returnedAuths = remote.listEffectiveAuthorizations(principal);
229+
assertEquals(2, returnedAuths.getAllAuths().size());
151230

152231
GenericResponse flush = remote.flushCachedCredentials(principal);
153232
assertEquals("test flush result", flush.getResult());
233+
234+
ProxiedUserDetails returnedUser = remote.getRemoteUser(principal);
235+
236+
// ensure that we get the cached user details
237+
ProxiedUserDetails dupeReturnedUser = remote.getRemoteUser(principal);
238+
assertEquals(returnedUser, dupeReturnedUser);
239+
240+
// setup the list effective auth response for the other user
241+
setListEffectiveAuthResponse(otherUserDN, auths);
242+
243+
// ensure that we get the other user details, not the cached user details
244+
ProxiedUserDetails newReturnedUser = remote.getRemoteUser(otherPrincipal);
245+
assertNotEquals(returnedUser, newReturnedUser);
154246
}
155247

156248
public static class MockResponseObjectFactory extends ResponseObjectFactory {

0 commit comments

Comments
 (0)