Skip to content

Commit

Permalink
WIP on securing get resource, integrationTest not using correct resou…
Browse files Browse the repository at this point in the history
…rce service

Signed-off-by: Craig Perkins <[email protected]>
  • Loading branch information
cwperks committed Dec 13, 2024
1 parent a3bd41d commit 20c24d0
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 56 deletions.
2 changes: 1 addition & 1 deletion sample-extension-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import java.util.concurrent.Callable

opensearchplugin {
name 'opensearch-security-sample-extension'
description 'Sample plugin that extends OpenSearch Security Resource Sharing plugin'
description 'Sample plugin that extends OpenSearch Security Resource Sharing Extension'
classname 'org.opensearch.security.sampleextension.SampleExtensionPlugin'
extendedPlugins = ['opensearch-security']
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
public class SampleExtensionPluginTests {

@ClassRule
public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS)
public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.SINGLENODE)
.plugin(SampleExtensionPlugin.class)
.anonymousAuth(true)
.authc(AUTHC_HTTPBASIC_INTERNAL)
Expand All @@ -50,6 +50,7 @@ public void testSecurityRoles() throws Exception {
assertThat(response.getTextFromJsonBody("/user_name"), equalTo("admin"));
System.out.println("Response: " + response.getBody());
HttpResponse pluginsResponse = client.get("_cat/plugins?s=component&h=name,component,version,description");
System.out.println("pluginsResponse: " + pluginsResponse.getBody());
assertThat(pluginsResponse.getBody(), containsString("org.opensearch.security.OpenSearchSecurityPlugin"));
assertThat(pluginsResponse.getBody(), containsString("org.opensearch.security.sampleextension.SampleExtensionPlugin"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ public class SampleExtensionPlugin extends Plugin implements ActionPlugin, Syste

private Client client;

private final SampleResourceSharingServiceProvider provider = new SampleResourceSharingServiceProvider();

@Override
public Collection<Object> createComponents(
Client client,
Expand All @@ -94,11 +92,15 @@ public Collection<Object> createComponents(
Supplier<RepositoriesService> repositoriesServiceSupplier
) {
this.client = client;
if (provider.get() == null) {
provider.set(new DefaultResourceSharingService<>(client, RESOURCE_INDEX_NAME, new SampleResourceFactory()));
if (SampleResourceSharingServiceProvider.getInstance().get() == null) {
System.out.println("Using DefaultResourceSharingService");
SampleResourceSharingServiceProvider.getInstance()
.set(new DefaultResourceSharingService<>(client, RESOURCE_INDEX_NAME, new SampleResourceFactory()));
}
System.out.println("provider: " + provider);
return List.of(provider);
System.out.println(
"SampleResourceSharingServiceProvider.getInstance(): " + SampleResourceSharingServiceProvider.getInstance().get()
);
return List.of(SampleResourceSharingServiceProvider.getInstance());
}

@Override
Expand Down Expand Up @@ -155,6 +157,6 @@ public ResourceFactory<? extends Resource> getResourceFactory() {
@SuppressWarnings("unchecked")
@Override
public void assignResourceSharingService(ResourceSharingService<? extends Resource> service) {
provider.set((ResourceSharingService<SampleResource>) service);
SampleResourceSharingServiceProvider.getInstance().set((ResourceSharingService<SampleResource>) service);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package org.opensearch.security.sampleextension.resource;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.opensearch.common.inject.Provider;
import org.opensearch.security.spi.ResourceSharingService;

Expand All @@ -12,6 +17,19 @@ public final class SampleResourceSharingServiceProvider implements Provider<Reso

private volatile ResourceSharingService<SampleResource> resourceSharingService;

private static final Map<ClassLoader, SampleResourceSharingServiceProvider> instances = new ConcurrentHashMap<>();

private SampleResourceSharingServiceProvider() {}

@SuppressWarnings("removal")
public static SampleResourceSharingServiceProvider getInstance() {
ClassLoader classLoader = AccessController.doPrivileged(
(PrivilegedAction<ClassLoader>) () -> Thread.currentThread().getContextClassLoader()
);
instances.computeIfAbsent(classLoader, cl -> new SampleResourceSharingServiceProvider());
return instances.get(classLoader);
}

/**
* Sets the resource sharing service implementation.
* This method is thread-safe and ensures the service is only set once.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.opensearch.security.spi.ResourceSharingService;
import org.opensearch.security.spi.ShareWith;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.security.support.WildcardMatcher;
import org.opensearch.security.user.User;

import static org.opensearch.security.resource.ResourceSharingListener.RESOURCE_SHARING_INDEX;
Expand All @@ -50,50 +51,24 @@ public SecurityResourceSharingService(Client client, String resourceIndex, Resou
this.resourceFactory = resourceFactory;
}

// private boolean hasPermissionsFor(User authenticatedUser, Resource resource) {
// // TODO Complete this function. The user has permissions if either of the conditions are true:
//
// // 1. The resource_user is the currently authenticated user
// // 2. The resource has been shared with the authenticated user
// // 3. The resource has been shared with a backend role that the authenticated user has
// if (authenticatedUser.getName().equals(resource.getResourceUser().getName())) {
// return true;
// }
// try (ThreadContext.StoredContext ignore = client.threadPool().getThreadContext().stashContext()) {
// SearchRequest searchRequest = new SearchRequest(RESOURCE_SHARING_INDEX);
// SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
// .must(QueryBuilders.termQuery("resource_index", resourceIndex))
// .must(QueryBuilders.termQuery("resource_id", resource.getResourceId()));
//
// searchSourceBuilder.query(boolQuery);
// searchSourceBuilder.size(1); // Limit to 1 result
// searchRequest.source(searchSourceBuilder);
//
// ActionListener<SearchResponse> searchListener = new ActionListener<SearchResponse>() {
// @Override
// public void onResponse(SearchResponse searchResponse) {
// SearchHit[] hits = searchResponse.getHits().getHits();
// if (hits.length > 0) {
// SearchHit hit = hits[0];
// T resource = resourceFactory.createResource();
// resource.fromSource(hit.getId(), hit.getSourceAsMap());
// getResourceListener.onResponse(resource);
// } else {
// getResourceListener.onFailure(new ResourceNotFoundException("Resource not found"));
// }
// }
//
// @Override
// public void onFailure(Exception e) {
// throw new OpenSearchException("Caught exception while loading resources: " + e.getMessage());
// }
// };
//
// client.search(searchRequest, searchListener);
// }
// return false;
// }
private boolean hasPermissionsFor(User authenticatedUser, Resource resource, ShareWith sharedWith) {
System.out.println("hasPermissionsFor: " + authenticatedUser + " " + resource + " " + sharedWith);
// 1. The resource_user is the currently authenticated user
// 2. The resource has been shared with the authenticated user
// 3. The resource has been shared with a backend role that the authenticated user has
if (authenticatedUser.getName().equals(resource.getResourceUser().getName())) {
return true;
}
WildcardMatcher userMatcher = WildcardMatcher.from(sharedWith.getUsers());
if (userMatcher.test(authenticatedUser.getName())) {
return true;
}
WildcardMatcher backendRoleMatcher = WildcardMatcher.from(sharedWith.getBackendRoles());
if (authenticatedUser.getRoles().stream().anyMatch(backendRoleMatcher::test)) {
return true;
}
return false;
}

@SuppressWarnings("unchecked")
@Override
Expand Down Expand Up @@ -200,6 +175,7 @@ public void getResource(String resourceId, ActionListener<T> getResourceListener
public void onResponse(GetResponse getResponse) {
T resource = resourceFactory.createResource();
resource.fromSource(getResponse.getId(), getResponse.getSourceAsMap());
System.out.println("finishGetResourceIfUserIsAllowed");
finishGetResourceIfUserIsAllowed(resource, authenticatedUser, getResourceListener);
}

Expand All @@ -217,8 +193,8 @@ private void finishGetResourceIfUserIsAllowed(T resource, User authenticatedUser
SearchRequest searchRequest = new SearchRequest(RESOURCE_SHARING_INDEX);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("resource_index", resourceIndex))
.must(QueryBuilders.termQuery("resource_id", resource.getResourceId()));
.must(QueryBuilders.termQuery("resource_index", resourceIndex))
.must(QueryBuilders.termQuery("resource_id", resource.getResourceId()));

searchSourceBuilder.query(boolQuery);
searchSourceBuilder.size(1); // Limit to 1 result
Expand All @@ -231,7 +207,11 @@ public void onResponse(SearchResponse searchResponse) {
if (hits.length > 0) {
SearchHit hit = hits[0];
ShareWith sharedWith = ShareWith.fromSource(hit.getSourceAsMap());
getResourceListener.onResponse(resource);
if (hasPermissionsFor(authenticatedUser, resource, sharedWith)) {
getResourceListener.onResponse(resource);
} else {
getResourceListener.onFailure(new OpenSearchException("User is not authorized to access this resource"));
}
} else {
getResourceListener.onFailure(new ResourceNotFoundException("Resource not found"));
}
Expand Down

0 comments on commit 20c24d0

Please sign in to comment.