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

[Extensions] Add a internal user provider interface #2713

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
483b9e2
Add extensionsManager into GuiceHolder
stephen-crawford Mar 27, 2023
6d58e08
Basic outline of functions for making service accounts
stephen-crawford Mar 27, 2023
42f460f
Outline of service account creation
stephen-crawford Mar 27, 2023
b6ab47a
Extension Service Outline
stephen-crawford Mar 28, 2023
e1422fb
Add test method names and pass through
stephen-crawford Mar 28, 2023
882b652
add licenses and spotless
stephen-crawford Mar 28, 2023
8297b84
Test notes
stephen-crawford Mar 29, 2023
2ddfc06
Update
stephen-crawford Mar 30, 2023
3af1e8d
clear
stephen-crawford Mar 31, 2023
d689d23
Checkstyle
stephen-crawford Mar 31, 2023
5d4cc8f
Comment out test
stephen-crawford Apr 3, 2023
13f3819
spotless
stephen-crawford Apr 3, 2023
34b4e4b
Remove extra tests
stephen-crawford Apr 3, 2023
10bd3bb
Update imports
stephen-crawford Apr 3, 2023
0f7f62f
Update imports
stephen-crawford Apr 3, 2023
0327f3b
Update imports
stephen-crawford Apr 3, 2023
2263129
Update contains
stephen-crawford Apr 4, 2023
9d2ec1d
fix service
stephen-crawford Apr 4, 2023
b1ffa6d
Fix tests
stephen-crawford Apr 5, 2023
9b59a76
remove prints
stephen-crawford Apr 5, 2023
6b52eeb
Clean service
stephen-crawford Apr 6, 2023
cd36a68
Clean service
stephen-crawford Apr 6, 2023
cd45201
Clean service
stephen-crawford Apr 6, 2023
7ab1862
remove unused import
stephen-crawford Apr 6, 2023
518ef90
remove imports
stephen-crawford Apr 6, 2023
c8df4fc
fix user service and add tests
stephen-crawford Apr 7, 2023
4605b55
fix user service and add tests
stephen-crawford Apr 7, 2023
eb826d8
resolve conflict
stephen-crawford Apr 7, 2023
abecea7
Remove dangling test helper
stephen-crawford Apr 7, 2023
1c3ac14
fix tests
stephen-crawford Apr 10, 2023
f9020b0
remove messaging
stephen-crawford Apr 10, 2023
a1ab910
reset auditlog file
stephen-crawford Apr 10, 2023
114f05d
reset auditlog file
stephen-crawford Apr 10, 2023
7c4a464
reset auditlog file
stephen-crawford Apr 10, 2023
5983d98
Cast message to string
stephen-crawford Apr 12, 2023
9639f87
Swap tenancy action names
stephen-crawford Apr 12, 2023
cd5c4e3
reset tenancy action naem
stephen-crawford Apr 14, 2023
80bb066
Swap to node instead of parsing string and remove uneccessary additio…
stephen-crawford Apr 17, 2023
6d8cb97
update checks
stephen-crawford Apr 18, 2023
5e2410f
remove unused variable
stephen-crawford Apr 18, 2023
1cec4e4
Specify encoding
stephen-crawford Apr 18, 2023
4ede132
remove uneccessary lines
stephen-crawford Apr 20, 2023
cd5f3b9
Add interface for intenral user provider
stephen-crawford Apr 20, 2023
1577af5
update methods and add basic tests
stephen-crawford Apr 21, 2023
b47a802
Swap to InternalUser Attributes
stephen-crawford Apr 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
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
import org.opensearch.security.transport.DefaultInterClusterRequestEvaluator;
import org.opensearch.security.transport.InterClusterRequestEvaluator;
import org.opensearch.security.transport.SecurityInterceptor;
import org.opensearch.security.user.SecurityInternalUserProvider;
import org.opensearch.security.user.User;
import org.opensearch.security.user.UserService;
import org.opensearch.tasks.Task;
Expand Down Expand Up @@ -821,6 +822,8 @@ public Collection<Object> createComponents(Client localClient, ClusterService cl

userService = new UserService(cs, cr, settings, localClient);

SecurityInternalUserProvider securityInternalUserProvider = new SecurityInternalUserProvider(userService);

final XFFResolver xffResolver = new XFFResolver(threadPool);
backendRegistry = new BackendRegistry(settings, adminDns, xffResolver, auditLog, threadPool);

Expand Down Expand Up @@ -878,7 +881,7 @@ public Collection<Object> createComponents(Client localClient, ClusterService cl
components.add(si);
components.add(dcf);
components.add(userService);

components.add(securityInternalUserProvider);

return components;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,19 @@

public class InternalUsersApiAction extends PatchableResourceApiAction {
static final List<String> RESTRICTED_FROM_USERNAME = ImmutableList.of(
":" // Not allowed in basic auth, see https://stackoverflow.com/a/33391003/533057
":" // Not allowed in basic auth, see https://stackoverflow.com/a/33391003/533057
);

private static final List<Route> routes = addRoutesPrefix(ImmutableList.of(
new Route(Method.GET, "/user/{name}"),
new Route(Method.GET, "/user/"),
new Route(Method.GET, "/user/{name}/authtoken"),
new Route(Method.DELETE, "/user/{name}"),
new Route(Method.PUT, "/user/{name}"),

// corrected mapping, introduced in OpenSearch Security
new Route(Method.GET, "/internalusers/{name}"),
new Route(Method.GET, "/internalusers/{name}/authtoken"),
new Route(Method.GET, "/internalusers/"),
new Route(Method.DELETE, "/internalusers/{name}"),
new Route(Method.PUT, "/internalusers/{name}"),
Expand Down Expand Up @@ -144,8 +146,10 @@ protected void handlePut(RestChannel channel, final RestRequest request, final C
throw new IOException(ex);
}


saveAndUpdateConfigs(this.securityIndexName,client, CType.INTERNALUSERS, internalUsersConfiguration, new OnSucessActionListener<IndexResponse>(channel) {


@Override
public void onResponse(IndexResponse response) {
if (userExisted) {
Expand All @@ -158,6 +162,55 @@ public void onResponse(IndexResponse response) {
});
}

@Override
protected void handleGet(final RestChannel channel, RestRequest request, Client client, final JsonNode content) throws IOException{

final String username = request.param("name");

final SecurityDynamicConfiguration<?> internalUsersConfiguration = load(getConfigName(), true);
filter(internalUsersConfiguration);

// no specific resource requested, return complete config
if (username == null || username.length() == 0) {

successResponse(channel, internalUsersConfiguration);
return;
}

final boolean userExisted = internalUsersConfiguration.exists(username);

if (!userExisted) {
notFound(channel, "Resource '" + username + "' not found.");
return;
}

String authToken = "";
try {
if (request.uri().contains("/internalusers/" + username + "/authtoken")) {
Copy link
Member

Choose a reason for hiding this comment

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

Do you think we should create a separate handler for this API? It may get confusing that the same handler is used for multiple endpoints.

Can you also add more description to the PR about the API being introduced?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The InternalUsersApiAction already includes several endpoints so I thought it was appropriate to add this here. It is just a special instance of the GET functionality.

I will add some extra documentation.


authToken = userService.generateAuthToken(username);
} else {

internalUsersConfiguration.removeOthers(username);
successResponse(channel, internalUsersConfiguration);
return;
}
} catch (UserServiceException ex) {
badRequestResponse(channel, ex.getMessage());
return;
}
catch (IOException ex) {
throw new IOException(ex);
}

if (!authToken.isEmpty()) {
createdResponse(channel, "'" + username + "' authtoken updated generated " + authToken);
} else {
badRequestResponse(channel, "'" + username + "' authtoken failed to be created.");
}
}


@Override
protected void filter(SecurityDynamicConfiguration<?> builder) {
super.filter(builder);
Expand Down Expand Up @@ -193,6 +246,10 @@ protected AbstractConfigurationValidator postProcessApplyPatchResult(RestChannel
return null;
}

public InternalUsersApiAction getAction(){
return this;
}

@Override
protected String getResourceName() {
return "user";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.opensearch.security.user;

import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration;

/**
* This interface will be defined in core following these changes. For now this is used for testing in the Security Plugin.
*
*/
public interface InternalUserProvider {
Copy link
Member

Choose a reason for hiding this comment

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

What do you think about making this a 'ServiceAccountManager'?

Then it would have only the use cases needed in OpenSearch core, during extensions registration for find or create with enable / disable?


public void putInternalUser(String userInfo) throws java.io.IOException;
Copy link
Member

Choose a reason for hiding this comment

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

I'd recommend against throwing IOException, can we try /catch where this is coming from or if you think its needed throw specific exceptions?


public SecurityDynamicConfiguration<?> getInternalUser(String userInfo);
Copy link
Member

Choose a reason for hiding this comment

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

SecurityDynamicConfiguration shouldn't be part of the interface, we should return a public interface/class instead


public void removeInternalUser(String userInfo);

public String getInternalUserAuthToken(String userInfo) throws IOException;
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this return type should be a string in OpenSearch, it should be some kind of interface around the auth token. @cwperks Did you have ideas about how we would pass refresh token in the java ecosystem?

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.opensearch.security.user;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import org.opensearch.common.inject.Inject;
import org.opensearch.security.DefaultObjectMapper;
import org.opensearch.security.securityconf.impl.CType;
import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration;

public class SecurityInternalUserProvider implements InternalUserProvider{

UserService userService;

@Inject
public SecurityInternalUserProvider(UserService userService) {
this.userService = userService;
}

@Override
public void putInternalUser(String userInfo) throws IOException {

JsonNode content = DefaultObjectMapper.readTree(userInfo);
final ObjectNode contentAsNode = (ObjectNode) content;

SecurityDynamicConfiguration<?> internalUsersConfiguration = userService.createOrUpdateAccount(contentAsNode);
userService.saveAndUpdateConfigs(userService.getUserConfigName().toString(), userService.client, CType.INTERNALUSERS, internalUsersConfiguration);
}

@Override
public SecurityDynamicConfiguration<?> getInternalUser(String username) {

final SecurityDynamicConfiguration<?> internalUsersConfiguration = userService.load(userService.getUserConfigName(), true);

// no specific resource requested, return complete config
if (username == null || username.length() == 0) {
return internalUsersConfiguration;
}

final boolean userExisted = internalUsersConfiguration.exists(username);

if (!userExisted) {
throw new UserServiceException("Failed to retrieve requested internal user.");
}

internalUsersConfiguration.removeOthers(username);
return internalUsersConfiguration;
}

@Override
public void removeInternalUser(String username) {
final SecurityDynamicConfiguration<?> internalUsersConfiguration = userService.load(userService.getUserConfigName(), true);
internalUsersConfiguration.remove(username);
}

@Override
public String getInternalUserAuthToken(String username) throws IOException {
return userService.generateAuthToken(username);
}
}
Loading