Skip to content

Commit

Permalink
fix: use context user as a po for k8s resources
Browse files Browse the repository at this point in the history
  • Loading branch information
a-cordier committed Sep 20, 2024
1 parent 1e5a8c2 commit 61477eb
Show file tree
Hide file tree
Showing 18 changed files with 313 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ <h3>Members</h3>
<th mat-header-cell *matHeaderCellDef>Role</th>
<td mat-cell *matCellDef="let member">
<mat-form-field>
<mat-select [formControlName]="member.id">
<mat-select [formControlName]="member.id" [disabled]="isKubernetesOrigin">
<mat-option *ngFor="let roleName of roleNames" [value]="roleName" [disabled]="roleName === 'PRIMARY_OWNER'">{{
roleName
}}</mat-option>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import inmemory.CRDMembersDomainServiceInMemory;
import inmemory.CategoryQueryServiceInMemory;
import inmemory.GroupQueryServiceInMemory;
import inmemory.MembershipQueryServiceInMemory;
import inmemory.PageSourceDomainServiceInMemory;
import inmemory.RoleQueryServiceInMemory;
import inmemory.UserDomainServiceInMemory;
Expand Down Expand Up @@ -454,14 +453,13 @@ public ValidateApiCRDUseCase validateApiCRDUseCase(
ValidatePageSourceDomainService validatePageSourceDomainService,
ValidatePageAccessControlsDomainService validatePageAccessControlsDomainService,
DocumentationValidationDomainService validationDomainService,
RoleQueryServiceInMemory roleQueryService,
MembershipQueryServiceInMemory membershipQueryService
RoleQueryServiceInMemory roleQueryService
) {
return new ValidateApiCRDUseCase(
new ValidateApiCRDDomainService(
new ValidateCategoryIdsDomainService(categoryQueryService),
verifyApiPathDomainService,
new ValidateCRDMembersDomainService(userDomainService, roleQueryService, membershipQueryService),
new ValidateCRDMembersDomainService(userDomainService, roleQueryService),
new ValidateGroupsDomainService(groupQueryService),
validateResourceDomainService,
new ValidatePagesDomainService(
Expand All @@ -478,13 +476,12 @@ public ValidateApplicationCRDUseCase validateApplicationCRDUseCase(
GroupQueryService groupQueryService,
UserDomainService userDomainService,
RoleQueryServiceInMemory roleQueryService,
MembershipQueryServiceInMemory membershipQueryService,
ValidateApplicationSettingsDomainService validateApplicationSettingsDomainService
) {
return new ValidateApplicationCRDUseCase(
new ValidateApplicationCRDDomainService(
new ValidateGroupsDomainService(groupQueryService),
new ValidateCRDMembersDomainService(userDomainService, roleQueryService, membershipQueryService),
new ValidateCRDMembersDomainService(userDomainService, roleQueryService),
validateApplicationSettingsDomainService
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public Validator.Result<ValidateApiCRDDomainService.Input> validateAndSanitize(V
membersValidator
.validateAndSanitize(
new ValidateCRDMembersDomainService.Input(
input.auditInfo().organizationId(),
input.auditInfo(),
input.spec.getId(),
MembershipReferenceType.API,
input.spec().getMembers()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public Result<ValidateApplicationCRDDomainService.Input> validateAndSanitize(Val
membersValidator
.validateAndSanitize(
new ValidateCRDMembersDomainService.Input(
input.auditInfo.organizationId(),
input.auditInfo,
input.spec.getId(),
MembershipReferenceType.APPLICATION,
input.spec.getMembers()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
import static io.gravitee.rest.api.service.common.ReferenceContext.Type.ORGANIZATION;

import io.gravitee.apim.core.DomainService;
import io.gravitee.apim.core.member.exception.UnsupportedMembershipReferencer;
import io.gravitee.apim.core.audit.model.AuditInfo;
import io.gravitee.apim.core.exception.TechnicalDomainException;
import io.gravitee.apim.core.member.model.MembershipReferenceType;
import io.gravitee.apim.core.member.model.crd.MemberCRD;
import io.gravitee.apim.core.membership.model.Membership;
import io.gravitee.apim.core.membership.model.Role;
import io.gravitee.apim.core.membership.query_service.MembershipQueryService;
import io.gravitee.apim.core.membership.query_service.RoleQueryService;
import io.gravitee.apim.core.user.domain_service.UserDomainService;
import io.gravitee.apim.core.validation.Validator;
Expand All @@ -34,29 +33,30 @@
import java.util.Optional;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
* @author Antoine CORDIER (antoine.cordier at graviteesource.com)
* @author GraviteeSource Team
*/
@Slf4j
@DomainService
@RequiredArgsConstructor
public class ValidateCRDMembersDomainService implements Validator<ValidateCRDMembersDomainService.Input> {

private final UserDomainService userDomainService;
private final RoleQueryService roleQueryService;
private final MembershipQueryService membershipQueryService;

public record Input(String organizationId, String referenceId, MembershipReferenceType referenceType, Set<MemberCRD> members)
public record Input(AuditInfo auditInfo, String referenceId, MembershipReferenceType referenceType, Set<MemberCRD> members)
implements Validator.Input {
Input sanitized(Set<MemberCRD> sanitizedMembers) {
return new Input(organizationId, referenceId, referenceType, sanitizedMembers);
return new Input(auditInfo, referenceId, referenceType, sanitizedMembers);
}
}

@Override
public Result<Input> validateAndSanitize(Input input) {
var sanitizedMembers = input.members == null ? Set.<MemberCRD>of() : new HashSet<>(input.members);
var sanitizedMembers = input.members == null ? new HashSet<MemberCRD>() : new HashSet<>(input.members);
var errors = new ArrayList<Error>();

validateAndSanitizeMemberId(input, sanitizedMembers, errors);
Expand All @@ -71,7 +71,7 @@ private void validateAndSanitizeMemberId(Input input, Set<MemberCRD> sanitized,
while (members.hasNext()) {
var member = members.next();
userDomainService
.findBySource(input.organizationId, member.getSource(), member.getSourceId())
.findBySource(input.auditInfo.organizationId(), member.getSource(), member.getSourceId())
.ifPresentOrElse(
user -> member.setId(user.getId()),
() -> {
Expand All @@ -80,7 +80,7 @@ private void validateAndSanitizeMemberId(Input input, Set<MemberCRD> sanitized,
"member [%s] of source [%s] could not be found in organization [%s]",
member.getSourceId(),
member.getSource(),
input.organizationId()
input.auditInfo.organizationId()
)
);
members.remove();
Expand All @@ -90,82 +90,42 @@ private void validateAndSanitizeMemberId(Input input, Set<MemberCRD> sanitized,
}

private void validateMemberRole(Input input, Set<MemberCRD> sanitized, ArrayList<Error> errors) {
var members = sanitized.iterator();
while (members.hasNext()) {
var member = members.next();
Role role = null;
switch (input.referenceType) {
case APPLICATION:
Optional<Role> applicationRole = roleQueryService.findApplicationRole(
member.getRole(),
new ReferenceContext(ORGANIZATION, input.organizationId())
);
if (applicationRole.isPresent()) {
role = applicationRole.get();
}
break;
case API:
Optional<Role> apiRole = roleQueryService.findApiRole(
member.getRole(),
new ReferenceContext(ORGANIZATION, input.organizationId())
);
if (apiRole.isPresent()) {
role = apiRole.get();
}
break;
default:
throw new UnsupportedMembershipReferencer(
String.format("membership reference is not supported [%s]", input.referenceType)
);
}
if (role == null) {
errors.add(Error.warning("member role [%s] doesn't exist", member.getRole()));
}
for (var member : sanitized) {
findRole(input.auditInfo.organizationId(), input.referenceType, member.getRole())
.ifPresentOrElse(
role -> log.debug("Role {} found for scope {}", member.getRole(), input.referenceType),
() -> errors.add(Error.warning("member role [%s] doesn't exist", member.getRole()))
);
}
}

private void validatePrimaryOwner(Input input, Set<MemberCRD> sanitized, ArrayList<Error> errors) {
Optional<Membership> membership;
switch (input.referenceType) {
case APPLICATION -> {
String poRole = roleQueryService
.findApplicationRole(PRIMARY_OWNER.name(), new ReferenceContext(ORGANIZATION, input.organizationId))
.orElseThrow(() -> new RuntimeException("application primary owner role not found"))
.getId();
membership =
membershipQueryService
.findByReference(Membership.ReferenceType.APPLICATION, input.referenceId)
.stream()
.filter(m -> m.getRoleId().equals(poRole))
.findFirst();
}
case API -> {
String poRole = roleQueryService
.findApiRole(PRIMARY_OWNER.name(), new ReferenceContext(ORGANIZATION, input.organizationId))
.orElseThrow(() -> new RuntimeException("api primary owner role not found"))
.getId();
membership =
membershipQueryService
.findByReference(Membership.ReferenceType.API, input.referenceId)
.stream()
.filter(m -> m.getRoleId().equals(poRole))
.findFirst();
}
default -> throw new UnsupportedMembershipReferencer(
String.format("membership reference type [%s] doesn't exist", input.referenceType.name())
);
}
private Optional<Role> findRole(String organizationId, MembershipReferenceType scope, String role) {
var context = new ReferenceContext(ORGANIZATION, organizationId);
return switch (scope) {
case API -> roleQueryService.findApiRole(role, context);
case APPLICATION -> roleQueryService.findApplicationRole(role, context);
default -> throw new TechnicalDomainException(String.format("Role scope [%s] is not supported", scope));
};
}

private void validatePrimaryOwner(Input input, Set<MemberCRD> sanitized, ArrayList<Error> errors) {
var actor = input.auditInfo.actor();
var members = sanitized.iterator();

while (members.hasNext()) {
var member = members.next();

log.debug("checking that member {} is not defined as a primary owner", member.getSourceId());

if (PRIMARY_OWNER.name().equals(member.getRole())) {
errors.add(Error.severe("setting a member with the primary owner role is not allowed"));
members.remove();
}

if (membership.isPresent() && membership.get().getMemberId().equals(member.getId())) {
errors.add(Error.severe("can not change the role of exiting primary owner [%s]", member.getSourceId()));
log.debug("checking that member {} is not the authenticated user who will be set as a primary owner", member.getSourceId());

if (actor.userId().equals(member.getId())) {
errors.add(Error.severe("can not change the role of primary owner [%s]", member.getSourceId()));
members.remove();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.gravitee.apim.infra.domain_service.member;

import io.gravitee.apim.core.audit.model.AuditInfo;
import io.gravitee.apim.core.exception.TechnicalDomainException;
import io.gravitee.apim.core.member.domain_service.CRDMembersDomainService;
import io.gravitee.apim.core.member.model.crd.MemberCRD;
import io.gravitee.apim.core.utils.StringUtils;
Expand All @@ -29,6 +30,7 @@
import io.gravitee.rest.api.service.common.ExecutionContext;
import io.gravitee.rest.api.service.exceptions.RoleNotFoundException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
Expand All @@ -42,8 +44,8 @@
* This class assumes validated and sanitized input and is thus to be used by kubernetes resources use cases only
*/
@Slf4j
@RequiredArgsConstructor
@Service
@RequiredArgsConstructor
public class CRDMembersDomainServiceImpl implements CRDMembersDomainService {

private final MembershipService membershipService;
Expand All @@ -53,12 +55,14 @@ public class CRDMembersDomainServiceImpl implements CRDMembersDomainService {
public void updateApiMembers(AuditInfo auditInfo, String apiId, Set<MemberCRD> members) {
updateMembers(auditInfo, apiId, RoleScope.API, MembershipReferenceType.API, members);
deleteOrphans(auditInfo, apiId, RoleScope.API, MembershipReferenceType.API, members);
transferOwnerShip(auditInfo, apiId, RoleScope.API, MembershipReferenceType.API);
}

@Override
public void updateApplicationMembers(AuditInfo auditInfo, String applicationId, Set<MemberCRD> members) {
updateMembers(auditInfo, applicationId, RoleScope.APPLICATION, MembershipReferenceType.APPLICATION, members);
deleteOrphans(auditInfo, applicationId, RoleScope.APPLICATION, MembershipReferenceType.APPLICATION, members);
transferOwnerShip(auditInfo, applicationId, RoleScope.APPLICATION, MembershipReferenceType.APPLICATION);
}

private void updateMembers(
Expand Down Expand Up @@ -108,6 +112,8 @@ private void deleteOrphans(
MembershipReferenceType referenceType,
Set<MemberCRD> members
) {
log.debug("Deleting orphan members");

var executionContext = new ExecutionContext(auditInfo.organizationId(), auditInfo.environmentId());

var poRole = roleService.findPrimaryOwnerRoleByOrganization(auditInfo.organizationId(), roleScope);
Expand All @@ -130,6 +136,26 @@ private void deleteOrphans(
);
}

private void transferOwnerShip(AuditInfo auditInfo, String referenceId, RoleScope roleScope, MembershipReferenceType referenceType) {
log.debug("Transferring owner ship to authenticated user {}", auditInfo.actor().userSourceId());

var currentPrimaryOwner = membershipService.getPrimaryOwner(auditInfo.organizationId(), referenceType, referenceId);

if (currentPrimaryOwner != null && currentPrimaryOwner.getMemberId().equals(auditInfo.actor().userId())) {
log.debug("User {} is already the primary owner for {} {}", auditInfo.actor().userSourceId(), referenceType, referenceId);
return;
}

var executionContext = new ExecutionContext(auditInfo.organizationId(), auditInfo.environmentId());
var primaryOwner = new MembershipService.MembershipMember(auditInfo.actor().userId(), null, MembershipMemberType.USER);

switch (roleScope) {
case API -> membershipService.transferApiOwnership(executionContext, referenceId, primaryOwner, List.of());
case APPLICATION -> membershipService.transferApplicationOwnership(executionContext, referenceId, primaryOwner, List.of());
default -> throw new TechnicalDomainException(String.format("Unknown role scope [%s]", roleScope));
}
}

private RoleEntity findRoleEntity(String organizationId, RoleScope roleScope, String roleNameOrId, RoleEntity defaultRole) {
try {
return roleService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ public String toString() {
}
}

class MembershipMember {
public class MembershipMember {

private final String memberId;
private final String reference;
Expand Down
Loading

0 comments on commit 61477eb

Please sign in to comment.