Skip to content

Commit

Permalink
PIN-3815: Added internal endpoint for assigning Certified attributes (#…
Browse files Browse the repository at this point in the history
…75)

Co-authored-by: Fabrizio Musella <[email protected]>
Co-authored-by: Alessio Gallitano <[email protected]>
  • Loading branch information
3 people authored Aug 23, 2023
1 parent 51a9bee commit 8d8f8a9
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 1 deletion.
20 changes: 20 additions & 0 deletions src/main/resources/interface-specification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,26 @@ paths:
required: true
schema:
type: string
post:
tags:
- tenant
operationId: internalAssignCertifiedAttribute
description: Assigns a Certified attribute to the requesting Tenant
responses:
'204':
description: Updated Tenant
'400':
description: Bad Request
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'409':
description: Attribute already assigned to Tenant
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
delete:
tags:
- tenant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@ object AttributeRegistryManagementAdapters {
CertifiedTenantAttribute(id = a.id, assignmentTimestamp = now, revocationTimestamp = None).some
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,35 @@ final case class TenantApiServiceImpl(
}
}

override def internalAssignCertifiedAttribute(
tenantOrigin: String,
tenantExternalId: String,
attributeOrigin: String,
attributeExternalId: String
)(implicit contexts: Seq[(String, String)], toEntityMarshallerProblem: ToEntityMarshaller[Problem]): Route =
authorize(INTERNAL_ROLE) {
val operationLabel =
s"Assigning certified attribute ($attributeOrigin/$attributeExternalId) to tenant ($tenantOrigin/$tenantExternalId)"
logger.info(operationLabel)

val result: Future[Unit] = for {
(tenant, attributeId) <- assignCertifiedAttribute(
tenantOrigin = tenantOrigin,
tenantExternalId = tenantExternalId,
attributeOrigin = attributeOrigin,
attributeExternalId = attributeExternalId
)
_ <- agreementProcessService.computeAgreementsByAttribute(
attributeId,
CompactTenant(tenant.id, tenant.attributes.map(_.toAgreementApi))
)
} yield ()

onComplete(result) {
internalAssignCertifiedAttributeResponse[Unit](operationLabel)(_ => internalAssignCertifiedAttribute204)
}
}

override def addDeclaredAttribute(seed: DeclaredTenantAttributeSeed)(implicit
contexts: Seq[(String, String)],
toEntityMarshallerProblem: ToEntityMarshaller[Problem],
Expand Down Expand Up @@ -692,6 +721,42 @@ final case class TenantApiServiceImpl(
}
} yield (updatedTenant, attributeToModify)

private def assignCertifiedAttribute(
tenantOrigin: String,
tenantExternalId: String,
attributeOrigin: String,
attributeExternalId: String
)(implicit contexts: Seq[(String, String)]): Future[(DependencyTenant, UUID)] = for {
tenantToModify <- tenantManagementService
.getTenantByExternalId(PersistentExternalId(tenantOrigin, tenantExternalId))
.map(_.toManagement)
attributeToAssign <- attributeRegistryManagementService
.getAttributeByExternalCode(attributeOrigin, attributeExternalId)
maybeAttribute = tenantToModify.attributes
.flatMap(_.certified)
.filter(_.revocationTimestamp.isEmpty)
.find(_.id == attributeToAssign.id)
now = dateTimeSupplier.get()
updatedTenant <- maybeAttribute.fold(
tenantManagementService.addTenantAttribute(tenantToModify.id, attributeToAssign.toCertifiedSeed(now))
)(_ => Future.failed(CertifiedAttributeAlreadyInTenant(tenantToModify.id, attributeOrigin, attributeExternalId)))

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,
mails = updatedTenant.mails.map(_.toSeed),
kind = tenantKind
)
)
}
} yield (updatedTenant, attributeToAssign.id)

private def assertAttributeVerificationAllowed(producerId: UUID, consumerId: UUID, attributeId: UUID): Future[Unit] =
assertVerifiedAttributeOperationAllowed(
producerId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,15 @@ object ResponseHandlers extends AkkaResponses {
case Failure(ex: TenantByIdNotFound) => notFound(ex, logMessage)
case Failure(ex) => internalServerError(ex, logMessage)
}

def internalAssignCertifiedAttributeResponse[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: CertifiedAttributeAlreadyInTenant) => conflict(ex, logMessage)
case Failure(ex: TenantNotFound) => notFound(ex, logMessage)
case Failure(ex: RegistryAttributeNotFound) => notFound(ex, logMessage)
case Failure(ex) => internalServerError(ex, logMessage)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,9 @@ object TenantProcessErrors {
final case class SelcareIdNotFound(selfcareId: UUID)
extends ComponentError("0021", s"Tenant with selfcare id ${selfcareId.toString} not found in the catalog")

final case class CertifiedAttributeAlreadyInTenant(tenantId: UUID, attributeOrigin: String, attributeCode: String)
extends ComponentError(
"0022",
s"Certified Attribute ($attributeOrigin, $attributeCode) already in tenant $tenantId"
)
}
7 changes: 7 additions & 0 deletions src/test/resources/authz.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@
"internal"
]
},
{
"route": "internalAssignCertifiedAttribute",
"verb": "POST",
"roles": [
"internal"
]
},
{
"route": "m2mUpsertTenant",
"verb": "POST",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ class TenantApiServiceAuthzSpec extends ClusteredMUnitRouteTest with SpecData {
)
}

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

test("Tenant api should accept authorized roles for m2mUpsertTenant") {
validateAuthorization(
endpoints("m2mUpsertTenant"),
Expand Down

0 comments on commit 8d8f8a9

Please sign in to comment.