Skip to content

Commit

Permalink
restrict env deletion before handle env deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
quentinovega committed Aug 28, 2023
1 parent 6171299 commit dfc924b
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 41 deletions.
17 changes: 1 addition & 16 deletions daikoku/app/controllers/ApiController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4153,22 +4153,7 @@ class ApiController(
api <- EitherT.fromOptionF(env.dataStore.apiRepo.forTenant(ctx.tenant)
.findOneNotDeleted(Json.obj("_id" -> apiId, "team" -> team.id.asJson, "currentVersion" -> version)), AppError.ApiNotFound)
plan <- EitherT.fromOptionF[Future, AppError, UsagePlan](env.dataStore.usagePlanRepo.forTenant(ctx.tenant).findById(planId), AppError.PlanNotFound)
updatedApi = api.copy(possibleUsagePlans = api.possibleUsagePlans.filter(pp => pp.value != planId))
_ <- EitherT.liftF(apiService.deleteApiPlansSubscriptions(Seq(plan), api, ctx.tenant, ctx.user))
_ <- EitherT.liftF(env.dataStore.apiRepo.forTenant(ctx.tenant).save(updatedApi))
_ <- EitherT.liftF(env.dataStore.usagePlanRepo.forTenant(ctx.tenant).deleteByIdLogically(planId))
_ <- EitherT.liftF(env.dataStore.operationRepo.forTenant(ctx.tenant).save(
Operation(
DatastoreId(IdGenerator.token(24)),
tenant = ctx.tenant.id,
itemId = plan.id.value,
itemType = ItemType.ThirdPartyProduct,
action = OperationAction.Delete,
payload = Json.obj(
"paymentSettings" -> plan.paymentSettings.map(_.asJson).getOrElse(JsNull).as[JsValue],
).some
)
))
updatedApi <- apiService.deleteUsagePlan(plan, api, ctx.tenant, ctx.user)
} yield Ok(updatedApi.asJson)

value.leftMap(_.render()).merge
Expand Down
74 changes: 52 additions & 22 deletions daikoku/app/controllers/TenantController.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package fr.maif.otoroshi.daikoku.ctrls

import akka.http.scaladsl.util.FastFuture
import cats.data.EitherT
import com.nimbusds.jose.jwk.KeyType
import controllers.AppError
import fr.maif.otoroshi.daikoku.actions.{DaikokuAction, DaikokuActionMaybeWithGuest}
import fr.maif.otoroshi.daikoku.audit.AuditTrailEvent
import fr.maif.otoroshi.daikoku.ctrls.authorizations.async._
Expand All @@ -26,6 +28,7 @@ import scala.util.Try

