Skip to content

Commit d630fdc

Browse files
committed
negative cache
fixes #3859
1 parent 19f90b6 commit d630fdc

File tree

3 files changed

+54
-15
lines changed

3 files changed

+54
-15
lines changed

plugins/src/main/java/opengrok/auth/entity/LdapUser.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
2222
*/
2323
package opengrok.auth.entity;
2424

@@ -46,7 +46,6 @@ public LdapUser() {
4646

4747
public LdapUser(String dn, Map<String, Set<String>> attrs) {
4848
this.dn = dn;
49-
5049
this.attributes = Objects.requireNonNullElseGet(attrs, HashMap::new);
5150
}
5251

plugins/src/main/java/opengrok/auth/plugin/LdapUserPlugin.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
package opengrok.auth.plugin;
2424

2525
import java.util.Arrays;
26+
import java.util.Collections;
2627
import java.util.HashMap;
2728
import java.util.HashSet;
2829
import java.util.Map;
@@ -35,6 +36,7 @@
3536
import opengrok.auth.plugin.entity.User;
3637
import opengrok.auth.plugin.ldap.AbstractLdapProvider;
3738
import opengrok.auth.plugin.ldap.LdapException;
39+
import org.jetbrains.annotations.NotNull;
3840
import org.opengrok.indexer.authorization.AuthorizationException;
3941
import org.opengrok.indexer.configuration.Group;
4042
import org.opengrok.indexer.configuration.Project;
@@ -52,6 +54,7 @@ public class LdapUserPlugin extends AbstractLdapPlugin {
5254
private static final Logger LOGGER = Logger.getLogger(LdapUserPlugin.class.getName());
5355

5456
static final String SESSION_ATTR = "opengrok-ldap-plugin-user";
57+
static final String NEGATIVE_CACHE_ATTR = "opengrok-ldap-plugin-user-invalid-user";
5558

5659
/**
5760
* List of configuration names.
@@ -135,9 +138,7 @@ protected boolean sessionExists(HttpServletRequest req) {
135138
*/
136139
String expandFilter(User user) {
137140
String filter = ldapFilter;
138-
139141
filter = expandUserFilter(user, filter);
140-
141142
filter = filter.replace("\\%", "%");
142143

143144
return filter;
@@ -170,19 +171,20 @@ public void fillSession(HttpServletRequest req, User user) {
170171
AbstractLdapProvider ldapProvider = getLdapProvider();
171172
try {
172173
AbstractLdapProvider.LdapSearchResult<Map<String, Set<String>>> res;
173-
if ((res = ldapProvider.lookupLdapContent(dn, expandedFilter,
174-
attrSet.toArray(new String[0]))) == null) {
174+
if ((res = ldapProvider.lookupLdapContent(dn, expandedFilter, attrSet.toArray(new String[0]))) == null) {
175175
LOGGER.log(Level.WARNING, "failed to get LDAP attributes ''{2}'' for user {0} " +
176176
"with filter ''{1}'' from LDAP provider {3}",
177177
new Object[]{user, expandedFilter, attrSet, getLdapProvider()});
178+
LdapUser ldapUser = new LdapUser(dn, null);
179+
ldapUser.setAttribute(NEGATIVE_CACHE_ATTR, Collections.singleton(null));
180+
updateSession(req, ldapUser);
178181
return;
179182
}
180183

181184
records = res.getAttrs();
182185
if (Boolean.FALSE.equals(useDN)) {
183186
dn = res.getDN();
184-
LOGGER.log(Level.FINEST, "got DN ''{0}'' for user {1}",
185-
new Object[]{dn, user});
187+
LOGGER.log(Level.FINEST, "got DN ''{0}'' for user {1}", new Object[]{dn, user});
186188
}
187189
} catch (LdapException ex) {
188190
throw new AuthorizationException(ex);
@@ -206,8 +208,7 @@ public void fillSession(HttpServletRequest req, User user) {
206208
userAttrSet.put(attrName, records.get(attrName));
207209
}
208210

209-
LOGGER.log(Level.FINEST, "DN for user {0} is ''{1}'' on {2}",
210-
new Object[]{user, dn, ldapProvider});
211+
LOGGER.log(Level.FINEST, "DN for user {0} is ''{1}'' on {2}", new Object[]{user, dn, ldapProvider});
211212
updateSession(req, new LdapUser(dn, userAttrSet));
212213
}
213214

@@ -217,7 +218,7 @@ public void fillSession(HttpServletRequest req, User user) {
217218
* @param req the request
218219
* @param user the new value for user
219220
*/
220-
void updateSession(HttpServletRequest req, LdapUser user) {
221+
void updateSession(@NotNull HttpServletRequest req, LdapUser user) {
221222
req.getSession().setAttribute(getSessionAttrName(), user);
222223
}
223224

@@ -229,13 +230,18 @@ private String getSessionAttrName() {
229230
return getSessionAttrName(instanceNum);
230231
}
231232

233+
private boolean checkUser(@NotNull HttpServletRequest request) {
234+
LdapUser ldapUser = (LdapUser) request.getSession().getAttribute(getSessionAttrName());
235+
return ldapUser != null && ldapUser.getAttribute(NEGATIVE_CACHE_ATTR) == null;
236+
}
237+
232238
@Override
233239
public boolean checkEntity(HttpServletRequest request, Project project) {
234-
return request.getSession().getAttribute(getSessionAttrName()) != null;
240+
return checkUser(request);
235241
}
236242

237243
@Override
238244
public boolean checkEntity(HttpServletRequest request, Group group) {
239-
return request.getSession().getAttribute(getSessionAttrName()) != null;
245+
return checkUser(request);
240246
}
241247
}

plugins/src/test/java/opengrok/auth/plugin/LdapUserPluginTest.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,24 @@
3737
import opengrok.auth.plugin.util.DummyHttpServletRequestLdap;
3838
import org.junit.jupiter.api.BeforeEach;
3939
import org.junit.jupiter.api.Test;
40+
import org.mockito.Mockito;
41+
import org.opengrok.indexer.configuration.Group;
42+
import org.opengrok.indexer.configuration.Project;
4043

4144
import static opengrok.auth.plugin.LdapUserPlugin.SESSION_ATTR;
42-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
4345
import static org.junit.jupiter.api.Assertions.assertEquals;
46+
import static org.junit.jupiter.api.Assertions.assertFalse;
4447
import static org.junit.jupiter.api.Assertions.assertNotNull;
48+
import static org.junit.jupiter.api.Assertions.assertSame;
4549
import static org.junit.jupiter.api.Assertions.assertThrows;
4650
import static org.mockito.ArgumentMatchers.any;
51+
import static org.mockito.ArgumentMatchers.anyBoolean;
52+
import static org.mockito.ArgumentMatchers.anyString;
53+
import static org.mockito.ArgumentMatchers.eq;
4754
import static org.mockito.ArgumentMatchers.isNull;
4855
import static org.mockito.Mockito.mock;
56+
import static org.mockito.Mockito.times;
57+
import static org.mockito.Mockito.verify;
4958
import static org.mockito.Mockito.when;
5059

5160
/**
@@ -111,7 +120,7 @@ void testFillSessionWithDnOff() throws LdapException {
111120
params.put(LdapUserPlugin.USE_DN, false);
112121
LdapUserPlugin plugin = new LdapUserPlugin();
113122
plugin.load(params, mockprovider);
114-
assertEquals(mockprovider, plugin.getLdapProvider());
123+
assertSame(mockprovider, plugin.getLdapProvider());
115124

116125
HttpServletRequest request = new DummyHttpServletRequestLdap();
117126
User user = new User("[email protected]", "id");
@@ -121,6 +130,31 @@ void testFillSessionWithDnOff() throws LdapException {
121130
assertEquals(dn, ((LdapUser) request.getSession().getAttribute(SESSION_ATTR)).getDn());
122131
}
123132

133+
@Test
134+
void testNegativeCache() throws LdapException {
135+
AbstractLdapProvider mockprovider = mock(LdapFacade.class);
136+
when(mockprovider.lookupLdapContent(isNull(), isNull(), any(String[].class))).thenReturn(null);
137+
138+
Map<String, Object> params = getParamsMap();
139+
params.put(LdapUserPlugin.ATTRIBUTES, "mail");
140+
params.put(LdapUserPlugin.USE_DN, false);
141+
LdapUserPlugin origPlugin = new LdapUserPlugin();
142+
LdapUserPlugin plugin = Mockito.spy(origPlugin);
143+
plugin.load(params, mockprovider);
144+
assertSame(mockprovider, plugin.getLdapProvider());
145+
146+
HttpServletRequest dummyRequest = new DummyHttpServletRequestLdap();
147+
User user = new User("[email protected]", "id");
148+
dummyRequest.setAttribute(UserPlugin.REQUEST_ATTR, new User("foo", "123"));
149+
plugin.fillSession(dummyRequest, user);
150+
151+
assertNotNull(dummyRequest.getSession().getAttribute(SESSION_ATTR));
152+
assertFalse(plugin.isAllowed(dummyRequest, new Project("foo")));
153+
assertFalse(plugin.isAllowed(dummyRequest, new Group("bar")));
154+
// Make sure that the session was filled so that the second call to isAllowed() did not fill it again.
155+
verify(plugin, times(2)).updateSession(eq(dummyRequest), anyString(), anyBoolean());
156+
}
157+
124158
@Test
125159
void testInstance() {
126160
Map<String, Object> params = getParamsMap();

0 commit comments

Comments
 (0)