Skip to content

Commit

Permalink
Fixes revoke script to handle duplicates
Browse files Browse the repository at this point in the history
Signed-off-by: Darshit Chanpura <[email protected]>
  • Loading branch information
DarshitChanpura committed Dec 6, 2024
1 parent 9d4ca1e commit b4b22d6
Showing 1 changed file with 67 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
package org.opensearch.security.resources;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -702,50 +705,45 @@ public ResourceSharing updateResourceSharingInfo(String resourceId, String sourc
// Atomic operation
// TODO check if this script can be updated to replace magic identifiers (i.e. users, roles and backend_roles) with the ones
// supplied in shareWith
Script updateScript = new Script(ScriptType.INLINE, """
Script updateScript = new Script(ScriptType.INLINE, "painless", """
if (ctx._source.share_with == null) {
ctx._source.share_with = [:];
}
for (def entry : params.shareWith.entrySet()) {
def scopeName = entry.getKey();
def newScope = entry.getValue();
if (ctx._source.share_with.containsKey(scopeName)) {
def existingScope = ctx._source.share_with.get(scopeName);
if (newScope.users != null) {
if (existingScope.users == null) {
existingScope.users = new HashSet();
}
existingScope.users = new HashSet<>(existingScope.users);
existingScope.users.addAll(newScope.users);
}
if (existingScope.roles == null) {
existingScope.roles = new HashSet();
}
existingScope.roles = new HashSet<>(existingScope.roles);
existingScope.roles.addAll(newScope.roles);
}
if (newScope.backend_roles != null) {
if (existingScope.backend_roles == null) {
existingScope.backend_roles = new HashSet();
if (!ctx._source.share_with.containsKey(scopeName)) {
def newScopeEntry = [:];
for (def field : newScope.entrySet()) {
if (field.getValue() != null && !field.getValue().isEmpty()) {
newScopeEntry[field.getKey()] = new HashSet(field.getValue());
}
existingScope.backend_roles = new HashSet<>(existingScope.backend_roles);
existingScope.backend_roles.addAll(newScope.backend_roles);
}
ctx._source.share_with[scopeName] = newScopeEntry;
} else {
def newScopeEntry = [:];
if (newScope.users != null) {
newScopeEntry.users = new HashSet(newScope.users);
}
if (newScope.roles != null) {
newScopeEntry.roles = new HashSet(newScope.roles);
}
if (newScope.backend_roles != null) {
newScopeEntry.backend_roles = new HashSet(newScope.backend_roles);
def existingScope = ctx._source.share_with[scopeName];
for (def field : newScope.entrySet()) {
def fieldName = field.getKey();
def newValues = field.getValue();
if (newValues != null && !newValues.isEmpty()) {
if (!existingScope.containsKey(fieldName)) {
existingScope[fieldName] = new HashSet();
}
for (def value : newValues) {
if (!existingScope[fieldName].contains(value)) {
existingScope[fieldName].add(value);
}
}
}
}
ctx._source.share_with.put(scopeName, newScopeEntry);
}
}
""", "painless", Collections.singletonMap("shareWith", shareWithMap));
""", Collections.singletonMap("shareWith", shareWithMap));

boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, updateScript);
return success ? new ResourceSharing(resourceId, sourceIdx, createdBy, shareWith) : null;
Expand Down Expand Up @@ -900,34 +898,49 @@ public ResourceSharing revokeAccess(
LOGGER.debug("Revoking access for resource {} in {} for entities: {} and scopes: {}", resourceId, sourceIdx, revokeAccess, scopes);

try {
// Revoke resource access
Script revokeScript = new Script(
ScriptType.INLINE,
"painless",
"""
if (ctx._source.share_with != null) {
Set scopesToProcess = new HashSet(params.scopes == null || params.scopes.isEmpty() ? ctx._source.share_with.keySet() : params.scopes);
for (def scopeName : scopesToProcess) {
if (ctx._source.share_with.containsKey(scopeName)) {
def existingScope = ctx._source.share_with.get(scopeName);
for (def entry : params.revokeAccess.entrySet()) {
def entityType = entry.getKey();
def entitiesToRemove = entry.getValue();
if (existingScope.containsKey(entityType) && existingScope[entityType] != null) {
existingScope[entityType].removeAll(entitiesToRemove);
}
Map<String, Object> revoke = new HashMap<>();
for (Map.Entry<EntityType, Set<String>> entry : revokeAccess.entrySet()) {
revoke.put(entry.getKey().name().toLowerCase(), new ArrayList<>(entry.getValue()));
}

List<String> scopesToUse = scopes != null ? new ArrayList<>(scopes) : new ArrayList<>();

Script revokeScript = new Script(ScriptType.INLINE, "painless", """
if (ctx._source.share_with != null) {
Set scopesToProcess = new HashSet(params.scopes.isEmpty() ? ctx._source.share_with.keySet() : params.scopes);
for (def scopeName : scopesToProcess) {
if (ctx._source.share_with.containsKey(scopeName)) {
def existingScope = ctx._source.share_with.get(scopeName);
for (def entry : params.revokeAccess.entrySet()) {
def entityType = entry.getKey();
def entitiesToRemove = entry.getValue();
if (existingScope.containsKey(entityType) && existingScope[entityType] != null) {
if (!(existingScope[entityType] instanceof HashSet)) {
existingScope[entityType] = new HashSet(existingScope[entityType]);
}
existingScope[entityType].removeAll(entitiesToRemove);
if (existingScope[entityType].isEmpty()) {
existingScope.remove(entityType);
}
}
}
if (existingScope.isEmpty()) {
ctx._source.share_with.remove(scopeName);
}
}
""",
Map.of("revokeAccess", revokeAccess, "scopes", scopes)
);
}
}
""", Map.of("revokeAccess", revoke, "scopes", scopesToUse));

// Execute updateByQuery
boolean success = updateByQueryResourceSharing(sourceIdx, resourceId, revokeScript);

return success ? fetchDocumentById(sourceIdx, resourceId) : null;

} catch (Exception e) {
Expand Down

0 comments on commit b4b22d6

Please sign in to comment.