Skip to content

Commit 5265344

Browse files
committed
Made LdapUser class unneccessary by introducing AuthenticationContext
Signed-off-by: Nils Bandener <[email protected]>
1 parent a658e22 commit 5265344

25 files changed

+515
-760
lines changed

src/main/java/com/amazon/dlic/auth/ldap/LdapUser.java

Lines changed: 4 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,7 @@
1111

1212
package com.amazon.dlic.auth.ldap;
1313

14-
import java.io.IOException;
15-
import java.util.Collections;
16-
import java.util.HashMap;
17-
import java.util.Map;
18-
19-
import com.google.common.collect.ImmutableSet;
20-
21-
import org.opensearch.core.common.io.stream.StreamInput;
22-
import org.opensearch.core.common.io.stream.StreamOutput;
23-
import org.opensearch.security.auth.ldap.util.Utils;
24-
import org.opensearch.security.support.WildcardMatcher;
25-
import org.opensearch.security.user.AuthCredentials;
26-
import org.opensearch.security.user.User;
27-
28-
import org.ldaptive.LdapAttribute;
29-
import org.ldaptive.LdapEntry;
14+
import org.opensearch.security.user.serialized.User;
3015

3116
/**
3217
* This class intentionally remains in the com.amazon.dlic.auth.ldap package
@@ -42,78 +27,10 @@
4227
public class LdapUser extends User {
4328

4429
private static final long serialVersionUID = 1L;
45-
private final transient LdapEntry userEntry;
4630
private final String originalUsername;
4731

48-
public LdapUser(
49-
final String name,
50-
String originalUsername,
51-
final LdapEntry userEntry,
52-
final AuthCredentials credentials,
53-
int customAttrMaxValueLen,
54-
WildcardMatcher allowlistedCustomLdapAttrMatcher
55-
) {
56-
super(name, ImmutableSet.of(), credentials);
57-
this.originalUsername = originalUsername;
58-
this.userEntry = userEntry;
59-
Map<String, String> attributes = getCustomAttributesMap();
60-
// TODO
61-
attributes.putAll(extractLdapAttributes(originalUsername, userEntry, customAttrMaxValueLen, allowlistedCustomLdapAttrMatcher));
62-
}
63-
64-
public LdapUser(StreamInput in) throws IOException {
65-
super(in);
66-
userEntry = null;
67-
originalUsername = in.readString();
68-
}
69-
70-
/**
71-
* May return null because ldapEntry is transient
72-
*
73-
* @return ldapEntry or null if object was deserialized
74-
*/
75-
public LdapEntry getUserEntry() {
76-
return userEntry;
77-
}
78-
79-
public String getDn() {
80-
return userEntry.getDn();
81-
}
82-
83-
public String getOriginalUsername() {
84-
return originalUsername;
85-
}
86-
87-
public static Map<String, String> extractLdapAttributes(
88-
String originalUsername,
89-
final LdapEntry userEntry,
90-
int customAttrMaxValueLen,
91-
WildcardMatcher allowlistedCustomLdapAttrMatcher
92-
) {
93-
Map<String, String> attributes = new HashMap<>();
94-
attributes.put("ldap.original.username", originalUsername);
95-
attributes.put("ldap.dn", userEntry.getDn());
96-
97-
if (customAttrMaxValueLen > 0) {
98-
for (LdapAttribute attr : userEntry.getAttributes()) {
99-
if (attr != null && !attr.isBinary() && !attr.getName().toLowerCase().contains("password")) {
100-
final String val = Utils.getSingleStringValue(attr);
101-
// only consider attributes which are not binary and where its value is not
102-
// longer than customAttrMaxValueLen characters
103-
if (val != null && val.length() > 0 && val.length() <= customAttrMaxValueLen) {
104-
if (allowlistedCustomLdapAttrMatcher.test(attr.getName())) {
105-
attributes.put("attr.ldap." + attr.getName(), val);
106-
}
107-
}
108-
}
109-
}
110-
}
111-
return Collections.unmodifiableMap(attributes);
112-
}
113-
114-
@Override
115-
public void writeTo(StreamOutput out) throws IOException {
116-
super.writeTo(out);
117-
out.writeString(originalUsername);
32+
public LdapUser(org.opensearch.security.user.User user) {
33+
super(user);
34+
this.originalUsername = null;
11835
}
11936
}

