Skip to content

Commit

Permalink
PIN-4371 BKE - Tenant process added route to revoke certified attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
nttdata-rtorsoli committed Jan 16, 2024
1 parent e910ad6 commit 97ebbb0
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 22 deletions.
37 changes: 37 additions & 0 deletions src/main/resources/interface-specification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,43 @@ paths:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
/tenants/{tenantId}/attributes/certified/{attributeId}:
parameters:
- $ref: '#/components/parameters/CorrelationIdHeader'
- name: tenantId
in: path
description: Tenant id
required: true
schema:
type: string
format: uuid
- name: attributeId
in: path
description: Attribute id
required: true
schema:
type: string
format: uuid
delete:
tags:
- tenant
operationId: revokeCertifiedAttributeById
description: Revoke a certified attribute to a Tenant by the requester Tenant
responses:
'204':
description: Certified Attribute revoked
'403':
description: Forbidden
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'404':
description: Tenant Not Found
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
/tenants/attributes/declared:
parameters:
- $ref: '#/components/parameters/CorrelationIdHeader'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,72 @@ final case class TenantApiServiceImpl(
}
}

override def revokeCertifiedAttributeById(tenantId: String, attributeId: String)(implicit
contexts: Seq[(String, String)],
toEntityMarshallerProblem: ToEntityMarshaller[Problem]
): Route = authorize(ADMIN_ROLE) {
val operationLabel = s"Revoke certified attribute ${attributeId} to tenant $tenantId"
logger.info(operationLabel)

val now: OffsetDateTime = dateTimeSupplier.get()

val result: Future[Unit] = for {
requesterTenantUuid <- getOrganizationIdFutureUUID(contexts)
targetTenantUuid <- tenantId.toFutureUUID
attributeUuid <- attributeId.toFutureUUID
requesterTenant <- tenantManagementService.getTenantById(requesterTenantUuid).map(_.toManagement)
_ <- requesterTenant.features
.collectFirstSome(_.certifier.map(_.certifierId))
.toFuture(TenantIsNotACertifier(requesterTenantUuid))
attribute <- attributeRegistryManagementService
.getAttributeById(attributeUuid)
_ <- attribute.kind match {
case Certified => Future.unit
case _ => Future.failed(RegistryAttributeIdNotFound(attribute.id))
}
targetTenant <- tenantManagementService.getTenantById(targetTenantUuid).map(_.toManagement)
attributeToRevoke <- targetTenant.attributes
.mapFilter(_.certified)
.find(_.id == attributeUuid)
.toFuture(
CertifiedAttributeNotFoundInTenant(
targetTenant.id,
attribute.id,
attribute.origin.getOrElse("none"),
attribute.code.getOrElse("none")
)
)
revokedAttribute = attributeToRevoke.copy(revocationTimestamp = now.some)
updatedTenant <- tenantManagementService
.updateTenantAttribute(
targetTenant.id,
attributeToRevoke.id,
DependencyTenantAttribute(certified = revokedAttribute.some)
)
tenantKind <- getTenantKindLoadingCertifiedAttributes(updatedTenant.attributes, updatedTenant.externalId)
updatedTenant <- updatedTenant.kind match {
case Some(x) if (x == tenantKind) => Future.successful(updatedTenant)
case _ =>
tenantManagementService.updateTenant(
updatedTenant.id,
DependencyTenantDelta(
selfcareId = updatedTenant.selfcareId,
features = updatedTenant.features,
kind = tenantKind
)
)
}
_ <- agreementProcessService.computeAgreementsByAttribute(
attributeUuid,
CompactTenant(updatedTenant.id, updatedTenant.attributes.map(_.toAgreementApi))
)
} yield ()

onComplete(result) {
revokeCertifiedAttributeByIdResponse[Unit](operationLabel)(_ => revokeCertifiedAttributeById204)
}
}

