Skip to content

Commit

Permalink
Merge branch '1.0.x' into PIN-4392
Browse files Browse the repository at this point in the history
  • Loading branch information
beetlecrunch committed Jan 29, 2024
2 parents a2fb773 + 993389f commit 1f0c01c
Show file tree
Hide file tree
Showing 12 changed files with 797 additions and 7 deletions.
87 changes: 86 additions & 1 deletion src/main/resources/interface-specification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,83 @@ paths:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
/tenants/{tenantId}/attributes/certified:
parameters:
- $ref: '#/components/parameters/CorrelationIdHeader'
- name: tenantId
in: path
description: Tenant id
required: true
schema:
type: string
format: uuid
post:
tags:
- tenant
operationId: addCertifiedAttribute
description: Add a certified attribute to a Tenant by the requester Tenant
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CertifiedTenantAttributeSeed'
required: true
responses:
'200':
description: Updated Tenant
content:
application/json:
schema:
$ref: '#/components/schemas/Tenant'
'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/{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 Expand Up @@ -964,7 +1041,7 @@ components:
format: int32
required:
- results
- totalCount
- totalCount
TenantFeature:
type: object
properties:
Expand Down Expand Up @@ -1061,6 +1138,14 @@ components:
format: date-time
required:
- id
CertifiedTenantAttributeSeed:
type: object
properties:
id:
type: string
format: uuid
required:
- id
UpdateVerifiedTenantAttributeSeed:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import it.pagopa.interop.tenantmanagement.client.model.MailKind.{DIGITAL_ADDRESS
import it.pagopa.interop.tenantmanagement.client.model.{
Certifier => DependencyCertifier,
DeclaredTenantAttribute => DependencyDeclaredTenantAttribute,
CertifiedTenantAttribute => DependencyCertifiedTenantAttribute,
ExternalId => DependencyExternalId,
MailKind => DependencyMailKind,
MailSeed => DependencyMailSeed,
Expand Down Expand Up @@ -71,12 +72,21 @@ object ApiAdapters {
)
}

implicit class TenantUnitTypeWrapper(private val u: TenantUnitType) extends AnyVal {
implicit class TenantUnitTypeWrapper(private val u: TenantUnitType) extends AnyVal {
def toDependency: DependencyTenantUnitType = u match {
case TenantUnitType.AOO => DependencyTenantUnitType.AOO
case TenantUnitType.UO => DependencyTenantUnitType.UO
}
}
implicit class CertifiedTenantAttributeSeedWrapper(private val seed: CertifiedTenantAttributeSeed) extends AnyVal {
def toCreateDependency(now: OffsetDateTime): DependencyTenantAttribute =
DependencyTenantAttribute(
declared = None,
verified = None,
certified =
DependencyCertifiedTenantAttribute(id = seed.id, assignmentTimestamp = now, revocationTimestamp = None).some
)
}

implicit class VerifiedTenantAttributeSeedWrapper(private val seed: VerifiedTenantAttributeSeed) extends AnyVal {
def toCreateDependency(now: OffsetDateTime, requesterId: UUID): DependencyTenantAttribute =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ object TenantApiMarshallerImpl extends TenantApiMarshaller with SprayJsonSupport

override implicit def toEntityMarshallerCertifiedAttributes: ToEntityMarshaller[CertifiedAttributes] =
sprayJsonMarshaller[CertifiedAttributes]

override implicit def fromEntityUnmarshallerCertifiedTenantAttributeSeed
: FromEntityUnmarshaller[CertifiedTenantAttributeSeed] = sprayJsonUnmarshaller[CertifiedTenantAttributeSeed]
}
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,158 @@ final case class TenantApiServiceImpl(
}
}

override def addCertifiedAttribute(tenantId: String, seed: CertifiedTenantAttributeSeed)(implicit
contexts: Seq[(String, String)],
toEntityMarshallerProblem: ToEntityMarshaller[Problem],
toEntityMarshallerTenant: ToEntityMarshaller[Tenant]
): Route = authorize(ADMIN_ROLE, M2M_ROLE) {
val operationLabel = s"Add certified attribute ${seed.id} to tenant $tenantId"
logger.info(operationLabel)

val now: OffsetDateTime = dateTimeSupplier.get()

val result: Future[Tenant] = for {
requesterTenantUuid <- getOrganizationIdFutureUUID(contexts)
targetTenantUuid <- tenantId.toFutureUUID
requesterTenant <- tenantManagementService.getTenantById(requesterTenantUuid).map(_.toManagement)
certifierId <- requesterTenant.features
.collectFirstSome(_.certifier.map(_.certifierId))
.toFuture(TenantIsNotACertifier(requesterTenantUuid))
attribute <- attributeRegistryManagementService
.getAttributeById(seed.id)
_ <- attribute.kind match {
case Certified => Future.unit
case _ => Future.failed(RegistryAttributeIdNotFound(attribute.id))
}
_ <- attribute.origin match {
case Some(value) if (value == certifierId) => Future.unit
case _ =>
Future.failed(
CertifiedAttributeOriginIsNotCompliantWithCertifier(
requesterTenantUuid,
targetTenantUuid,
attribute.origin,
certifierId
)
)
}
targetTenant <- tenantManagementService.getTenantById(targetTenantUuid).map(_.toManagement)
certifiedAttribute = targetTenant.attributes.flatMap(_.certified).find(_.id == seed.id)
updatedTenant <- certifiedAttribute.fold(
tenantManagementService.addTenantAttribute(targetTenantUuid, seed.toCreateDependency(now))
)(attr =>
attr.revocationTimestamp match {
case None => Future.failed(CertifiedAttributeAlreadyAssigned(targetTenantUuid, attribute.id))
case Some(_) =>
tenantManagementService.updateTenantAttribute(
targetTenantUuid,
attribute.id,
attribute.toCertifiedSeed(now)
)
}
)
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(
seed.id,
CompactTenant(updatedTenant.id, updatedTenant.attributes.map(_.toAgreementApi))
)
} yield updatedTenant.toApi

onComplete(result) {
addCertifiedAttributeResponse[Tenant](operationLabel)(addCertifiedAttribute200)
}
}

override def revokeCertifiedAttributeById(tenantId: String, attributeId: String)(implicit
contexts: Seq[(String, String)],
toEntityMarshallerProblem: ToEntityMarshaller[Problem]
): Route = authorize(ADMIN_ROLE, M2M_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)
certifierId <- 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))
}
_ <- attribute.origin match {
case Some(value) if (value == certifierId) => Future.unit
case _ =>
Future.failed(
CertifiedAttributeOriginIsNotCompliantWithCertifier(
requesterTenantUuid,
targetTenantUuid,
attribute.origin,
certifierId
)
)
}
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 @@ -684,7 +836,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 @@ -736,7 +890,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 @@ -19,6 +19,9 @@ package object impl extends SprayJsonSupport with DefaultJsonProtocol {
implicit def m2mAttributeSeedFormat: RootJsonFormat[M2MAttributeSeed] = jsonFormat1(M2MAttributeSeed)
implicit def m2mTenantSeedFormat: RootJsonFormat[M2MTenantSeed] = jsonFormat3(M2MTenantSeed)
implicit def selfcareTenantSeedFormat: RootJsonFormat[SelfcareTenantSeed] = jsonFormat6(SelfcareTenantSeed)
implicit def certifiedTenantAttributeSeedFormat: RootJsonFormat[CertifiedTenantAttributeSeed] = jsonFormat1(
CertifiedTenantAttributeSeed
)

implicit def certifierFormat: RootJsonFormat[Certifier] = jsonFormat1(Certifier)
implicit def tenantFeatureFormat: RootJsonFormat[TenantFeature] = jsonFormat1(TenantFeature)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,32 @@ object ResponseHandlers extends AkkaResponses {
case Failure(ex) => internalServerError(ex, logMessage)
}

def addCertifiedAttributeResponse[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: RegistryAttributeIdNotFound) => badRequest(ex, logMessage)
case Failure(ex: CertifiedAttributeAlreadyAssigned) => badRequest(ex, logMessage)
case Failure(ex: CertifiedAttributeOriginIsNotCompliantWithCertifier) => forbidden(ex, logMessage)
case Failure(ex: TenantIsNotACertifier) => forbidden(ex, logMessage)
case Failure(ex: TenantByIdNotFound) => notFound(ex, logMessage)
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: TenantIsNotACertifier) => forbidden(ex, logMessage)
case Failure(ex: CertifiedAttributeOriginIsNotCompliantWithCertifier) => forbidden(ex, logMessage)
case Failure(ex: CertifiedAttributeNotFoundInTenant) => notFound(ex, logMessage)
case Failure(ex: TenantByIdNotFound) => notFound(ex, logMessage)
case Failure(ex: RegistryAttributeIdNotFound) => notFound(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
Loading

0 comments on commit 1f0c01c

Please sign in to comment.