Merge "Configurable ldap.fetchMemberOfEagerly to optimize LDAP login"
This commit is contained in:
@@ -2390,6 +2390,16 @@ Directory servers.
|
|||||||
Default is unset for RFC 2307 servers (disabled)
|
Default is unset for RFC 2307 servers (disabled)
|
||||||
and `memberOf` for Active Directory.
|
and `memberOf` for Active Directory.
|
||||||
|
|
||||||
|
[[ldap.fetchMemberOfEagerly]]ldap.fetchMemberOfEagerly::
|
||||||
|
+
|
||||||
|
_(Optional)_ Whether to fetch the `memberOf` account attribute on
|
||||||
|
login. Setups which use LDAP for user authentication but don't make
|
||||||
|
use of the LDAP groups may benefit from setting this option to `false`
|
||||||
|
as this will result in a much faster LDAP login.
|
||||||
|
+
|
||||||
|
Default is unset for RFC 2307 servers (disabled) and `true` for
|
||||||
|
Active Directory.
|
||||||
|
|
||||||
[[ldap.groupBase]]ldap.groupBase::
|
[[ldap.groupBase]]ldap.groupBase::
|
||||||
+
|
+
|
||||||
Root of the tree containing all group objects. This is typically
|
Root of the tree containing all group objects. This is typically
|
||||||
|
|||||||
@@ -185,13 +185,20 @@ import javax.security.auth.login.LoginException;
|
|||||||
return ldapSchema;
|
return ldapSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
LdapQuery.Result findAccount(final Helper.LdapSchema schema,
|
LdapQuery.Result findAccount(Helper.LdapSchema schema,
|
||||||
final DirContext ctx, final String username) throws NamingException,
|
DirContext ctx, String username, boolean fetchMemberOf)
|
||||||
AccountException {
|
throws NamingException, AccountException {
|
||||||
final HashMap<String, String> params = new HashMap<>();
|
final HashMap<String, String> params = new HashMap<>();
|
||||||
params.put(LdapRealm.USERNAME, username);
|
params.put(LdapRealm.USERNAME, username);
|
||||||
|
|
||||||
for (LdapQuery accountQuery : schema.accountQueryList) {
|
List<LdapQuery> accountQueryList;
|
||||||
|
if (fetchMemberOf) {
|
||||||
|
accountQueryList = schema.accountWithMemberOfQueryList;
|
||||||
|
} else {
|
||||||
|
accountQueryList = schema.accountQueryList;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (LdapQuery accountQuery : accountQueryList) {
|
||||||
List<LdapQuery.Result> res = accountQuery.query(ctx, params);
|
List<LdapQuery.Result> res = accountQuery.query(ctx, params);
|
||||||
if (res.size() == 1) {
|
if (res.size() == 1) {
|
||||||
return res.get(0);
|
return res.get(0);
|
||||||
@@ -213,7 +220,7 @@ import javax.security.auth.login.LoginException;
|
|||||||
|
|
||||||
if (account == null) {
|
if (account == null) {
|
||||||
try {
|
try {
|
||||||
account = findAccount(schema, ctx, username);
|
account = findAccount(schema, ctx, username, false);
|
||||||
} catch (AccountException e) {
|
} catch (AccountException e) {
|
||||||
LdapRealm.log.warn("Account " + username +
|
LdapRealm.log.warn("Account " + username +
|
||||||
" not found, assuming empty group membership");
|
" not found, assuming empty group membership");
|
||||||
@@ -234,9 +241,9 @@ import javax.security.auth.login.LoginException;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (schema.accountMemberField != null) {
|
if (schema.accountMemberField != null) {
|
||||||
if (account == null) {
|
if (account == null || account.getAll(schema.accountMemberField) == null) {
|
||||||
try {
|
try {
|
||||||
account = findAccount(schema, ctx, username);
|
account = findAccount(schema, ctx, username, true);
|
||||||
} catch (AccountException e) {
|
} catch (AccountException e) {
|
||||||
LdapRealm.log.warn("Account " + username +
|
LdapRealm.log.warn("Account " + username +
|
||||||
" not found, assuming empty group membership");
|
" not found, assuming empty group membership");
|
||||||
@@ -311,6 +318,7 @@ import javax.security.auth.login.LoginException;
|
|||||||
final String accountMemberField;
|
final String accountMemberField;
|
||||||
final String[] accountMemberFieldArray;
|
final String[] accountMemberFieldArray;
|
||||||
final List<LdapQuery> accountQueryList;
|
final List<LdapQuery> accountQueryList;
|
||||||
|
final List<LdapQuery> accountWithMemberOfQueryList;
|
||||||
|
|
||||||
final List<String> groupBases;
|
final List<String> groupBases;
|
||||||
final SearchScope groupScope;
|
final SearchScope groupScope;
|
||||||
@@ -322,6 +330,7 @@ import javax.security.auth.login.LoginException;
|
|||||||
type = discoverLdapType(ctx);
|
type = discoverLdapType(ctx);
|
||||||
groupMemberQueryList = new ArrayList<>();
|
groupMemberQueryList = new ArrayList<>();
|
||||||
accountQueryList = new ArrayList<>();
|
accountQueryList = new ArrayList<>();
|
||||||
|
accountWithMemberOfQueryList = new ArrayList<>();
|
||||||
|
|
||||||
final Set<String> accountAtts = new HashSet<>();
|
final Set<String> accountAtts = new HashSet<>();
|
||||||
|
|
||||||
@@ -375,7 +384,6 @@ import javax.security.auth.login.LoginException;
|
|||||||
LdapRealm.optdef(config, "accountMemberField", type.accountMemberField());
|
LdapRealm.optdef(config, "accountMemberField", type.accountMemberField());
|
||||||
if (accountMemberField != null) {
|
if (accountMemberField != null) {
|
||||||
accountMemberFieldArray = new String[] {accountMemberField};
|
accountMemberFieldArray = new String[] {accountMemberField};
|
||||||
accountAtts.add(accountMemberField);
|
|
||||||
} else {
|
} else {
|
||||||
accountMemberFieldArray = null;
|
accountMemberFieldArray = null;
|
||||||
}
|
}
|
||||||
@@ -384,8 +392,15 @@ import javax.security.auth.login.LoginException;
|
|||||||
final String accountPattern =
|
final String accountPattern =
|
||||||
LdapRealm.reqdef(config, "accountPattern", type.accountPattern());
|
LdapRealm.reqdef(config, "accountPattern", type.accountPattern());
|
||||||
|
|
||||||
|
Set<String> accountWithMemberOfAtts;
|
||||||
|
if (accountMemberField != null) {
|
||||||
|
accountWithMemberOfAtts = new HashSet<>(accountAtts);
|
||||||
|
accountWithMemberOfAtts.add(accountMemberField);
|
||||||
|
} else {
|
||||||
|
accountWithMemberOfAtts = null;
|
||||||
|
}
|
||||||
for (String accountBase : LdapRealm.requiredList(config, "accountBase")) {
|
for (String accountBase : LdapRealm.requiredList(config, "accountBase")) {
|
||||||
final LdapQuery accountQuery =
|
LdapQuery accountQuery =
|
||||||
new LdapQuery(accountBase, accountScope, new ParameterizedString(
|
new LdapQuery(accountBase, accountScope, new ParameterizedString(
|
||||||
accountPattern), accountAtts);
|
accountPattern), accountAtts);
|
||||||
if (accountQuery.getParameters().isEmpty()) {
|
if (accountQuery.getParameters().isEmpty()) {
|
||||||
@@ -393,6 +408,13 @@ import javax.security.auth.login.LoginException;
|
|||||||
"No variables in ldap.accountPattern");
|
"No variables in ldap.accountPattern");
|
||||||
}
|
}
|
||||||
accountQueryList.add(accountQuery);
|
accountQueryList.add(accountQuery);
|
||||||
|
|
||||||
|
if (accountWithMemberOfAtts != null) {
|
||||||
|
LdapQuery accountWithMemberOfQuery =
|
||||||
|
new LdapQuery(accountBase, accountScope, new ParameterizedString(
|
||||||
|
accountPattern), accountWithMemberOfAtts);
|
||||||
|
accountWithMemberOfQueryList.add(accountWithMemberOfQuery);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class LdapAuthBackend implements AuthBackend {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final Helper.LdapSchema schema = helper.getSchema(ctx);
|
final Helper.LdapSchema schema = helper.getSchema(ctx);
|
||||||
final LdapQuery.Result m = helper.findAccount(schema, ctx, username);
|
final LdapQuery.Result m = helper.findAccount(schema, ctx, username, false);
|
||||||
|
|
||||||
if (authConfig.getAuthType() == AuthType.LDAP) {
|
if (authConfig.getAuthType() == AuthType.LDAP) {
|
||||||
// We found the user account, but we need to verify
|
// We found the user account, but we need to verify
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ public class LdapRealm extends AbstractRealm {
|
|||||||
private final EmailExpander emailExpander;
|
private final EmailExpander emailExpander;
|
||||||
private final LoadingCache<String, Optional<Account.Id>> usernameCache;
|
private final LoadingCache<String, Optional<Account.Id>> usernameCache;
|
||||||
private final Set<Account.FieldName> readOnlyAccountFields;
|
private final Set<Account.FieldName> readOnlyAccountFields;
|
||||||
|
private final boolean fetchMemberOfEagerly;
|
||||||
private final Config config;
|
private final Config config;
|
||||||
|
|
||||||
private final LoadingCache<String, Set<AccountGroup.UUID>> membershipCache;
|
private final LoadingCache<String, Set<AccountGroup.UUID>> membershipCache;
|
||||||
@@ -95,6 +96,8 @@ public class LdapRealm extends AbstractRealm {
|
|||||||
if (optdef(config, "accountSshUserName", "DEFAULT") != null) {
|
if (optdef(config, "accountSshUserName", "DEFAULT") != null) {
|
||||||
readOnlyAccountFields.add(Account.FieldName.USER_NAME);
|
readOnlyAccountFields.add(Account.FieldName.USER_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchMemberOfEagerly = optional(config, "fetchMemberOfEagerly", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SearchScope scope(final Config c, final String setting) {
|
static SearchScope scope(final Config c, final String setting) {
|
||||||
@@ -215,7 +218,8 @@ public class LdapRealm extends AbstractRealm {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final Helper.LdapSchema schema = helper.getSchema(ctx);
|
final Helper.LdapSchema schema = helper.getSchema(ctx);
|
||||||
final LdapQuery.Result m = helper.findAccount(schema, ctx, username);
|
final LdapQuery.Result m = helper.findAccount(schema, ctx, username,
|
||||||
|
fetchMemberOfEagerly);
|
||||||
|
|
||||||
if (authConfig.getAuthType() == AuthType.LDAP && !who.isSkipAuthentication()) {
|
if (authConfig.getAuthType() == AuthType.LDAP && !who.isSkipAuthentication()) {
|
||||||
// We found the user account, but we need to verify
|
// We found the user account, but we need to verify
|
||||||
@@ -244,7 +248,9 @@ public class LdapRealm extends AbstractRealm {
|
|||||||
// in the middle of authenticating the user, its likely we will
|
// in the middle of authenticating the user, its likely we will
|
||||||
// need to know what access rights they have soon.
|
// need to know what access rights they have soon.
|
||||||
//
|
//
|
||||||
membershipCache.put(username, helper.queryForGroups(ctx, username, m));
|
if (fetchMemberOfEagerly) {
|
||||||
|
membershipCache.put(username, helper.queryForGroups(ctx, username, m));
|
||||||
|
}
|
||||||
return who;
|
return who;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user