Skip to content

Commit d365764

Browse files
Updates documentation
Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
1 parent 1d1752a commit d365764

File tree

2 files changed

+92
-142
lines changed

2 files changed

+92
-142
lines changed

RESOURCE_ACCESS_CONTROL_FOR_PLUGINS.md

Lines changed: 14 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ This feature ensures **secure** and **controlled** access to shareableResources
1616
This feature introduces **one primary component** for plugin developers:
1717

1818
### **1. `opensearch-security-spi`**
19-
- A **Service Provider Interface (SPI)** that plugins must implement to declare themselves as **Resource Plugins**.
19+
- A **Service Provider Interface (SPI)** that provides `ResourceSharingExtension` interface that plugins must implement to declare themselves as **Resource Plugins**.
2020
- The security plugin keeps track of these plugins (similar to how JobScheduler tracks `JobSchedulerExtension`).
21-
- Allows resource plugins to utilize a **service provider client** to implement access control.
21+
- Provides resource plugins with a **client** to implement access control.
2222

2323
### **Plugin Implementation Requirements:**
2424

@@ -42,16 +42,14 @@ opensearchplugin {
4242
}
4343
```
4444
- **Implement** the `ResourceSharingExtension` class.
45-
- **Ensure** that its declared resources implement the `Resource` interface.
4645
- **Ensure** that each resource index only contains 1 type of resource.
47-
- **Provide a resource parser**, which the security plugin uses to extract resource details from the resource index.
4846
- **Register itself** in `META-INF/services` by creating the following file:
4947
```
5048
src/main/resources/META-INF/services/org.opensearch.security.spi.ResourceSharingExtension
5149
```
5250
- This file must contain a **single line** specifying the **fully qualified class name** of the plugin’s `ResourceSharingExtension` implementation, e.g.:
5351
```
54-
org.opensearch.sample.SampleResourcePlugin
52+
org.opensearch.sample.SampleResourceSharingExtension
5553
```
5654
5755
---
@@ -180,45 +178,10 @@ Each **action-group** entry contains the following access definitions:
180178
### **Declaring a Plugin as a Resource Plugin**
181179
To integrate with the security plugin, your plugin must:
182180
1. Extend `ResourceSharingExtension` and implement required methods.
183-
2. Implement the `ShareableResource` interface for resource declaration.
184-
3. Implement a resource parser to extract resource details.
185-
4. Implement a client accessor to utilize `ResourceSharingClient`.
181+
2. Implement a client accessor to utilize `ResourceSharingClient`.
186182

187183
[`opensearch-security-spi` README.md](./spi/README.md) is a great resource to learn more about the components of SPI and how to set up.
188184

189-
Tip: Refer to the `org.opensearch.sample.SampleResourcePlugin` class to understand the setup in further detail.
190-
191-
Example usage:
192-
```java
193-
public class SampleResourcePlugin extends Plugin implements SystemIndexPlugin, ResourceSharingExtension {
194-
195-
// Override required methods
196-
197-
@Override
198-
public Collection<SystemIndexDescriptor> getSystemIndexDescriptors(Settings settings) {
199-
final SystemIndexDescriptor systemIndexDescriptor = new SystemIndexDescriptor(RESOURCE_INDEX_NAME, "Sample index with resources");
200-
return Collections.singletonList(systemIndexDescriptor);
201-
}
202-
203-
@Override
204-
public Set<ResourceProvider> getResourceProviders() {
205-
return Set.of(
206-
new ResourceProvider(
207-
SampleResource.class.getCanonicalName(), // class-name of the resource
208-
RESOURCE_INDEX_NAME, // the index that stores resource, **must only store the type of resource defined in the line above**
209-
new SampleResourceParser() // parser to parse the resource
210-
)
211-
);
212-
}
213-
214-
@Override
215-
public void assignResourceSharingClient(ResourceSharingClient resourceSharingClient) {
216-
ResourceSharingClientAccessor.setResourceSharingClient(resourceSharingClient);
217-
}
218-
}
219-
```
220-
221-
222185
### **Calling Access Control Methods from the ResourceSharingClient Client**
223186
The client provides **four access control methods** for plugins. For detailed usage and implementation, refer to the [`opensearch-security-spi` README.md](./spi/README.md#available-java-apis)
224187

@@ -230,20 +193,20 @@ The client provides **four access control methods** for plugins. For detailed us
230193
void verifyResourceAccess(String resourceId, String resourceIndex, ActionListener<Boolean> listener);
231194
```
232195

