Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -69,7 +69,8 @@ Set<Capability> getCapabilityClosure(@Nonnull Subject subject,
*/
boolean hasPermission(@Nonnull Subject subject,
@Nonnull Resource resource,
@Nonnull Capability capability);
@Nonnull Capability capability,
String jwt);

Collection<Subject> getSubjectsWithAccessToResource(Resource resource);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import edu.stanford.protege.webprotege.common.ProjectId;
import edu.stanford.protege.webprotege.ipc.EventDispatcher;
import org.bson.Document;
import org.keycloak.common.VerificationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
Expand Down Expand Up @@ -49,19 +50,29 @@ public class AccessManagerImpl implements AccessManager {

private final EventDispatcher eventDispatcher;

private final TokenValidator tokenValidator;

private final BuiltInRoleOracle builtInRoleOracle;

/**
* Constructs an {@link AccessManager} that is backed by MongoDb.
*
* @param mongoTemplate A {@link MongoTemplate} that is used to access MongoDb.
*/
public AccessManagerImpl(ObjectMapper objectMapper,
MongoTemplate mongoTemplate,
ProjectRoleDefinitionsManager projectRoleDefinitionsManager, RoleDefinitionsManager roleDefinitionsManager, EventDispatcher eventDispatcher) {
ProjectRoleDefinitionsManager projectRoleDefinitionsManager,
RoleDefinitionsManager roleDefinitionsManager,
EventDispatcher eventDispatcher,
TokenValidator tokenValidator,
BuiltInRoleOracle builtInRoleOracle) {
this.objectMapper = objectMapper;
this.mongoTemplate = mongoTemplate;
this.projectRoleDefinitionsManager = projectRoleDefinitionsManager;
this.roleDefinitionsManager = roleDefinitionsManager;
this.eventDispatcher = eventDispatcher;
this.tokenValidator = tokenValidator;
this.builtInRoleOracle = builtInRoleOracle;
}

/**
Expand Down Expand Up @@ -241,12 +252,31 @@ public Set<Capability> getCapabilityClosure(@Nonnull Subject subject, @Nonnull R
}

@Override
public boolean hasPermission(@Nonnull Subject subject, @Nonnull Resource resource, @Nonnull Capability capability) {
public boolean hasPermission(@Nonnull Subject subject, @Nonnull Resource resource, @Nonnull Capability capability, String jwt) {
logger.info("Checking permission for subject {} and resource {} with capability: {}", subject, resource, capability);

lock.readLock().lock();
try {
return find(withUserOrAnyUserAndTarget(subject, resource))
List<Capability> capabilities = new ArrayList<>(find(withUserOrAnyUserAndTarget(subject, resource))
.map(d -> objectMapper.convertValue(d, RoleAssignment.class))
.anyMatch(roleAssignment -> roleAssignment.getCapabilityClosure().contains(capability));
.flatMap(roleAssignment -> roleAssignment.getCapabilityClosure().stream())
.toList());

if (jwt != null && !jwt.isEmpty()) {
try {
List<RoleId> roleIds = tokenValidator.extractClaimsWithoutVerification(jwt).stream()
.map(RoleId::new)
.toList();
capabilities.addAll(builtInRoleOracle.getCapabilitiesAssociatedToRoles(roleIds));
} catch (VerificationException e) {
logger.error("Error getting token claims", e);
throw new RuntimeException(e);
}
}


return capabilities.contains(capability);

} finally {
lock.readLock().unlock();
}
Expand Down Expand Up @@ -285,7 +315,8 @@ public Collection<Resource> getResourcesAccessibleToSubject(Subject subject, Cap
lock.readLock().lock();
try {
var userName = toUserName(subject);
var query = query(where(USER_NAME).is(userName).and(CAPABILITY_CLOSURE).is(capability.id()));
logger.info("Trying to fetch resources {} and capability {}", userName, capability.id());
var query = query(where(USER_NAME).is(userName).and(CAPABILITY_CLOSURE + ".id").is(capability.id()));
return find(query)
.map(f -> objectMapper.convertValue(f, RoleAssignment.class))
.map(ra -> {
Expand Down Expand Up @@ -331,7 +362,7 @@ public void rebuild() {
public void rebuild(ProjectId projectId) {
lock.writeLock().lock();
try {
logger.info("Rebuilding permissions for project: {}" , projectId);
logger.info("Rebuilding permissions for project: {}", projectId);
var criteria = where(PROJECT_ID).is(projectId.value());
var queryObject = new Query(criteria).getQueryObject();
rebuildMatchingRoleAssignments(queryObject);
Expand All @@ -345,7 +376,7 @@ private void rebuildMatchingRoleAssignments(Document queryObject) {
var collection = mongoTemplate.getCollection(COLLECTION_NAME);
collection.find(queryObject)
.forEach(roleAssignmentDoc -> {
final var roleAssignmentId = roleAssignmentDoc.get("_id" );
final var roleAssignmentId = roleAssignmentDoc.get("_id");
var roleAssignment = objectMapper.convertValue(roleAssignmentDoc, RoleAssignment.class);

// For each role assignment we compute the role closure and then
Expand Down Expand Up @@ -373,8 +404,8 @@ private void rebuildMatchingRoleAssignments(Document queryObject) {
sortedCapabilityClosure);

var updatedRoleAssignmentDoc = objectMapper.convertValue(updatedRoleAssignment, Document.class);
updatedRoleAssignmentDoc.put("_id" , roleAssignmentId);
collection.replaceOne(new Document("_id" , roleAssignmentId), updatedRoleAssignmentDoc);
updatedRoleAssignmentDoc.put("_id", roleAssignmentId);
collection.replaceOne(new Document("_id", roleAssignmentId), updatedRoleAssignmentDoc);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,11 @@ public AuthorizationCommandsService(AccessManager accessManager, TokenValidator
}
// TODO: Update this when Alex has committed the code
public GetAuthorizationStatusResponse handleAuthorizationStatusCommand(GetAuthorizationStatusRequest request, ExecutionContext executionContext) {
var hasPermission = false;
if(request.resource().isApplication()) {
List<RoleId> roleIds;
try {
roleIds = tokenValidator.getTokenClaims(executionContext.jwt()).stream()
.map(RoleId::new)
.toList();
Set<Capability> capabilities = new HashSet<>(builtInRoleOracle.getCapabilitiesAssociatedToRoles(roleIds));
hasPermission = capabilities.contains(request.capability());

} catch (VerificationException e) {
throw new RuntimeException(e);
}

}else {
hasPermission = accessManager.hasPermission(request.subject(),
var hasPermission = accessManager.hasPermission(request.subject(),
request.resource(),
request.capability());
}
request.capability(),
executionContext.jwt());

if(hasPermission) {
return new GetAuthorizationStatusResponse(request.resource(),
request.subject(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/
@WebProtegeHandler
public class GetAuthorizationStatusHandler implements CommandHandler<GetAuthorizationStatusRequest, GetAuthorizationStatusResponse> {
private final static Logger logger = LoggerFactory.getLogger(GetAuthorizedCapabilitiesHandler.class);
private final static Logger logger = LoggerFactory.getLogger(GetAuthorizationStatusHandler.class);

private final AccessManager accessManager;

Expand All @@ -47,26 +47,11 @@ public Class<GetAuthorizationStatusRequest> getRequestClass() {

@Override
public Mono<GetAuthorizationStatusResponse> handleRequest(GetAuthorizationStatusRequest request, ExecutionContext executionContext) {
var hasPermission = false;
if(request.resource().isApplication()) {
List<RoleId> roleIds;
try {
roleIds = tokenValidator.getTokenClaims(executionContext.jwt()).stream()
.map(RoleId::new)
.toList();
Set<Capability> capabilities = new HashSet<>(builtInRoleOracle.getCapabilitiesAssociatedToRoles(roleIds));
hasPermission = capabilities.contains(request.capability());
var hasPermission = accessManager.hasPermission(request.subject(),
request.resource(),
request.capability(),
executionContext.jwt());

} catch (VerificationException e) {
logger.error("Error getting token claims", e);
throw new RuntimeException(e);
}

}else {
hasPermission = accessManager.hasPermission(request.subject(),
request.resource(),
request.capability());
}
if(hasPermission) {
return Mono.just(new GetAuthorizationStatusResponse(request.resource(),
request.subject(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public Mono<GetAuthorizedCapabilitiesResponse> handleRequest(GetAuthorizedCapabi

try {
//extract any SUPER admin capabilities from token
var roleIds = tokenValidator.getTokenClaims(executionContext.jwt()).stream()
var roleIds = tokenValidator.extractClaimsWithoutVerification(executionContext.jwt()).stream()
.map(RoleId::new)
.toList();
capabilities.addAll(new HashSet<>(builtInRoleOracle.getCapabilitiesAssociatedToRoles(roleIds)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ public Map<String,PublicKey> setUpPublicKey(RestTemplate restTemplate) throws IO
return publicKeys;
}

public Set<String> getTokenClaims(String jwt) throws VerificationException {

/**
* Extracts claims from a JWT token without verification.
* Note: This method should only be used when token verification is not critical.
* For security-critical operations, use getTokenClaims() instead.
*
* @param jwt The JWT token to extract claims from
* @return Set of roles from the token's resource access
* @throws VerificationException if the token is malformed
*/
public Set<String> extractClaimsWithoutVerification(String jwt) throws VerificationException {
TokenVerifier<AccessToken> verifier = TokenVerifier.create(jwt, AccessToken.class);

PublicKey publicKey = publicKeys.get(verifier.getHeader().getKeyId());
if(publicKey == null) {
throw new VerificationException();
}
verifier.publicKey(publicKey);
verifier.verify().withDefaultChecks();
AccessToken token = verifier.getToken();
return token.getResourceAccess().get("webprotege").getRoles();

}
}