override def verifyVerifiedAttribute(tenantId: String, seed: VerifiedTenantAttributeSeed)(implicit
contexts: Seq[(String, String)],
toEntityMarshallerProblem: ToEntityMarshaller[Problem],
Expand Down Expand Up @@ -723,7 +789,9 @@ final case class TenantApiServiceImpl(
attributeToModify <- tenantToModify.attributes
.mapFilter(_.certified)
.find(_.id == attributeIdToRevoke)
.toFuture(CertifiedAttributeNotFoundInTenant(tenantToModify.id, attributeOrigin, attributeExternalId))
.toFuture(
CertifiedAttributeNotFoundInTenant(tenantToModify.id, attributeIdToRevoke, attributeOrigin, attributeExternalId)
)
modifiedAttribute = attributeToModify.copy(revocationTimestamp = dateTimeSupplier.get().some)
updatedTenant <- tenantManagementService
.updateTenantAttribute(
Expand Down Expand Up @@ -775,7 +843,6 @@ final case class TenantApiServiceImpl(
attributeToAssign.toCertifiedSeed(now)
)
)

tenantKind <- getTenantKindLoadingCertifiedAttributes(updatedTenant.attributes, updatedTenant.externalId)
updatedTenant <- updatedTenant.kind match {
case Some(x) if (x == tenantKind) => Future.successful(updatedTenant)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ object ResponseHandlers extends AkkaResponses {
case Failure(ex) => internalServerError(ex, logMessage)
}

def revokeCertifiedAttributeByIdResponse[T](logMessage: String)(
success: T => Route
)(result: Try[T])(implicit contexts: Seq[(String, String)], logger: LoggerTakingImplicit[ContextFieldsToLog]): Route =
result match {
case Success(s) => success(s)
case Failure(ex: CertifiedAttributeNotFoundInTenant) => badRequest(ex, logMessage)
case Failure(ex: TenantIsNotACertifier) => forbidden(ex, logMessage)
case Failure(ex: TenantByIdNotFound) => notFound(ex, logMessage)
case Failure(ex: CertifiedAttributeAlreadyExists) => conflict(ex, logMessage)
case Failure(ex) => internalServerError(ex, logMessage)
}

def verifyVerifiedAttributeResponse[T](logMessage: String)(
success: T => Route
)(result: Try[T])(implicit contexts: Seq[(String, String)], logger: LoggerTakingImplicit[ContextFieldsToLog]): Route =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,14 @@ object TenantProcessErrors {

final case class TenantByIdNotFound(tenantId: UUID) extends ComponentError("0011", s"Tenant $tenantId not found")

final case class CertifiedAttributeNotFoundInTenant(tenantId: UUID, attributeOrigin: String, attributeCode: String)
extends ComponentError(
final case class CertifiedAttributeNotFoundInTenant(
tenantId: UUID,
attributeId: UUID,
attributeOrigin: String,
attributeCode: String
) extends ComponentError(
"0012",
s"Certified Attribute ($attributeOrigin, $attributeCode) not found in tenant $tenantId"
s"Certified Attribute $attributeId ($attributeOrigin, $attributeCode) not found in tenant $tenantId"
)

final case class DeclaredAttributeNotFoundInTenant(tenantId: UUID, attributeId: UUID)
Expand Down
9 changes: 8 additions & 1 deletion src/test/resources/authz.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@
"roles": [
"admin"
]
}
},
{
"route": "revokeCertifiedAttributeById",
"verb": "DELETE",
"roles": [
"admin"
]
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,13 @@ class TenantApiServiceAuthzSpec extends ClusteredMUnitRouteTest with SpecData {
}
)
}

test("Tenant api should accept authorized roles for revokeCertifiedAttributeById") {
validateAuthorization(
endpoints("revokeCertifiedAttributeById"),
{ implicit c: Seq[(String, String)] =>
tenantService.revokeCertifiedAttributeById(UUID.randomUUID().toString, UUID.randomUUID().toString)
}
)
}
}
Loading

0 comments on commit 97ebbb0

Please sign in to comment.