src/main/java/org/opensearch/security/auth/AuthenticationBackend.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,10 @@ public interface AuthenticationBackend {
5959
* <p/>
6060
* Results of this method are normally cached so that we not need to query the backend for every authentication attempt.
6161
* <p/>
62-
* @param The credentials to be validated, never null
62+
* @param context The context of this authentication; contains the auth credentials
6363
* @return the authenticated User, never null
6464
* @throws OpenSearchSecurityException in case an authentication failure
6565
* (when credentials are incorrect, the user does not exist or the backend is not reachable)
6666
*/
67-
User authenticate(AuthCredentials credentials) throws OpenSearchSecurityException;
68-
67+
User authenticate(AuthenticationContext context) throws OpenSearchSecurityException;
6968
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*
8+
* Modifications Copyright OpenSearch Contributors. See
9+
* GitHub history for details.
10+
*/
11+
12+
package org.opensearch.security.auth;
13+
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
import java.util.Optional;
17+
18+
import org.opensearch.security.user.AuthCredentials;
19+
20+
/**
21+
* This class allows HTTPAuthenticators and authentication backends to provide context data to authorization backends.
22+
* This is especially useful for siblings of authentication backends and authorization backends (like LDAP authc and
23+
* LDAP authz) to pass data which is specific to the auth type (like the LDAP user entry).
24+
* <p>
25+
* This allows to abolish specialized sub-classes of the User object (like LdapUser).
26+
*/
27+
public class AuthenticationContext {
28+
private final AuthCredentials credentials;
29+
private final Map<Class<?>, Object> contextData = new HashMap<>();
30+
31+
public AuthenticationContext(AuthCredentials credentials) {
32+
this.credentials = credentials;
33+
}
34+
35+
public <T> void addContextData(Class<T> contextDataType, T contextDataObject) {
36+
this.contextData.put(contextDataType, contextDataObject);
37+
}
38+
39+
public <T> Optional<T> getContextData(Class<T> contextDataType) {
40+
@SuppressWarnings("unchecked")
41+
T result = (T) this.contextData.get(contextDataType);
42+
return Optional.ofNullable(result);
43+
}
44+
45+
public AuthCredentials getCredentials() {
46+
return credentials;
47+
}
48+
}

src/main/java/org/opensearch/security/auth/AuthorizationBackend.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
package org.opensearch.security.auth;
2828

2929
import org.opensearch.OpenSearchSecurityException;
30-
import org.opensearch.security.user.AuthCredentials;
3130
import org.opensearch.security.user.User;
3231

3332
/**
@@ -60,11 +59,11 @@ public interface AuthorizationBackend {
6059
* Add them by calling either {@code user.addRole()} or {@code user.addRoles()}
6160
* </P>
6261
* @param user The authenticated user to populate with backend roles, never null
63-
* @param credentials Credentials to authenticate to the authorization backend, maybe null.
62+
* @param context Context data specific to the request that is currently processed.
6463
* <em>This parameter is for future usage, currently always empty credentials are passed!</em>
6564
* @throws OpenSearchSecurityException in case when the authorization backend cannot be reached
6665
* or the {@code credentials} are insufficient to authenticate to the authorization backend.
6766
*/
68-
User addRoles(User user, AuthCredentials credentials) throws OpenSearchSecurityException;
67+
User addRoles(User user, AuthenticationContext context) throws OpenSearchSecurityException;
6968

7069
}