class TenantController(DaikokuAction: DaikokuAction,
DaikokuActionMaybeWithGuest: DaikokuActionMaybeWithGuest,
apiService: ApiService,
env: Env,
cc: ControllerComponents,
translator: Translator)
Expand Down Expand Up @@ -300,19 +303,44 @@ class TenantController(DaikokuAction: DaikokuAction,
FastFuture.successful(
BadRequest(Json.obj("error" -> "Error while parsing payload",
"msg" -> e.toString)))
case JsSuccess(tenant, _) =>
ctx.setCtxValue("tenant.name", tenant.name)
ctx.setCtxValue("tenant.id", tenant.id)
case JsSuccess(updatedTenant, _) =>
ctx.setCtxValue("tenant.name", updatedTenant.name)
ctx.setCtxValue("tenant.id", updatedTenant.id)

//FIXME: if env is deleted => delete all associated env/plan
def checkEnvironments(oldTenant: Tenant): EitherT[Future, AppError, Unit] = {
updatedTenant.display match {
case TenantDisplay.Environment =>
val deletedEnvs = oldTenant.environments.diff(updatedTenant.environments)
EitherT.cond(deletedEnvs.nonEmpty, (), AppError.EntityConflict("tenant's environment couldn't deleted"))
case TenantDisplay.Default =>
EitherT.pure[Future, AppError](())
}
}

// def checkEnvironments(): Future[Unit] = {
// tenant.display match {
// case TenantDisplay.Environment => {
// }
// case TenantDisplay.Default => FastFuture.successful(())
// def checkEnvironments(oldTenant: Tenant): EitherT[Future, AppError, Unit] = {
// updatedTenant.display match {
// case TenantDisplay.Environment =>
// val deletedEnvs = oldTenant.environments.diff(updatedTenant.environments)
// EitherT.liftF(Future.sequence(deletedEnvs.map(name => {
// for {
// plans <- env.dataStore.usagePlanRepo.forTenant(ctx.tenant).find(Json.obj(
// "customName" -> name
// ))
// _ <- Future.sequence(plans.map(plan => {
// for {
// api <- EitherT.fromOptionF(env.dataStore.apiRepo.forTenant(ctx.tenant)
// .findOne(Json.obj("possibleUsagePlans" -> plan.id.asJson)), AppError.ApiNotFound)
// _ <- apiService.deleteUsagePlan(plan, api, ctx.tenant, ctx.user)
// } yield api
// }).map(_.value))
// } yield ()
// })).map(_ -> ()))
// case TenantDisplay.Default => EitherT.pure[Future, AppError](())
// }
// }

tenant.tenantMode match {
updatedTenant.tenantMode match {
case Some(value) =>
value match {
case TenantMode.Maintenance | TenantMode.Construction =>
Expand All @@ -323,23 +351,25 @@ class TenantController(DaikokuAction: DaikokuAction,
case _ =>
}

//TODO: in case of display-mode environment, check if some env are deleted and deleted all associated env/plan
for {
_ <- env.dataStore.tenantRepo.save(tenant)
// _ <- checkEnvironments()
_ <- env.dataStore.teamRepo
.forTenant(tenant)
(for {
oldTenant <- EitherT.fromOptionF(env.dataStore.tenantRepo.findByIdNotDeleted(updatedTenant.id), AppError.TenantNotFound)
_ <- checkEnvironments(oldTenant)
_ <- EitherT.liftF[Future, AppError, Boolean](env.dataStore.tenantRepo.save(updatedTenant))
_ <- EitherT.liftF[Future, AppError, Boolean](env.dataStore.teamRepo
.forTenant(updatedTenant)
.save(
adminTeam.copy(
name = s"${tenant.humanReadableId}-admin-team",
contact = tenant.contact,
avatar = tenant.style.map(_.logo)
))
name = s"${updatedTenant.humanReadableId}-admin-team",
contact = updatedTenant.contact,
avatar = updatedTenant.style.map(_.logo)
)))
} yield {
Ok(
Json.obj("tenant" -> tenant.asJsonWithJwt,
"uiPayload" -> tenant.toUiPayload(env)))
}
Json.obj("tenant" -> updatedTenant.asJsonWithJwt,
"uiPayload" -> updatedTenant.toUiPayload(env)))
})
.leftMap(_.render())
.merge
}
}
}
Expand Down
24 changes: 23 additions & 1 deletion daikoku/app/utils/ApiService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import jobs.{ApiKeyStatsJob, OtoroshiVerifierJob}
import org.joda.time.DateTime
import play.api.i18n.{Lang, MessagesApi}
import play.api.libs.json.{JsArray, JsBoolean, JsNull, JsNumber, JsObject, JsString, JsValue, Json}
import play.api.mvc.Result
import play.api.mvc.{AnyContent, Result}
import play.api.mvc.Results.Ok
import reactivemongo.bson.BSONObjectID

Expand Down Expand Up @@ -783,6 +783,28 @@ class ApiService(env: Env,
.map(_ => plan)
}

def deleteUsagePlan(plan: UsagePlan, api: Api, tenant: Tenant, user: User): EitherT[Future, AppError, Api] = {
val updatedApi = api.copy(possibleUsagePlans = api.possibleUsagePlans.filter(pp => pp != plan.id))
for {
_ <- EitherT.liftF(deleteApiPlansSubscriptions(Seq(plan), api, tenant, user))
_ <- EitherT.liftF(env.dataStore.apiRepo.forTenant(tenant).save(updatedApi))
_ <- EitherT.liftF(env.dataStore.usagePlanRepo.forTenant(tenant).deleteByIdLogically(plan.id))
//FIXME: save operation just if needed
_ <- EitherT.liftF(env.dataStore.operationRepo.forTenant(tenant).save(
Operation(
DatastoreId(IdGenerator.token(24)),
tenant = tenant.id,
itemId = plan.id.value,
itemType = ItemType.ThirdPartyProduct,
action = OperationAction.Delete,
payload = Json.obj(
"paymentSettings" -> plan.paymentSettings.map(_.asJson).getOrElse(JsNull).as[JsValue],
).some
)
))
} yield updatedApi
}

def deleteApiPlansSubscriptions(
plans: Seq[UsagePlan],
api: Api,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ export const DisplayForm = (props: UpdateFormProps) => {
label: translate('display-mode.label'),
help: translate('display-mode.help')
},
//TODO: draw a custom component with is default flag
environments: {
type: type.string,
array: true,
label: translate('display-mode.environments.label'),
visible: ({ rawValues }) => rawValues.isPrivate,
constraints: [
constraints.required('constraints.required.value')
constraints.required(translate('constraints.required.value'))
],
onChange: ({ value }) => {
const diff = props.tenant.environments.filter(x => !value.map(t => t.value).includes(x));
Expand Down

0 comments on commit dfc924b

Please sign in to comment.