-
Notifications
You must be signed in to change notification settings - Fork 287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Api token authc/z implementation with Cache #4992
base: feature/api-tokens
Are you sure you want to change the base?
Conversation
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
…rmissions Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
originalSource = (new String(BaseEncoding.base64().decode((String) base64), StandardCharsets.UTF_8)); | ||
} else { | ||
originalSource = XContentHelper.convertToJson(originalResult.internalSourceRef(), false, XContentType.JSON); | ||
if (originalSource == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes in this file are to correct a mis-merge that happened in prior PRs to this feature branch.
Signed-off-by: Derek Ho <[email protected]>
src/main/java/org/opensearch/security/action/apitokens/ApiTokenIndexListenerCache.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/privileges/PrivilegesEvaluationContext.java
Outdated
Show resolved
Hide resolved
src/test/java/org/opensearch/security/action/apitokens/ApiTokenAuthenticatorTest.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
src/main/java/org/opensearch/security/privileges/PrivilegesEvaluationContext.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
src/main/java/org/opensearch/security/privileges/PrivilegesEvaluationContext.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/privileges/ActionPrivileges.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/authtoken/jwt/ApiTokenJwtVendor.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/authtoken/jwt/JwtVendor.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/action/apitokens/ApiTokenRepository.java
Show resolved
Hide resolved
…curity into auth Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
src/main/java/org/opensearch/security/authtoken/jwt/claims/JwtClaimsBuilder.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/privileges/PrivilegesEvaluationContext.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java
Outdated
Show resolved
Hide resolved
src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
} | ||
|
||
public PrivilegesEvaluatorResponse hasAnyClusterPrivilege(PrivilegesEvaluationContext context, Set<String> actions) { | ||
return cluster.providesAnyPrivilege(context, actions, context.getMappedRoles()); | ||
if (context instanceof RoleBasedPrivilegesEvaluationContext) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a good contender for polymorphism.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to avoid the if/else constructs which are very prominent in this PR, I would recommend to extract ActionPrivileges
as an interface or abstract super class, and have one implementation which deals with RoleBasedPrivilegesEvaluationContext
and another implementation which deals with PermissionBasedPrivilegesEvaluationContext
.
return ctxWithUserName("test-user", roles); | ||
} | ||
|
||
static RoleBasedPrivilegesEvaluationContext ctxWithUserName(String userName, String... roles) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the name ctxWithUserName()
be somehow refined? Considering that ctxForApiToken()
also accepts a userName
param, it is unclear by the name what the difference to ctxWithUserName()
is.
@@ -373,14 +373,14 @@ public void handleSearchContext(SearchContext searchContext, ThreadPool threadPo | |||
return; | |||
} | |||
|
|||
PrivilegesEvaluationContext privilegesEvaluationContext = this.dlsFlsBaseContext.getPrivilegesEvaluationContext(); | |||
if (privilegesEvaluationContext == null) { | |||
PrivilegesEvaluationContext PrivilegesEvaluationContext = this.dlsFlsBaseContext.getPrivilegesEvaluationContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: PrivilegesEvaluationContext
should be lower case
@@ -110,13 +110,13 @@ public SecurityFlsDlsIndexSearcherWrapper( | |||
protected DirectoryReader dlsFlsWrap(final DirectoryReader reader, boolean isAdmin) throws IOException { | |||
|
|||
final ShardId shardId = ShardUtils.extractShardId(reader); | |||
PrivilegesEvaluationContext privilegesEvaluationContext = this.dlsFlsBaseContext.getPrivilegesEvaluationContext(); | |||
PrivilegesEvaluationContext PrivilegesEvaluationContext = this.dlsFlsBaseContext.getPrivilegesEvaluationContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: PrivilegesEvaluationContext should be lower case
} | ||
|
||
public PrivilegesEvaluatorResponse hasAnyClusterPrivilege(PrivilegesEvaluationContext context, Set<String> actions) { | ||
return cluster.providesAnyPrivilege(context, actions, context.getMappedRoles()); | ||
if (context instanceof RoleBasedPrivilegesEvaluationContext) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to avoid the if/else constructs which are very prominent in this PR, I would recommend to extract ActionPrivileges
as an interface or abstract super class, and have one implementation which deals with RoleBasedPrivilegesEvaluationContext
and another implementation which deals with PermissionBasedPrivilegesEvaluationContext
.
} | ||
|
||
if (!indexHasAllPermissions) { | ||
return PrivilegesEvaluatorResponse.insufficient("Insufficient permissions for the index" + concreteIndex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error message does not indicate which permission is missing. It also does not mention any other potential indices which also might have missing permissions. Thus, it is not really user friendly.
I would recommend to use the CheckTable
class as it is also used by the other index privilege evaluation methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, the API contract for hasIndexPrivilege()
which calls this method defines that situations where there are privileges available for sub-sets of indices are reported with a isPartiallyOk()
response, combined with the set of available indices:
security/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java
Lines 165 to 179 in 472f7c7
/** | |
* Checks whether this instance provides privileges for the combination of the provided action, | |
* the provided indices and the provided roles. | |
* <p> | |
* Returns a PrivilegesEvaluatorResponse with allowed=true if privileges are available. | |
* <p> | |
* If privileges are only available for a sub-set of indices, isPartiallyOk() will return true | |
* and the indices for which privileges are available are returned by getAvailableIndices(). This allows the | |
* do_not_fail_on_forbidden behaviour. | |
*/ | |
public PrivilegesEvaluatorResponse hasIndexPrivilege( | |
PrivilegesEvaluationContext context, | |
Set<String> actions, | |
IndexResolverReplacer.Resolved resolvedIndices | |
) { |
This is necessary for do_not_fail_on_forbidden functionality. Without that behaviour, that functionality won't work correctly for API tokens.
@@ -127,6 +128,7 @@ public final static <T> SecurityDynamicConfiguration<T> addStatics(SecurityDynam | |||
private final Path configPath; | |||
private final InternalAuthenticationBackend iab; | |||
private final ClusterInfoHolder cih; | |||
private final ApiTokenRepository ar; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Despite the abbreviations that are used in the existing code, I would recommend to use non-abbreviated attribute names, as it strongly improves code readability.
new NoOpAuthenticationBackend(Settings.EMPTY, null), | ||
new ApiTokenAuthenticator(getDynamicApiTokenSettings(), this.cih.getClusterName(), ar), | ||
false, | ||
-2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a user has configured an auth domain with the priority -2, this will be removed in favor of the ApiTokenAuthenticator
. This might be unexpected behavior.
private final Map<String, Permissions> jtis = new ConcurrentHashMap<>(); | ||
|
||
void reloadApiTokensFromIndex() { | ||
Map<String, ApiToken> tokensFromIndex = apiTokenIndexHandler.getTokenMetadatas(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ApiTokenIndexHandler seems to do a actionGet
, i.e., a sync operation. Whenever we're on transport operations, I'd strongly recommend to prefer async operations using action listeners, as otherwise there's a slight deadlock risk due to thread pool exhaustion.
// First check if this permission applies to this index | ||
boolean indexMatched = false; | ||
for (String pattern : indexPermission.getIndexPatterns()) { | ||
if (WildcardMatcher.from(pattern).test(concreteIndex)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OpenSearch has the property that privileges on aliases also grant privileges on indices contained inside the alias. Similar properties hold for data streams and backing indices. I think this support is missing here.
|
||
@Override | ||
public ImmutableSet<String> getMappedRoles() { | ||
return ImmutableSet.of(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are quite a few dependencies on mapped roles.
- There's
ProtectedIndexEvaluator
which uses the settingplugins.security.protected_indices.roles
to define roles which can access protected indices PrivilegeEvaluator
uses mapped roles to realize the Dashboards multi tenancy implementation- User attributes exposes the mapped roles via
${user.securityRoles}
, thus they can be used in DLS statements
Is it anticipated that this functionality won't be available with API tokens?
Description
This PR implements authc/z for api tokens. For authc it is based on token validity for as well as presence in a cache, which listens to index and delete operations on the api token index. For Authz, it onboards onto action privileges class, where instead of returning not authorized for cluster and index privileges, requests go through a final api token check, where if the user is an api token, it will evaluate whether the permissions in the cache are sufficient to execute the request.
Issues Resolved
[List any issues this PR will resolve]
Is this a backport? If so, please add backport PR # and/or commits #, and remove
backport-failed
label from the original PR.Do these changes introduce new permission(s) to be displayed in the static dropdown on the front-end? If so, please open a draft PR in the security dashboards plugin and link the draft PR here
Testing
[Please provide details of testing done: unit testing, integration testing and manual testing]
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.