233-
### **2. `shareResource`**
196+
### **2. `share`**
234197

235198
**Grants access to a resource for specified users, roles, and backend roles.**
236199

237200
```
238-
void shareResource(String resourceId, String resourceIndex, SharedWithActionGroup.ActionGroupRecipients recipients, ActionListener<ResourceSharing> listener);
201+
void share(String resourceId, String resourceIndex, SharedWithActionGroup.ActionGroupRecipients recipients, ActionListener<ResourceSharing> listener);
239202
```
240203

241-
### **3. `revokeResourceAccess`**
204+
### **3. `revoke`**
242205

243206
**Removes access permissions for specified users, roles, and backend roles.**
244207

245208
```
246-
void revokeResourceAccess(String resourceId, String resourceIndex, SharedWithActionGroup.ActionGroupRecipients entitiesToRevoke, ActionListener<ResourceSharing> listener);
209+
void revoke(String resourceId, String resourceIndex, SharedWithActionGroup.ActionGroupRecipients entitiesToRevoke, ActionListener<ResourceSharing> listener);
247210
```
248211

249212
### **4. `getAccessibleResourceIds`**
@@ -271,7 +234,7 @@ protected void doExecute(Task task, ShareResourceRequest request, ActionListener
271234
}
272235

273236
ResourceSharingClient resourceSharingClient = ResourceSharingClientAccessor.getResourceSharingClient();
274-
resourceSharingClient.shareResource(
237+
resourceSharingClient.share(
275238
request.getResourceId(),
276239
RESOURCE_INDEX_NAME,
277240
request.getShareWith(),
@@ -307,23 +270,23 @@ sequenceDiagram
307270
SPI -->> Plugin: Response: Access Granted
308271

309272
%% For share, revoke, and list: return 501 Not Implemented
310-
Plugin ->> SPI: shareResource (noop)
273+
Plugin ->> SPI: share (noop)
311274
SPI -->> Plugin: Error 501 Not Implemented
312275

313-
Plugin ->> SPI: revokeResourceAccess (noop)
276+
Plugin ->> SPI: revoke (noop)
314277
SPI -->> Plugin: Error 501 Not Implemented
315278

316-
Plugin ->> SPI: listAccessibleResources (noop)
279+
Plugin ->> SPI: getAccessibleResourceIds (noop)
317280
SPI -->> Plugin: Error 501 Not Implemented
318281
else Security Plugin Enabled
319282
%% Step 3: Plugin calls Java APIs declared by ResourceSharingClient
320-
Plugin ->> SPI: Calls Java API (`verifyResourceAccess`, `shareResource`, `revokeResourceAccess`, `listAccessibleResources`)
283+
Plugin ->> SPI: Calls Java API (`verifyResourceAccess`, `share`, `revoke`, `getAccessibleResourceIds`)
321284

322285
%% Step 4: Request is sent to Security Plugin
323286
SPI ->> Security: Sends request to Security Plugin for processing
324287

325288
%% Step 5: Security Plugin handles request and returns response
326-
Security -->> SPI: Response (Access Granted or Denied / Resource Shared or Revoked / List Resources )
289+
Security -->> SPI: Response (Access Granted or Denied / Resource Shared or Revoked / List Resource IDs )
327290

328291
%% Step 6: Security SPI sends response back to Plugin
329292
SPI -->> Plugin: Passes processed response back to Plugin
@@ -465,7 +428,6 @@ sample_read_access:
465428
### **For Plugin Developers**
466429
- **Declare resources properly** in the `ResourceSharingExtension`.
467430
- **Use the resource sharing client** instead of direct index queries to check access.
468-
- **Implement a resource parser** to ensure correct resource extraction.
469431

470432
### **For Users & Admins**
471433
- **Keep system index protection enabled** for better security.

spi/README.md

Lines changed: 78 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,19 @@ This **Service Provider Interface (SPI)** provides the necessary **interfaces an
55

66
### **Resource Sharing and Access Control Extension**
77

8-
This **Service Provider Interface (SPI)** provides an extension point to implement **Resource Sharing and Access Control** in OpenSearch.
8+
This extension point provides extending plugins with interfaces necessary to implement **Resource Sharing and Access Control** in OpenSearch.
99

1010
---
1111

1212
### **Usage**
1313

1414
A plugin that **defines a resource** and aims to implement **access control** over that resource must **extend** the `ResourceSharingExtension` class to register itself as a **Resource Plugin**.
1515

16-
#### **Example: Implementing a Resource Plugin**
17-
```java
18-
public class SampleResourcePlugin extends Plugin implements SystemIndexPlugin, ResourceSharingExtension {
19-
20-
// Override required methods
21-
22-
@Override
23-
public Collection<SystemIndexDescriptor> getSystemIndexDescriptors(Settings settings) {
24-
final SystemIndexDescriptor systemIndexDescriptor = new SystemIndexDescriptor(RESOURCE_INDEX_NAME, "Sample index with resources");
25-
return Collections.singletonList(systemIndexDescriptor);
26-
}
27-
28-
@Override
29-
public Set<ResourceProvider> getResourceProviders() {
30-
return Set.of(
31-
new ResourceProvider(
32-
SampleResource.class.getCanonicalName(), // class-name of the resource
33-
RESOURCE_INDEX_NAME, // the index that stores resource, **must only store the type of resource defined in the line above**
34-
new SampleResourceParser() // parser to parse the resource
35-
)
36-
);
37-
}
38-
39-
@Override
40-
public void assignResourceSharingClient(ResourceSharingClient resourceSharingClient) {
41-
ResourceSharingClientAccessor.setResourceSharingClient(resourceSharingClient);
42-
}
43-
}
44-
```
45-
4616
---
4717

48-
### **Checklist for Implementing a Resource Plugin**
18+
### **Checklist for plugins aiming to implement Resource Access Control**
4919

50-
To properly integrate with the **Resource Sharing and Access Control SPI**, follow these steps:
20+
To properly integrate with the **Resource Sharing and Access Control Extension**, follow these steps:
5121

5222
#### **1. Add Required Dependencies**
5323
Include **`opensearch-security-spi`** in your **`build.gradle`** file.
@@ -57,62 +27,80 @@ dependencies {
5727
compileOnly group: 'org.opensearch', name:'opensearch-security-spi', version:"${opensearch_build_version}"
5828
}
5929
```
60-
6130
---
6231

63-
#### **2. Register the Plugin Using the Java SPI Mechanism**
64-
- Navigate to your plugin's `src/main/resources` folder.
65-
- Locate or create the `META-INF/services` directory.
66-
- Inside `META-INF/services`, create a file named:
67-
```
68-
org.opensearch.security.spi.resources.ResourceSharingExtension
69-
```
70-
- Edit the file and add a **single line** containing the **fully qualified class name** of your plugin implementation.
71-
Example:
72-
```
73-
org.opensearch.sample.SampleResourcePlugin
74-
```
75-
> This step ensures that OpenSearch **dynamically loads your plugin** as a resource-sharing extension.
76-
77-
---
78-
79-
#### **3. Declare a Resource Class**
80-
Each plugin must define a **resource class** that implements the `Resource` interface.
32+
#### **2. Declare a Resource Class**
33+
Each plugin must define a **resource class** .
8134
Example:
8235
```java
83-
public class SampleResource implements ShareableResource {
36+
public class SampleResource {
8437
private String id;
8538
private String owner;
8639

8740
// Constructor, getters, setters, etc.
88-
89-
@Override
90-
public String getName() {
91-
return name;
92-
}
9341
}
9442
```
9543

9644
---
9745

98-
#### **4. Implement a Resource Parser**
99-
A **`ResourceParser`** is required to convert **resource data** from OpenSearch indices.
46+
#### **3. Declare Resource Index as System index**
47+
**Important:** Mark the resource **index as a system index** to enforce security protections.
48+
10049
Example:
10150
```java
102-
public class SampleResourceParser implements ShareableResourceParser<SampleResource> {
51+
public class SampleResourcePlugin extends Plugin implements SystemIndexPlugin {
52+
53+
// Override required methods
54+
10355
@Override
104-
public SampleResource parseXContent(XContentParser parser) throws IOException {
105-
return SampleResource.fromXContent(parser);
56+
public Collection<SystemIndexDescriptor> getSystemIndexDescriptors(Settings settings) {
57+
final SystemIndexDescriptor systemIndexDescriptor = new SystemIndexDescriptor(RESOURCE_INDEX_NAME, "Sample index with resources");
58+
return Collections.singletonList(systemIndexDescriptor);
10659
}
10760
}
10861
```
10962

11063
---
11164

112-
#### **5. Implement the `ResourceSharingExtension` Interface**
65+
#### **4. Implement the `ResourceSharingExtension` Interface**
11366
Ensure that your **plugin declaration class** implements `ResourceSharingExtension` and provides **all required methods**.
11467

115-
**Important:** Mark the resource **index as a system index** to enforce security protections.
68+
```java
69+
// Create a new extension point to register itself of a resource access control plugin
70+
public class SampleResourceSharingExtension implements ResourceSharingExtension {
71+
72+
@Override
73+
public Set<ResourceProvider> getResourceProviders() {
74+
return Set.of(
75+
new ResourceProvider(
76+
SampleResource.class.getCanonicalName(), // class-name of the resource
77+
RESOURCE_INDEX_NAME // the index that stores resource, **must only store the type of resource defined in the line above**
78+
)
79+
);
80+
}
81+
82+
@Override
83+
public void assignResourceSharingClient(ResourceSharingClient resourceSharingClient) {
84+
ResourceSharingClientAccessor.setResourceSharingClient(resourceSharingClient);
85+
}
86+
}
87+
```
88+
89+
---
90+
91+
#### **5. Register the Plugin Using the Java SPI Mechanism**
92+
- Navigate to your plugin's `src/main/resources` folder.
93+
- Locate or create the `META-INF/services` directory.
94+
- Inside `META-INF/services`, create a file named:
95+
```
96+
org.opensearch.security.spi.resources.ResourceSharingExtension
97+
```
98+
- Edit the file and add a **single line** containing the **fully qualified class name** of your resource sharing extension implementation class.
99+
Example:
100+
```
101+
org.opensearch.sample.SampleResourceSharingExtension
102+
```
103+
> This step ensures that OpenSearch **dynamically loads your plugin** as a resource-sharing extension.
116104
117105
---
118106

@@ -168,31 +156,31 @@ protected void doExecute(Task task, DeleteResourceRequest request, ActionListene
168156
// Check permission to resource
169157
ResourceSharingClient resourceSharingClient = ResourceSharingClientAccessor.getResourceSharingClient();
170158
resourceSharingClient.verifyResourceAccess(resourceId, RESOURCE_INDEX_NAME, ActionListener.wrap(isAuthorized -> {
171-
if (!isAuthorized) {
172-
listener.onFailure(
173-
new OpenSearchStatusException("Current user is not authorized to delete resource: " + resourceId, RestStatus.FORBIDDEN)
174-
);
175-
return;
176-
}
177-
178-
// Authorization successful, proceed with deletion
179-
ThreadContext threadContext = transportService.getThreadPool().getThreadContext();
180-
try (ThreadContext.StoredContext ignored = threadContext.stashContext()) {
181-
deleteResource(resourceId, ActionListener.wrap(deleteResponse -> {
182-
if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
183-
listener.onFailure(new ResourceNotFoundException("Resource " + resourceId + " not found."));
184-
} else {
185-
listener.onResponse(new DeleteResourceResponse("Resource " + resourceId + " deleted successfully."));
186-
}
187-
}, exception -> {
188-
log.error("Failed to delete resource: " + resourceId, exception);
189-
listener.onFailure(exception);
190-
}));
191-
}
192-
}, exception -> {
193-
log.error("Failed to verify resource access: " + resourceId, exception);
194-
listener.onFailure(exception);
195-
}));
159+
if (!isAuthorized) {
160+
listener.onFailure(
161+
new OpenSearchStatusException("Current user is not authorized to delete resource: " + resourceId, RestStatus.FORBIDDEN)
162+
);
163+
return;
164+
}
165+
166+
// Authorization successful, proceed with deletion
167+
ThreadContext threadContext = transportService.getThreadPool().getThreadContext();
168+
try (ThreadContext.StoredContext ignored = threadContext.stashContext()) {
169+
deleteResource(resourceId, ActionListener.wrap(deleteResponse -> {
170+
if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
171+
listener.onFailure(new ResourceNotFoundException("Resource " + resourceId + " not found."));
172+
} else {
173+
listener.onResponse(new DeleteResourceResponse("Resource " + resourceId + " deleted successfully."));
174+
}
175+
}, exception -> {
176+
log.error("Failed to delete resource: " + resourceId, exception);
177+
listener.onFailure(exception);
178+
}));
179+
}
180+
}, exception -> {
181+
log.error("Failed to verify resource access: " + resourceId, exception);
182+
listener.onFailure(exception);
183+
}));
196184
}
197185

198186
private void deleteResource(String resourceId, ActionListener<DeleteResponse> listener) {

0 commit comments

Comments
 (0)