Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6ae7090
Merge branch 'main' of github.com:opensearch-project/security into HEAD
derek-ho Nov 14, 2024
3177c34
Scaffolding for POST/DELETE/GET api tokens calls (#4921)
derek-ho Dec 16, 2024
dacdae5
Adds JTI and expiration field support for API Tokens (#4967)
derek-ho Dec 20, 2024
e255e14
Merge branch 'main' of github.com:opensearch-project/security into HEAD
derek-ho Dec 20, 2024
190bfec
Merge branch 'main' of github.com:opensearch-project/security into HEAD
derek-ho Jan 17, 2025
79f0c46
Api token authc/z implementation with Cache (#4992)
derek-ho Feb 4, 2025
a8b4ac1
Subset of permissions check on creation (#5012)
derek-ho Feb 21, 2025
3776667
Merge branch 'main' of github.com:opensearch-project/security into HEAD
derek-ho Mar 11, 2025
8750e8b
Change API token index actions to use action listeners and limit to 1…
derek-ho Mar 24, 2025
7b8b069
Merge branch 'main' into feature/api-tokens-cwperx
cwperks May 22, 2025
12c0f9c
Fix naming
cwperks May 22, 2025
896e9e2
Use one PrivilegesEvaluatorContext
cwperks May 22, 2025
97db90d
Handle authz
cwperks May 22, 2025
362e67f
fix unit tests
cwperks May 22, 2025
fa98ae2
Fix tests
cwperks May 23, 2025
f3cd485
Merge branch 'main' into feature/api-tokens-cwperx
cwperks May 27, 2025
68107ff
Add integrationTests for API Token
cwperks May 27, 2025
f5b965a
Add more integration tests
cwperks May 27, 2025
ba93aa3
Add token prefix
cwperks May 28, 2025
e35d3ef
Merge branch 'main' into feature/api-tokens-cwperx
cwperks Jun 23, 2025
c028420
Rebase with main
cwperks Jun 23, 2025
109c1ef
Merge branch 'main' into feature/api-tokens-cwperx
cwperks Jun 25, 2025
3a71078
Fix compilation issues
cwperks Jun 25, 2025
dad7551
Add to CHANGELOG
cwperks Jun 25, 2025
30f4f6f
Merge branch 'main' into feature/api-tokens-cwperx
cwperks Jul 21, 2025
3f11a61
Merge branch 'main' into feature/api-tokens-cwperx
cwperks Aug 25, 2025
eb512e3
Address PR feedback
cwperks Aug 25, 2025
93a0e4f
Attempt to resolve conflicts
cwperks Nov 25, 2025
bdd7d7f
Fix unit test
cwperks Nov 26, 2025
6c45459
Merge branch 'main' into feature/api-tokens-cwperx
cwperks Mar 17, 2026
51c086f
Address PR feedback
cwperks Mar 17, 2026
cd47c36
Address comments
cwperks Mar 17, 2026
2780872
Use XContent parsing
cwperks Mar 17, 2026
720f72f
Address comments
cwperks Mar 17, 2026
c86b305
Fix test
cwperks Mar 17, 2026
64bee5f
Make API Tokens Opaque Strings
cwperks Mar 17, 2026
a0876e4
Update delete
cwperks Mar 17, 2026
78b5cbb
Merge branch 'main' into feature/api-tokens-cwperx
cwperks Mar 24, 2026
0d867ed
Implement soft-delete for token revocation
cwperks Mar 25, 2026
eacd135
spotlessApply
cwperks Mar 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Make security plugin aware of FIPS build param (-Pcrypto.standard=FIPS-140-3) ([#5952](https://github.com/opensearch-project/security/pull/5952))
- Hardens input validation for resource sharing APIs ([#5831](https://github.com/opensearch-project/security/pull/5831)
- Optimize getFieldFilter to only return a predicate when index has FLS restrictions for user ([#5777](https://github.com/opensearch-project/security/pull/5777))
- Introduce API Tokens with `cluster_permissions` and `index_permissions` directly associated with the token ([#5443](https://github.com/opensearch-project/security/pull/5443))
- Performance optimizations for building internal authorization data structures upon config updates ([#5988](https://github.com/opensearch-project/security/pull/5988))
- Make encryption_key optional for obo token authenticator ([#6017](https://github.com/opensearch-project/security/pull/6017)
- [Resource Sharing] Using custom action prefixes for sample resource plugin ([#6020](https://github.com/opensearch-project/security/pull/6020)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/
package org.opensearch.test.framework;

import java.io.IOException;

import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;

public class ApiTokenConfig implements ToXContentObject {
private Boolean enabled;

public ApiTokenConfig enabled(Boolean enabled) {
this.enabled = enabled;
return this;
}

@Override
public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException {
xContentBuilder.startObject();
xContentBuilder.field("enabled", enabled);
xContentBuilder.endObject();
return xContentBuilder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ public TestSecurityConfig onBehalfOf(OnBehalfOfConfig onBehalfOfConfig) {
return this;
}

public TestSecurityConfig apiToken(ApiTokenConfig apiTokenConfig) {
config.apiTokenConfig(apiTokenConfig);
return this;
}

public TestSecurityConfig authc(AuthcDomain authcDomain) {
config.authc(authcDomain);
return this;
Expand Down Expand Up @@ -268,6 +273,7 @@ public static class Config implements ToXContentObject {
private Boolean doNotFailOnForbidden;
private XffConfig xffConfig;
private OnBehalfOfConfig onBehalfOfConfig;
private ApiTokenConfig apiTokenConfig;
private Map<String, AuthcDomain> authcDomainMap = new LinkedHashMap<>();

private AuthFailureListeners authFailureListeners;
Expand All @@ -293,6 +299,11 @@ public Config onBehalfOfConfig(OnBehalfOfConfig onBehalfOfConfig) {
return this;
}

public Config apiTokenConfig(ApiTokenConfig apiTokenConfig) {
this.apiTokenConfig = apiTokenConfig;
return this;
}

public Config authc(AuthcDomain authcDomain) {
authcDomainMap.put(authcDomain.id, authcDomain);
return this;
Expand All @@ -317,6 +328,10 @@ public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params
xContentBuilder.field("on_behalf_of", onBehalfOfConfig);
}

if (apiTokenConfig != null) {
xContentBuilder.field("api_tokens", apiTokenConfig);
}

if (anonymousAuth || (xffConfig != null)) {
xContentBuilder.startObject("http");
xContentBuilder.field("anonymous_auth_enabled", anonymousAuth);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.opensearch.security.action.configupdate.ConfigUpdateResponse;
import org.opensearch.security.securityconf.impl.CType;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.test.framework.ApiTokenConfig;
import org.opensearch.test.framework.AuditConfiguration;
import org.opensearch.test.framework.AuthFailureListeners;
import org.opensearch.test.framework.AuthzDomain;
Expand Down Expand Up @@ -664,6 +665,11 @@ public Builder onBehalfOf(OnBehalfOfConfig onBehalfOfConfig) {
return this;
}

public Builder apiToken(ApiTokenConfig apiTokenConfig) {
testSecurityConfig.apiToken(apiTokenConfig);
return this;
}

public Builder loadConfigurationIntoIndex(boolean loadConfigurationIntoIndex) {
this.loadConfigurationIntoIndex = loadConfigurationIntoIndex;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import org.opensearch.common.util.BigArrays;
import org.opensearch.common.util.PageCacheRecycler;
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.core.action.ActionListener;
import org.opensearch.core.action.ActionResponse;
import org.opensearch.core.common.io.stream.NamedWriteableRegistry;
import org.opensearch.core.index.Index;
Expand Down Expand Up @@ -133,6 +134,10 @@
import org.opensearch.search.internal.SearchContext;
import org.opensearch.search.query.QuerySearchResult;
import org.opensearch.secure_sm.AccessController;
import org.opensearch.security.action.apitokens.ApiTokenAction;
import org.opensearch.security.action.apitokens.ApiTokenRepository;
import org.opensearch.security.action.apitokens.ApiTokenUpdateAction;
import org.opensearch.security.action.apitokens.TransportApiTokenUpdateAction;
import org.opensearch.security.action.configupdate.ConfigUpdateAction;
import org.opensearch.security.action.configupdate.TransportConfigUpdateAction;
import org.opensearch.security.action.onbehalf.CreateOnBehalfOfTokenAction;
Expand Down Expand Up @@ -279,6 +284,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin
private volatile UserService userService;
private volatile RestLayerPrivilegesEvaluator restLayerEvaluator;
private volatile ConfigurationRepository cr;
private volatile ApiTokenRepository apiTokenRepository;
private volatile AdminDNs adminDns;
private volatile ClusterService cs;
private volatile AtomicReference<DiscoveryNode> localNode = new AtomicReference<>();
Expand Down Expand Up @@ -682,6 +688,22 @@ public List<RestHandler> getRestHandlers(
)
);
handlers.add(new CreateOnBehalfOfTokenAction(tokenManager));
handlers.add(
new ApiTokenAction(
Objects.requireNonNull(threadPool),
cr,
privilegesConfiguration,
settings,
adminDns,
auditLog,
configPath,
principalExtractor,
apiTokenRepository,
cs,
indexNameExpressionResolver,
roleMapper
)
);
handlers.addAll(
SecurityRestApiActions.getHandler(
settings,
Expand Down Expand Up @@ -741,6 +763,7 @@ public UnaryOperator<RestHandler> getRestHandlerWrapper(final ThreadContext thre
List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> actions = new ArrayList<>(1);
if (!disabled && !SSLConfig.isSslOnlyMode()) {
actions.add(new ActionHandler<>(ConfigUpdateAction.INSTANCE, TransportConfigUpdateAction.class));
actions.add(new ActionHandler<>(ApiTokenUpdateAction.INSTANCE, TransportApiTokenUpdateAction.class));
// external storage does not support reload and does not provide SSL certs info
if (!ExternalSecurityKeyStore.hasExternalSslContext(settings)) {
actions.add(new ActionHandler<>(CertificatesActionType.INSTANCE, TransportCertificatesInfoNodesAction.class));
Expand Down Expand Up @@ -1211,6 +1234,7 @@ public Collection<Object> createComponents(
);
this.roleMapper = roleMapper;
tokenManager = new SecurityTokenManager(cs, threadPool, userService, roleMapper);
apiTokenRepository = new ApiTokenRepository(localClient, clusterService);

PrivilegesConfiguration privilegesConfiguration = new PrivilegesConfiguration(
cr,
Expand All @@ -1224,7 +1248,8 @@ public Collection<Object> createComponents(
settings,
cih::getReasonForUnavailability,
irr,
xContentRegistry
xContentRegistry,
apiTokenRepository
);
this.privilegesConfiguration = privilegesConfiguration;

Expand Down Expand Up @@ -1305,7 +1330,7 @@ public Collection<Object> createComponents(
configPath,
compatConfig
);
dcf = new DynamicConfigFactory(cr, settings, configPath, localClient, threadPool, cih, passwordHasher);
dcf = new DynamicConfigFactory(cr, settings, configPath, localClient, threadPool, cih, passwordHasher, apiTokenRepository);
dcf.registerDCFListener(backendRegistry);
dcf.registerDCFListener(compatConfig);
dcf.registerDCFListener(irr);
Expand Down Expand Up @@ -1356,6 +1381,7 @@ public Collection<Object> createComponents(
components.add(dcf);
components.add(userService);
components.add(passwordHasher);
components.add(apiTokenRepository);

components.add(sslSettingsManager);
if (isSslCertReloadEnabled(settings) && sslCertificatesHotReloadEnabled(settings)) {
Expand Down Expand Up @@ -2335,6 +2361,14 @@ public void onNodeStarted(DiscoveryNode localNode) {
this.localNode.set(localNode);
if (!SSLConfig.isSslOnlyMode() && !client && !disabled && !useClusterStateToInitSecurityConfig(settings)) {
cr.initOnNodeStart();
if (apiTokenRepository != null) {
apiTokenRepository.reloadApiTokensFromIndex(
ActionListener.wrap(
unused -> log.debug("API tokens loaded on node start"),
e -> log.warn("Failed to load API tokens on node start", e)
)
);
}
}

// resourceSharingIndexManagementRepository will be null when sec plugin is disabled or is in SSLOnly mode, hence it will not be
Expand Down Expand Up @@ -2412,8 +2446,13 @@ public Collection<SystemIndexDescriptor> getSystemIndexDescriptors(Settings sett
ConfigConstants.SECURITY_CONFIG_INDEX_NAME,
ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX
);
final SystemIndexDescriptor apiTokenSystemIndexDescriptor = new SystemIndexDescriptor(
ConfigConstants.OPENSEARCH_API_TOKENS_INDEX,
"Security API token index"
);
final SystemIndexDescriptor securityIndexDescriptor = new SystemIndexDescriptor(indexPattern, "Security index");
systemIndexDescriptors.add(securityIndexDescriptor);
systemIndexDescriptors.add(apiTokenSystemIndexDescriptor);

for (String resourceIndex : resourcePluginInfo.getResourceIndices()) {
final SystemIndexDescriptor resourceSharingIndexDescriptor = new SystemIndexDescriptor(
Expand Down
Loading
Loading