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

Plugin service accounts #8526

Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5782c10
PluginServiceAccounts
stephen-crawford Jul 5, 2023
4eb4a22
Update changelog
stephen-crawford Jul 5, 2023
3e3f44e
Use pluginInfo
stephen-crawford Jul 5, 2023
949cc6e
Structure changes
stephen-crawford Jul 6, 2023
968d2e1
Add test outline
stephen-crawford Jul 6, 2023
f0c8e93
Basic Service Account Tests
stephen-crawford Jul 7, 2023
a35fce9
Merge branch 'opensearch-project:main' into pluginServiceAccount
stephen-crawford Jul 7, 2023
c93370c
Update changelog
stephen-crawford Jul 7, 2023
a958e44
Use noop to pass tests
stephen-crawford Jul 7, 2023
fc35c8c
Add noop for tests
stephen-crawford Jul 7, 2023
f4a8b35
optimize imports
stephen-crawford Jul 7, 2023
eacf3bb
Merge branch 'main' into pluginServiceAccounts
stephen-crawford Jul 7, 2023
08e0cb9
Refactor identity elements and swap to classes from interfaces to all…
stephen-crawford Jul 10, 2023
305518d
Merge branch 'main' into pluginServiceAccounts
stephen-crawford Jul 11, 2023
1d76147
start outlining runas
stephen-crawford Jul 11, 2023
6be0f4f
Add runAs
stephen-crawford Jul 12, 2023
4937eb1
Swap var
stephen-crawford Jul 12, 2023
68ed66d
Merge branch 'main' into pluginServiceAccounts
stephen-crawford Jul 24, 2023
25c713f
Merge branch 'opensearch-project:main' into pluginServiceAccounts
stephen-crawford Jul 28, 2023
9297405
Merge branch 'opensearch-project:main' into pluginServiceAccounts
stephen-crawford Jul 31, 2023
b5b4868
Fix imports
stephen-crawford Jul 31, 2023
56787c8
Fix imports
stephen-crawford Jul 31, 2023
cc18cfe
connect both extensions and plugins; tests failing because client pas…
stephen-crawford Jul 31, 2023
140c039
Merge branch 'main' into pluginServiceAccounts
stephen-crawford Aug 8, 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 @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Allow mmap to use new JDK-19 preview APIs in Apache Lucene 9.4+ ([#5151](https://github.com/opensearch-project/OpenSearch/pull/5151))
- Add events correlation engine plugin ([#6854](https://github.com/opensearch-project/OpenSearch/issues/6854))
- Add support for ignoring missing Javadoc on generated code using annotation ([#7604](https://github.com/opensearch-project/OpenSearch/pull/7604))
- Introduce Service Accounts for Plugins ([#8526](https://github.com/opensearch-project/OpenSearch/pull/8526))
- Add partial results support for concurrent segment search ([#8306](https://github.com/opensearch-project/OpenSearch/pull/8306))

### Dependencies
Expand Down
1 change: 1 addition & 0 deletions plugins/identity-shiro/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {
testImplementation "org.mockito:mockito-core:${versions.mockito}"
testImplementation project(path: ':client:rest-high-level')
testImplementation 'junit:junit:4.13.2'
testImplementation project(path: ':server')
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@

package org.opensearch.identity.shiro;

import org.opensearch.identity.Subject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.opensearch.common.settings.Settings;
import org.opensearch.identity.ServiceAccountManager;
import org.opensearch.identity.Subject;
import org.opensearch.identity.tokens.TokenManager;
import org.opensearch.plugins.IdentityPlugin;
import org.opensearch.common.settings.Settings;
import org.opensearch.plugins.Plugin;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.SecurityManager;

/**
* Identity implementation with Shiro
Expand All @@ -28,6 +29,7 @@ public final class ShiroIdentityPlugin extends Plugin implements IdentityPlugin

private final Settings settings;
private final ShiroTokenManager authTokenHandler;
private final ShiroServiceAccountManager serviceAccountManager;

/**
* Create a new instance of the Shiro Identity Plugin
Expand All @@ -37,6 +39,7 @@ public final class ShiroIdentityPlugin extends Plugin implements IdentityPlugin
public ShiroIdentityPlugin(final Settings settings) {
this.settings = settings;
authTokenHandler = new ShiroTokenManager();
serviceAccountManager = new ShiroServiceAccountManager();

SecurityManager securityManager = new ShiroSecurityManager();
SecurityUtils.setSecurityManager(securityManager);
Expand All @@ -61,4 +64,9 @@ public Subject getSubject() {
public TokenManager getTokenManager() {
return this.authTokenHandler;
}

@Override
public ServiceAccountManager getServiceAccountManager() {
return this.serviceAccountManager;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* 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.shiro;

import java.security.Principal;
import java.util.List;
import java.util.Objects;
import org.opensearch.Application;
import org.opensearch.identity.ServiceAccount;

/**
* This class defines a ServiceAccount in the context of the Shiro Identity Plugin.
*
* Here we can see the ShiroServiceAccount implement the ServiceAccount and be used to track permissions assigned to applications.
*/
public class ShiroServiceAccount implements ServiceAccount {

private final Application application;
private final Principal name;
private List<String> permissions = List.of(); // Fine-grained access controls not yet configured

/**
* Creates a principal for an application identity
* @param application The application to be associated with the service account
*/
public ShiroServiceAccount(final Application application) {

this.application = application;
name = application.getPrincipal();
}

/**
* This method will not usually exist but is required until fine-grained access controls are implemented
*
* @param permissions The permissions to assign the service account
*/
public void setPermissions(List<String> permissions) {
this.permissions = List.copyOf(permissions);
}

@Override
public String getName() {
return name.getName();
}

@Override
public boolean equals(final Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
final ServiceAccount that = (ServiceAccount) obj;
return Objects.equals(name, that.getName());
}

@Override
public int hashCode() {
return Objects.hash(name);
}

@Override
public String toString() {
return "ServiceAccount(" + "name=" + name + ")";
}

@Override
public List<String> getPermissions() {
return List.copyOf(permissions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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.shiro;

import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.Application;
import org.opensearch.identity.IdentityService;
import org.opensearch.identity.ServiceAccount;
import org.opensearch.identity.ServiceAccountManager;

/**
* Oversees the assignment of ServiceAccounts when using the ShiroIdentityPlugin
*
* @opensearch.experimental
*/
class ShiroServiceAccountManager implements ServiceAccountManager {

private static final Logger log = LogManager.getLogger(IdentityService.class);

private static Map<Application, ServiceAccount> applicationServiceAccountMap = new HashMap<>();

public ShiroServiceAccountManager() {

}

@Override
public ServiceAccount getServiceAccount(Application app) {
if (applicationServiceAccountMap.get(app) == null) {
applicationServiceAccountMap.put(app, new ShiroServiceAccount(app));
}
return applicationServiceAccountMap.get(app);
}

public Map<Application, ServiceAccount> getApplicationServiceAccountMap() {
return applicationServiceAccountMap;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* 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.shiro;

import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.opensearch.cluster.ApplicationManager;
import org.opensearch.common.settings.Settings;
import org.opensearch.env.Environment;
import org.opensearch.env.TestEnvironment;
import org.opensearch.identity.IdentityService;
import org.opensearch.identity.ServiceAccount;
import org.opensearch.index.IndexModule;
import org.opensearch.plugins.IdentityPlugin;
import org.opensearch.plugins.Plugin;
import org.opensearch.plugins.PluginsService;
import org.opensearch.test.OpenSearchTestCase;

public class ShiroServiceAccountTests extends OpenSearchTestCase {

private ShiroServiceAccountManager shiroServiceAccountManager;
private ApplicationManager applicationManager;
private PluginsService pluginsService;
private IdentityService identityService;
private IdentityPlugin identityPlugin;
private AdditionalSettingsPlugin1 additionalSettingsPlugin1;
private AdditionalSettingsPlugin2 additionalSettingsPlugin2;

Settings settings = Settings.builder()
.put(Environment.PATH_HOME_SETTING.getKey(), createTempDir())
.put("my.setting", "test")
.put(IndexModule.INDEX_STORE_TYPE_SETTING.getKey(), IndexModule.Type.NIOFS.getSettingsKey())
.build();

@SuppressWarnings("unchecked")
static PluginsService newPluginsService(
Settings settings,
ApplicationManager applicationManager,
Class<? extends Plugin>... classpathPlugins
) {
return new PluginsService(
settings,
applicationManager,
null,
null,
TestEnvironment.newEnvironment(settings).pluginsDir(),
Arrays.asList(classpathPlugins)
);
}

public static class AdditionalSettingsPlugin1 extends Plugin {
@Override
public Settings additionalSettings() {
return Settings.builder()
.put("foo.bar", "1")
.put(IndexModule.INDEX_STORE_TYPE_SETTING.getKey(), IndexModule.Type.MMAPFS.getSettingsKey())
.build();
}
}

public static class AdditionalSettingsPlugin2 extends Plugin {
@Override
public Settings additionalSettings() {
return Settings.builder().put("test.this", "2").build();
}
}

@Before
public void setup() {
identityPlugin = new ShiroIdentityPlugin(Settings.EMPTY);
List<IdentityPlugin> pluginList = List.of(identityPlugin);
applicationManager = new ApplicationManager();
identityService = new IdentityService(Settings.EMPTY, pluginList);
additionalSettingsPlugin1 = new AdditionalSettingsPlugin1();
additionalSettingsPlugin2 = new AdditionalSettingsPlugin2();
shiroServiceAccountManager = new ShiroServiceAccountManager();
applicationManager.register(shiroServiceAccountManager);
}

@SuppressWarnings("unchecked")
public void testRegisterSinglePlugin() {
pluginsService = newPluginsService(settings, applicationManager, additionalSettingsPlugin1.getClass());
ServiceAccount serviceAccount = shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(0).v1());
assertEquals(pluginsService.plugins.get(0).v1().getPrincipal().getName(), serviceAccount.getName());
assertEquals(shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(0).v1()), serviceAccount);
}

@SuppressWarnings("unchecked")
public void testRegisterMultiplePlugins() {
pluginsService = newPluginsService(
settings,
applicationManager,
additionalSettingsPlugin1.getClass(),
additionalSettingsPlugin2.getClass()
);
ServiceAccount serviceAccount1 = shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(0).v1());
ServiceAccount serviceAccount2 = shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(1).v1());
assertEquals(pluginsService.plugins.get(0).v1().getPrincipal().getName(), serviceAccount1.getName());
assertEquals(shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(0).v1()), serviceAccount1);
assertEquals(pluginsService.plugins.get(1).v1().getPrincipal().getName(), serviceAccount2.getName());
assertEquals(shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(1).v1()), serviceAccount2);
}

@SuppressWarnings("unchecked")
public void testRegisterMultiplePluginsWithSameName() {
pluginsService = newPluginsService(
settings,
applicationManager,
additionalSettingsPlugin1.getClass(),
additionalSettingsPlugin1.getClass()
);
ServiceAccount serviceAccount1a = shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(0).v1());
ServiceAccount serviceAccount1b = shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(1).v1());
assertEquals(pluginsService.plugins.get(0).v1().getPrincipal().getName(), serviceAccount1a.getName());
assertEquals(shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(0).v1()), serviceAccount1a);
assertEquals(pluginsService.plugins.get(1).v1().getPrincipal().getName(), serviceAccount1b.getName());
assertEquals(shiroServiceAccountManager.getServiceAccount(pluginsService.plugins.get(1).v1()), serviceAccount1b);
assertEquals(serviceAccount1a, serviceAccount1b); // Plugins are identified by their names so same name means same service account
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@

package org.opensearch;

import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.opensearch.cluster.metadata.IndexNameExpressionResolver;
import org.opensearch.cluster.node.DiscoveryNodes;
import org.opensearch.common.settings.ClusterSettings;
Expand All @@ -43,10 +46,6 @@
import org.opensearch.rest.RestController;
import org.opensearch.rest.RestHandler;

import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;

public class DieWithDignityPlugin extends Plugin implements ActionPlugin {

public DieWithDignityPlugin() {
Expand All @@ -65,5 +64,4 @@ public List<RestHandler> getRestHandlers(
) {
return Collections.singletonList(new RestDieWithDignityAction());
}

}
15 changes: 15 additions & 0 deletions server/src/main/java/org/opensearch/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch;

import org.opensearch.identity.Subject;

/**
* A service that transmits data to and/or from OpenSearch
*
* @opensearch.experimental
*/
public interface Application extends Subject {}
stephen-crawford marked this conversation as resolved.
Show resolved Hide resolved
Loading