|
13 | 13 | import org.elasticsearch.common.cache.CacheBuilder; |
14 | 14 | import org.elasticsearch.common.unit.TimeValue; |
15 | 15 | import org.elasticsearch.common.util.concurrent.ThreadContext; |
| 16 | +import org.elasticsearch.license.XPackLicenseState; |
16 | 17 | import org.elasticsearch.threadpool.ThreadPool; |
17 | 18 | import org.elasticsearch.xpack.core.security.authc.AuthenticationResult; |
18 | 19 | import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; |
|
21 | 22 | import org.elasticsearch.xpack.core.security.authc.kerberos.KerberosRealmSettings; |
22 | 23 | import org.elasticsearch.xpack.core.security.user.User; |
23 | 24 | import org.elasticsearch.xpack.security.authc.support.CachingRealm; |
| 25 | +import org.elasticsearch.xpack.security.authc.support.DelegatedAuthorizationSupport; |
24 | 26 | import org.elasticsearch.xpack.security.authc.support.UserRoleMapper; |
25 | 27 | import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore; |
26 | 28 | import org.ietf.jgss.GSSException; |
@@ -63,6 +65,7 @@ public final class KerberosRealm extends Realm implements CachingRealm { |
63 | 65 | private final Path keytabPath; |
64 | 66 | private final boolean enableKerberosDebug; |
65 | 67 | private final boolean removeRealmName; |
| 68 | + private DelegatedAuthorizationSupport delegatedRealms; |
66 | 69 |
|
67 | 70 | public KerberosRealm(final RealmConfig config, final NativeRoleMappingStore nativeRoleMappingStore, final ThreadPool threadPool) { |
68 | 71 | this(config, nativeRoleMappingStore, new KerberosTicketValidator(), threadPool, null); |
@@ -100,6 +103,15 @@ public KerberosRealm(final RealmConfig config, final NativeRoleMappingStore nati |
100 | 103 | } |
101 | 104 | this.enableKerberosDebug = KerberosRealmSettings.SETTING_KRB_DEBUG_ENABLE.get(config.settings()); |
102 | 105 | this.removeRealmName = KerberosRealmSettings.SETTING_REMOVE_REALM_NAME.get(config.settings()); |
| 106 | + this.delegatedRealms = null; |
| 107 | + } |
| 108 | + |
| 109 | + @Override |
| 110 | + public void initialize(Iterable<Realm> realms, XPackLicenseState licenseState) { |
| 111 | + if (delegatedRealms != null) { |
| 112 | + throw new IllegalStateException("Realm has already been initialized"); |
| 113 | + } |
| 114 | + delegatedRealms = new DelegatedAuthorizationSupport(realms, config, licenseState); |
103 | 115 | } |
104 | 116 |
|
105 | 117 | @Override |
@@ -133,13 +145,14 @@ public AuthenticationToken token(final ThreadContext context) { |
133 | 145 |
|
134 | 146 | @Override |
135 | 147 | public void authenticate(final AuthenticationToken token, final ActionListener<AuthenticationResult> listener) { |
| 148 | + assert delegatedRealms != null : "Realm has not been initialized correctly"; |
136 | 149 | assert token instanceof KerberosAuthenticationToken; |
137 | 150 | final KerberosAuthenticationToken kerbAuthnToken = (KerberosAuthenticationToken) token; |
138 | 151 | kerberosTicketValidator.validateTicket((byte[]) kerbAuthnToken.credentials(), keytabPath, enableKerberosDebug, |
139 | 152 | ActionListener.wrap(userPrincipalNameOutToken -> { |
140 | 153 | if (userPrincipalNameOutToken.v1() != null) { |
141 | 154 | final String username = maybeRemoveRealmName(userPrincipalNameOutToken.v1()); |
142 | | - buildUser(username, userPrincipalNameOutToken.v2(), listener); |
| 155 | + resolveUser(username, userPrincipalNameOutToken.v2(), listener); |
143 | 156 | } else { |
144 | 157 | /** |
145 | 158 | * This is when security context could not be established may be due to ongoing |
@@ -189,35 +202,36 @@ private void handleException(Exception e, final ActionListener<AuthenticationRes |
189 | 202 | } |
190 | 203 | } |
191 | 204 |
|
192 | | - private void buildUser(final String username, final String outToken, final ActionListener<AuthenticationResult> listener) { |
| 205 | + private void resolveUser(final String username, final String outToken, final ActionListener<AuthenticationResult> listener) { |
193 | 206 | // if outToken is present then it needs to be communicated with peer, add it to |
194 | 207 | // response header in thread context. |
195 | 208 | if (Strings.hasText(outToken)) { |
196 | 209 | threadPool.getThreadContext().addResponseHeader(WWW_AUTHENTICATE, NEGOTIATE_AUTH_HEADER_PREFIX + outToken); |
197 | 210 | } |
198 | | - final User user = (userPrincipalNameToUserCache != null) ? userPrincipalNameToUserCache.get(username) : null; |
199 | | - if (user != null) { |
200 | | - /** |
201 | | - * TODO: bizybot If authorizing realms configured, resolve user from those |
202 | | - * realms and then return. |
203 | | - */ |
204 | | - listener.onResponse(AuthenticationResult.success(user)); |
| 211 | + |
| 212 | + if (delegatedRealms.hasDelegation()) { |
| 213 | + delegatedRealms.resolve(username, listener); |
205 | 214 | } else { |
206 | | - /** |
207 | | - * TODO: bizybot If authorizing realms configured, resolve user from those |
208 | | - * realms, cache it and then return. |
209 | | - */ |
210 | | - final UserRoleMapper.UserData userData = new UserRoleMapper.UserData(username, null, Collections.emptySet(), null, this.config); |
211 | | - userRoleMapper.resolveRoles(userData, ActionListener.wrap(roles -> { |
212 | | - final User computedUser = new User(username, roles.toArray(new String[roles.size()]), null, null, null, true); |
213 | | - if (userPrincipalNameToUserCache != null) { |
214 | | - userPrincipalNameToUserCache.put(username, computedUser); |
215 | | - } |
216 | | - listener.onResponse(AuthenticationResult.success(computedUser)); |
217 | | - }, listener::onFailure)); |
| 215 | + final User user = (userPrincipalNameToUserCache != null) ? userPrincipalNameToUserCache.get(username) : null; |
| 216 | + if (user != null) { |
| 217 | + listener.onResponse(AuthenticationResult.success(user)); |
| 218 | + } else { |
| 219 | + buildUser(username, listener); |
| 220 | + } |
218 | 221 | } |
219 | 222 | } |
220 | 223 |
|
| 224 | + private void buildUser(final String username, final ActionListener<AuthenticationResult> listener) { |
| 225 | + final UserRoleMapper.UserData userData = new UserRoleMapper.UserData(username, null, Collections.emptySet(), null, this.config); |
| 226 | + userRoleMapper.resolveRoles(userData, ActionListener.wrap(roles -> { |
| 227 | + final User computedUser = new User(username, roles.toArray(new String[roles.size()]), null, null, null, true); |
| 228 | + if (userPrincipalNameToUserCache != null) { |
| 229 | + userPrincipalNameToUserCache.put(username, computedUser); |
| 230 | + } |
| 231 | + listener.onResponse(AuthenticationResult.success(computedUser)); |
| 232 | + }, listener::onFailure)); |
| 233 | + } |
| 234 | + |
221 | 235 | @Override |
222 | 236 | public void lookupUser(final String username, final ActionListener<User> listener) { |
223 | 237 | listener.onResponse(null); |
|
0 commit comments