Skip to content
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

Introduce Application Scopes for Extension Management #7850

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
493e6fd
Initial thoughts
peternied May 8, 2023
32334f4
Compilable
peternied May 8, 2023
069face
Basic scope checkpoints, need followup on how checks are performed
peternied May 8, 2023
246177c
Plumbing for scope checking
peternied May 9, 2023
5f08318
Example of protection for an extension point
peternied May 12, 2023
5257d8a
More scoping coverage
peternied May 12, 2023
21e2e48
Remove how extensions identiity is related to scope access
peternied May 12, 2023
089e19a
Match main
stephen-crawford May 31, 2023
7eadf07
Assign all action scopes
stephen-crawford Jun 2, 2023
6b44985
Fix build
stephen-crawford Jun 2, 2023
2c88559
fix test failures
stephen-crawford Jun 2, 2023
23b923d
add basic scope tests
stephen-crawford Jun 5, 2023
06d57d5
spotless
stephen-crawford Jun 5, 2023
dd4a093
Tests pass
stephen-crawford Jun 5, 2023
52b4640
Update actions
stephen-crawford Jun 6, 2023
856eb01
Application aware subject to encapsulate scope checks inside of OpenS…
peternied Jun 6, 2023
44cffb3
Update scopes
stephen-crawford Jun 13, 2023
635c9b5
update actions
stephen-crawford Jun 14, 2023
72393fe
fix painless
stephen-crawford Jun 14, 2023
402b443
fix change
stephen-crawford Jun 14, 2023
8ac10db
Fix updates
stephen-crawford Jun 14, 2023
5906c1c
Fix action swaps
stephen-crawford Jun 14, 2023
6db77bd
fix repsonse
stephen-crawford Jun 14, 2023
70c1960
fix repsonse
stephen-crawford Jun 14, 2023
e46a14b
fix repsonse
stephen-crawford Jun 14, 2023
37e3da2
fix repsonse
stephen-crawford Jun 14, 2023
38e0739
fix repsonse
stephen-crawford Jun 14, 2023
b16ee2c
reset actions
stephen-crawford Jun 14, 2023
1570c32
reset actions
stephen-crawford Jun 14, 2023
e05ee6b
initial fixes
stephen-crawford Jun 14, 2023
d13cbe8
Update scope definitions
stephen-crawford Jun 15, 2023
3cc021b
optimize
stephen-crawford Jun 19, 2023
8efb62b
Add ApplicationService
stephen-crawford Jun 19, 2023
8beb430
Update scopes
stephen-crawford Jun 20, 2023
a1326f3
Fix precommit
stephen-crawford Jun 20, 2023
6f63f6e
Add javadocs
stephen-crawford Jun 20, 2023
b860705
add pacakge info
stephen-crawford Jun 20, 2023
d41c52a
Pass tests
stephen-crawford Jun 21, 2023
779a43e
remove debug
stephen-crawford Jun 21, 2023
60465e5
Update shiro
stephen-crawford Jun 22, 2023
8f5539a
Update CHANGELOG.md
stephen-crawford Jun 22, 2023
c1c3d28
Update Scopes
stephen-crawford Jun 23, 2023
836b564
Refactor
stephen-crawford Jun 23, 2023
05282af
Update extension manager
stephen-crawford Jun 23, 2023
317ff2a
Finish action scopes refactor
stephen-crawford Jun 23, 2023
4bd34e2
Add outline of extension point test
stephen-crawford Jun 26, 2023
34a4b00
Remove extra exception
stephen-crawford Jun 26, 2023
131f6b7
ActionScopetests pass
stephen-crawford Jun 27, 2023
443ff0c
Pas all tests
stephen-crawford Jul 3, 2023
600385e
Fix exception tests
stephen-crawford Jul 3, 2023
bf03fc4
Merge branch 'opensearch-project:main' into limitedScopes
stephen-crawford Jul 3, 2023
0c380ac
Add noop applicationManager
stephen-crawford Jul 3, 2023
2bb390a
remove user scopes
stephen-crawford Jul 3, 2023
c24f75b
add message
stephen-crawford Jul 3, 2023
bae552b
Apply suggestions from code review
stephen-crawford Jul 3, 2023
926cb0c
Apply suggestions from code review
stephen-crawford Jul 3, 2023
556877c
fix rebase
stephen-crawford Jul 5, 2023
90291e6
Merge branch 'main' into limitedScopes
stephen-crawford Jul 5, 2023
e89239f
spotless
stephen-crawford Jul 5, 2023
7a776b6
Fix exception issue
stephen-crawford Jul 5, 2023
78f0246
Merge branch 'main' into limitedScopes
stephen-crawford Jul 6, 2023
0d1f730
Fix imports after rebase
stephen-crawford Jul 6, 2023
a689cb1
fix failure
stephen-crawford Jul 6, 2023
4431867
Merge branch 'main' into limitedScopes
stephen-crawford Jul 11, 2023
76aceca
Peter's feedback
stephen-crawford Jul 11, 2023
8143851
Merge branch 'opensearch-project:main' into limitedScopes
stephen-crawford Jul 11, 2023
54fe5e7
Merge branch 'opensearch-project:main' into limitedScopes
stephen-crawford Jul 11, 2023
fcf319a
Update code coverage
stephen-crawford Jul 12, 2023
2928b75
Merge branch 'main' into limitedScopes
stephen-crawford Jul 13, 2023
c74ab97
Update imports
stephen-crawford Jul 13, 2023
91cf161
Merge branch 'main' into limitedScopes
stephen-crawford Jul 13, 2023
3502517
just need to add extension point test
stephen-crawford Jul 13, 2023
4f942d3
Add test
stephen-crawford Jul 13, 2023
c3fdaff
spotless
stephen-crawford Jul 13, 2023
904f034
Update plugins/identity-shiro/src/main/java/org/opensearch/identity/s…
stephen-crawford Jul 14, 2023
ca5ef1d
Update server/src/main/java/org/opensearch/cluster/node/DiscoveryNode…
stephen-crawford Jul 14, 2023
7e94469
Update to support writeables
stephen-crawford Jul 14, 2023
67d6251
Fix initialization
stephen-crawford Jul 14, 2023
6b7bfec
Update usage
stephen-crawford Jul 14, 2023
2e5cc0f
Fix package order
stephen-crawford Jul 14, 2023
3d7d780
Update server/src/main/java/org/opensearch/extensions/DiscoveryExtens…
stephen-crawford Jul 14, 2023
ef949f4
Update server/src/main/java/org/opensearch/extensions/DiscoveryExtens…
stephen-crawford Jul 14, 2023
7e77422
Fix renames
stephen-crawford Jul 14, 2023
3f4c491
Spotless
stephen-crawford Jul 14, 2023
7be9930
Update xcontext
stephen-crawford Jul 14, 2023
a0acf53
Fix test
stephen-crawford Jul 14, 2023
498e245
Update writeable and xcontent use
stephen-crawford Jul 14, 2023
787e1f7
Fix style
stephen-crawford Jul 14, 2023
0c868bd
fix streaming
stephen-crawford Jul 14, 2023
d8578f7
Merge remote-tracking branch 'origin/main' into limitedScopes
peternied Jul 17, 2023
49d5290
Clean up subject creep
peternied Jul 17, 2023
aeaa324
Resolve shiro test issue
peternied Jul 18, 2023
1c8dbe5
IT placeholders, refactor to support assuming identities
peternied Jul 19, 2023
027b9e7
Merge branch 'main' into limitedScopes
stephen-crawford Jul 24, 2023
e925550
Add more details the integration test
peternied Jul 26, 2023
756954b
Merge branch 'opensearch-project:main' into limitedScopes
stephen-crawford Jul 28, 2023
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 @@ -78,6 +78,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Added
- Add server version as REST response header [#6583](https://github.com/opensearch-project/OpenSearch/issues/6583)
- Start replication checkpointTimers on primary before segments upload to remote store. ([#8221]()https://github.com/opensearch-project/OpenSearch/pull/8221)
- Introduce Application Scopes ([#7850]((https://github.com/opensearch-project/OpenSearch/issues/6583)))

### Dependencies
- Bump `org.apache.logging.log4j:log4j-core` from 2.17.1 to 2.20.0 ([#8307](https://github.com/opensearch-project/OpenSearch/pull/8307))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.lucene.tests.util.LuceneTestCase;
import org.junit.Before;
import org.opensearch.LegacyESVersion;
import org.opensearch.Version;
import org.opensearch.cli.ExitCodes;
Expand All @@ -50,7 +50,6 @@
import org.opensearch.env.Environment;
import org.opensearch.env.TestEnvironment;
import org.opensearch.test.OpenSearchTestCase;
import org.junit.Before;

@LuceneTestCase.SuppressFileSystems("*")
public class ListPluginsCommandTests extends OpenSearchTestCase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,28 @@

import java.security.Principal;
import java.util.Objects;

import java.util.Optional;
import org.opensearch.identity.Subject;
import org.opensearch.identity.tokens.AuthToken;

/**
* Subject backed by Shiro
* A Subject backed by the Identity Shiro Plugin and wrapping a shiro.subject implementing Subject
*
* @opensearch.experimental
*/
public class ShiroSubject implements Subject {
private final ShiroTokenManager authTokenHandler;
private final org.apache.shiro.subject.Subject shiroSubject;
private final org.apache.shiro.subject.Subject delegate;

/**
* Creates a new shiro subject for use with the IdentityPlugin
* Cannot return null
* @param authTokenHandler Used to extract auth header info
* @param subject The specific subject being authc/z'd
* @param shiroBackedSubject The specific subject being authc/z'd
*/
public ShiroSubject(final ShiroTokenManager authTokenHandler, final org.apache.shiro.subject.Subject subject) {
public ShiroSubject(final ShiroTokenManager authTokenHandler, final org.apache.shiro.subject.Subject shiroBackedSubject) {
this.authTokenHandler = Objects.requireNonNull(authTokenHandler);
this.shiroSubject = Objects.requireNonNull(subject);
this.delegate = Objects.requireNonNull(shiroBackedSubject);
}

/**
Expand All @@ -41,7 +41,7 @@ public ShiroSubject(final ShiroTokenManager authTokenHandler, final org.apache.s
*/
@Override
public Principal getPrincipal() {
final Object o = shiroSubject.getPrincipal();
final Object o = delegate.getPrincipal();
if (o == null) return null;
if (o instanceof Principal) return (Principal) o;
return () -> o.toString();
Expand Down Expand Up @@ -85,7 +85,13 @@ public String toString() {
*/
public void authenticate(AuthToken authenticationToken) {
final org.apache.shiro.authc.AuthenticationToken authToken = authTokenHandler.translateAuthToken(authenticationToken)
.orElseThrow(() -> new UnsupportedAuthenticationToken());
shiroSubject.login(authToken);
.orElseThrow(UnsupportedAuthenticationToken::new);
delegate.login(authToken);
}

@SuppressWarnings("unchecked")
@Override
public Optional<Principal> getApplication() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should not mix application and user subject, it should either one or another, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is included because it is an optional defined in the Subject interface. The idea is that we are able to differentiate between human users and applications by checking if a subject has an associated application inside the application manager.

Does that make sense?

Copy link
Collaborator

@reta reta Jul 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is included because it is an optional defined in the Subject interface.

I mean this should be designed as exclusive abstractions, the optional just branch off the different states and makes API prone to misuse.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a Subject there the notion of a principal. In different frameworks it is represented as, an ordered collections of principals, other have multiple principals with one called a primary, or in what is proposed here there is a user principle and a separate application principal. What do you think?

I like the approach to have a distinct application principal as OpenSearch application concept for plugin and extension systems is limited to core. The Application manager that @scrawfor99 has proposed isn't meant to be accessed/extended by extensions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other have multiple principals with one called a primary, or in what is proposed here there is a user principle and a separate application principal. What do you think?

I don't see that in the current design: every single (meaningful) usage of the getApplication() delegates to getPrincipal() (see https://github.com/opensearch-project/OpenSearch/pull/7850/files#diff-1aa331c528b9d022fdd78cc985d90857b10cf9d0d5da28bafd2acb5dbbf799e3R166 fe), there is no primary or secondary. This is why I think we have to split those into distinct subjects: user, application (may be later plugin or extension).

Copy link
Member

@peternied peternied Jul 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've revisited many of these usages and largely removed subject's usage. There is another pull request [link] where we are adding service accounts which are tied to applications, and allow them to make REST requests - IMO this is a better personification as it allows applications to follow the same external authentication processes as users.

Contrived example of how the subject is changable over time;
So if I make a request with my openSearch client, after its authenticated, its {sub: peternied}, and then it flows into a plugins action handler, it should be {sub: peternied, app: machine_learning}, then after the request leaves the handler, it becomes just {sub: peternied}, if it enters another plugin entry point {sub: peternied, app: security_plugin} gets changed. The primary subject is still me; however, the application changes as the request is handled in OpenSearch.

peternied, machine_learning, security_plugin are all principals, but you cannot authenticate with the cluster as machine_learning as it isn't a Subject. Let me know if this helps clarify

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, thanks @peternied , it cleared the confusion for me, so this is basically an originator, right? Should we call it as such?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good pull on originator [1] is the correct name. I think on the subject, we would switch to getOriginator() as you suggest, but keep the ApplicationAwareSubject which handle when the originator is an plugin/extension.

return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.junit.Before;
import org.opensearch.identity.noop.NoopTokenManager;
import org.opensearch.identity.tokens.AuthToken;
import org.opensearch.identity.tokens.BasicAuthToken;
import org.opensearch.identity.tokens.BearerAuthToken;
Expand All @@ -31,12 +30,10 @@
public class AuthTokenHandlerTests extends OpenSearchTestCase {

private ShiroTokenManager shiroAuthTokenHandler;
private NoopTokenManager noopTokenManager;

@Before
public void testSetup() {
shiroAuthTokenHandler = new ShiroTokenManager();
noopTokenManager = new NoopTokenManager();
}

public void testShouldExtractBasicAuthTokenSuccessfully() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@

package org.opensearch.identity.shiro;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import org.opensearch.OpenSearchException;
import org.opensearch.common.settings.Settings;
import org.opensearch.identity.ApplicationManager;
import org.opensearch.identity.IdentityService;
import org.opensearch.plugins.IdentityPlugin;
import org.opensearch.test.OpenSearchTestCase;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThrows;

public class ShiroIdentityPluginTests extends OpenSearchTestCase {

public void testSingleIdentityPluginSucceeds() {
public void testSingleIdentityPluginSucceeds() throws IOException {
IdentityPlugin identityPlugin1 = new ShiroIdentityPlugin(Settings.EMPTY);
List<IdentityPlugin> pluginList1 = List.of(identityPlugin1);
IdentityService identityService1 = new IdentityService(Settings.EMPTY, pluginList1);
IdentityService identityService1 = new IdentityService(Settings.EMPTY, pluginList1, new ApplicationManager());
assertThat(identityService1.getTokenManager(), is(instanceOf(ShiroTokenManager.class)));
}

Expand All @@ -33,8 +36,25 @@ public void testMultipleIdentityPluginsFail() {
IdentityPlugin identityPlugin2 = new ShiroIdentityPlugin(Settings.EMPTY);
IdentityPlugin identityPlugin3 = new ShiroIdentityPlugin(Settings.EMPTY);
List<IdentityPlugin> pluginList = List.of(identityPlugin1, identityPlugin2, identityPlugin3);
Exception ex = assertThrows(OpenSearchException.class, () -> new IdentityService(Settings.EMPTY, pluginList));
Exception ex = assertThrows(
OpenSearchException.class,
() -> new IdentityService(Settings.EMPTY, pluginList, new ApplicationManager())
);
assert (ex.getMessage().contains("Multiple identity plugins are not supported,"));
}

public void testShiroIdentityMethods() throws IOException {
IdentityPlugin identityPlugin1 = new ShiroIdentityPlugin(Settings.EMPTY);
List<IdentityPlugin> pluginList1 = List.of(identityPlugin1);
IdentityService identityService1 = new IdentityService(Settings.EMPTY, pluginList1, new ApplicationManager());
assertThat(identityService1.getTokenManager(), is(instanceOf(ShiroTokenManager.class)));
assertTrue(identityPlugin1.getSubject() instanceof ShiroSubject);
assertEquals(identityPlugin1.getSubject().hashCode(), Objects.hash(identityPlugin1.getSubject().getPrincipal()));
assertEquals(identityPlugin1.getSubject().getApplication(), Optional.empty());
assertEquals(
identityPlugin1.getSubject().toString(),
"ShiroSubject(principal=" + identityPlugin1.getSubject().getPrincipal() + ")"
);
}

}

This file was deleted.

3 changes: 2 additions & 1 deletion server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ dependencies {
api project(':libs:opensearch-x-content')
api project(":libs:opensearch-geo")
api project(":libs:opensearch-telemetry")
testImplementation project(path: ':modules:reindex')


compileOnly project(':libs:opensearch-plugin-classloader')
compileOnly project(':libs:opensearch-plugin-classloader')
testRuntimeOnly project(':libs:opensearch-plugin-classloader')

// lucene
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.identity;

import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.CollectionTerminatedException;
import org.apache.lucene.search.ScoreMode;

import org.opensearch.ExceptionsHelper;
import org.opensearch.action.ActionListener;
import org.opensearch.action.admin.cluster.node.stats.NodeStats;
import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest;
import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse;
import org.opensearch.action.index.IndexRequest;
import org.opensearch.action.index.IndexResponse;
import org.opensearch.action.support.IndicesOptions;
import org.opensearch.action.support.WriteRequest;
import org.opensearch.client.Client;
import org.opensearch.cluster.metadata.IndexMetadata;
import org.opensearch.common.breaker.CircuitBreaker;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.unit.TimeValue;
import org.opensearch.common.util.concurrent.AtomicArray;
import org.opensearch.core.common.Strings;
import org.opensearch.core.xcontent.ObjectParser;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.index.IndexSettings;
import org.opensearch.index.query.QueryShardContext;
import org.opensearch.index.query.RangeQueryBuilder;
import org.opensearch.index.shard.IndexShard;
import org.opensearch.indices.IndicesService;
import org.opensearch.indices.replication.common.ReplicationType;
import org.opensearch.plugins.Plugin;
import org.opensearch.plugins.SearchPlugin;
import org.opensearch.core.rest.RestStatus;
import org.opensearch.search.DocValueFormat;
import org.opensearch.search.SearchHit;
import org.opensearch.search.aggregations.AbstractAggregationBuilder;
import org.opensearch.search.aggregations.AggregationBuilder;
import org.opensearch.search.aggregations.Aggregations;
import org.opensearch.search.aggregations.Aggregator;
import org.opensearch.search.aggregations.AggregatorBase;
import org.opensearch.search.aggregations.AggregatorFactories;
import org.opensearch.search.aggregations.AggregatorFactory;
import org.opensearch.search.aggregations.CardinalityUpperBound;
import org.opensearch.search.aggregations.InternalAggregation;
import org.opensearch.search.aggregations.LeafBucketCollector;
import org.opensearch.search.aggregations.bucket.terms.LongTerms;
import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.opensearch.search.aggregations.metrics.InternalMax;
import org.opensearch.search.aggregations.support.ValueType;
import org.opensearch.search.builder.SearchSourceBuilder;
import org.opensearch.search.fetch.FetchSubPhase;
import org.opensearch.search.fetch.FetchSubPhaseProcessor;
import org.opensearch.search.internal.SearchContext;
import org.opensearch.test.OpenSearchIntegTestCase;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;

public class IdentityServiceIT extends OpenSearchIntegTestCase {

// TODO: Verify that application aware subject is persisted on the same request

}
Loading