src/main/java/org/opensearch/security/auth/BackendRegistry.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,9 @@ public User call() throws Exception {
528528

529529
Optional<User> impersonatedUser = impersonationBackend.impersonate(user);
530530
if (impersonatedUser.isPresent()) {
531-
return authz(impersonatedUser.get(), null, authorizers); // no role cache because no miss here in case of noop
531+
AuthenticationContext context = new AuthenticationContext(new AuthCredentials(user.getName()));
532+
return authz(context, impersonatedUser.get(), null, authorizers); // no role cache because no miss here in case of
533+
// noop
532534
}
533535

534536
if (isDebugEnabled) {
@@ -545,7 +547,12 @@ public User call() throws Exception {
545547
}
546548
}
547549

548-
private User authz(User authenticatedUser, Cache<User, Set<String>> roleCache, final Set<AuthorizationBackend> authorizers) {
550+
private User authz(
551+
AuthenticationContext context,
552+
User authenticatedUser,
553+
Cache<User, Set<String>> roleCache,
554+
final Set<AuthorizationBackend> authorizers
555+
) {
549556

550557
if (authenticatedUser == null) {
551558
return authenticatedUser;
@@ -575,7 +582,7 @@ private User authz(User authenticatedUser, Cache<User, Set<String>> roleCache, f
575582
);
576583
}
577584

578-
authenticatedUser = ab.addRoles(authenticatedUser, new AuthCredentials(authenticatedUser.getName()));
585+
authenticatedUser = ab.addRoles(authenticatedUser, context);
579586
} catch (Exception e) {
580587
log.error("Cannot retrieve roles for {} from {} due to {}", authenticatedUser, ab.getType(), e.toString(), e);
581588
}
@@ -603,13 +610,16 @@ private User authcz(
603610
if (ac == null) {
604611
return null;
605612
}
613+
614+
AuthenticationContext context = new AuthenticationContext(ac);
615+
606616
try {
607617

608618
// noop backend configured and no authorizers
609619
// that mean authc and authz was completely done via HTTP (like JWT or PKI)
610620
if (authBackend.getClass() == NoOpAuthenticationBackend.class && authorizers.isEmpty()) {
611621
// no cache
612-
return authBackend.authenticate(ac);
622+
return authBackend.authenticate(context);
613623
}
614624

615625
return cache.get(ac, new Callable<User>() {
@@ -622,8 +632,8 @@ public User call() throws Exception {
622632
authBackend.getType()
623633
);
624634
}
625-
final User authenticatedUser = authBackend.authenticate(ac);
626-
return authz(authenticatedUser, roleCache, authorizers);
635+
final User authenticatedUser = authBackend.authenticate(context);
636+
return authz(context, authenticatedUser, roleCache, authorizers);
627637
}
628638
});
629639
} catch (Exception e) {

src/main/java/org/opensearch/security/auth/internal/InternalAuthenticationBackend.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
import org.opensearch.OpenSearchSecurityException;
4141
import org.opensearch.security.auth.AuthenticationBackend;
42+
import org.opensearch.security.auth.AuthenticationContext;
4243
import org.opensearch.security.auth.AuthorizationBackend;
4344
import org.opensearch.security.auth.ImpersonationBackend;
4445
import org.opensearch.security.hasher.PasswordHasher;
@@ -98,7 +99,8 @@ public boolean passwordMatchesHash(String hash, char[] array) {
9899
}
99100

100101
@Override
101-
public User authenticate(final AuthCredentials credentials) {
102+
public User authenticate(AuthenticationContext context) {
103+
AuthCredentials credentials = context.getCredentials();
102104

103105
boolean userExists;
104106

@@ -159,7 +161,7 @@ public String getType() {
159161
}
160162

161163
@Override
162-
public User addRoles(User user, AuthCredentials credentials) throws OpenSearchSecurityException {
164+
public User addRoles(User user, AuthenticationContext context) throws OpenSearchSecurityException {
163165
if (internalUsersModel == null) {
164166
throw new OpenSearchSecurityException(
165167
"Internal authentication backend not configured. May be OpenSearch Security is not initialized."

src/main/java/org/opensearch/security/auth/internal/NoOpAuthenticationBackend.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
import org.opensearch.common.settings.Settings;
3535
import org.opensearch.security.auth.AuthenticationBackend;
36+
import org.opensearch.security.auth.AuthenticationContext;
3637
import org.opensearch.security.user.AuthCredentials;
3738
import org.opensearch.security.user.User;
3839

@@ -48,7 +49,8 @@ public String getType() {
4849
}
4950

5051
@Override
51-
public User authenticate(final AuthCredentials credentials) {
52+
public User authenticate(AuthenticationContext context) {
53+
AuthCredentials credentials = context.getCredentials();
5254
return new User(
5355
credentials.getUsername(),
5456
ImmutableSet.copyOf(credentials.getBackendRoles()),

src/main/java/org/opensearch/security/auth/internal/NoOpAuthorizationBackend.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
import java.nio.file.Path;
3030

3131
import org.opensearch.common.settings.Settings;
32+
import org.opensearch.security.auth.AuthenticationContext;
3233
import org.opensearch.security.auth.AuthorizationBackend;
33-
import org.opensearch.security.user.AuthCredentials;
3434
import org.opensearch.security.user.User;
3535

3636
public class NoOpAuthorizationBackend implements AuthorizationBackend {
@@ -45,7 +45,7 @@ public String getType() {
4545
}
4646

4747
@Override
48-
public User addRoles(User user, AuthCredentials credentials) {
48+
public User addRoles(User user, AuthenticationContext context) {
4949
return user;
5050
}
5151
}

0 commit comments

Comments
 (0)