diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/AclsRoutes.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/AclsRoutes.scala index 3e9d46d331..c5541f0270 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/AclsRoutes.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/AclsRoutes.scala @@ -31,8 +31,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.model.ResourceF._ import ch.epfl.bluebrain.nexus.delta.sdk.model.search.SearchResults import ch.epfl.bluebrain.nexus.delta.sdk.model.search.SearchResults.searchResultsJsonLdEncoder import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.{acls => aclsPermissions} -import ch.epfl.bluebrain.nexus.delta.sdk.permissions.model.Permission -import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Identity, Label, ProjectRef} +import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Label, ProjectRef} import io.circe._ import io.circe.generic.extras.Configuration import io.circe.generic.extras.semiauto.deriveConfiguredDecoder @@ -196,23 +195,6 @@ class AclsRoutes(identities: Identities, acls: Acls, aclCheck: AclCheck)(implici object AclsRoutes { - final private case class IdentityPermissions(identity: Identity, permissions: Set[Permission]) - - final private[routes] case class AclValues(value: Seq[(Identity, Set[Permission])]) - - private[routes] object AclValues { - - implicit private val identityPermsDecoder: Decoder[IdentityPermissions] = { - implicit val config: Configuration = Configuration.default.withStrictDecoding - deriveConfiguredDecoder[IdentityPermissions] - } - - implicit val aclValuesDecoder: Decoder[AclValues] = - Decoder - .decodeSeq[IdentityPermissions] - .map(seq => AclValues(seq.map(value => value.identity -> value.permissions))) - } - final private[routes] case class ReplaceAcl(acl: AclValues) private[routes] object ReplaceAcl { diff --git a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/AclsRoutesPayloadSpec.scala b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/AclsRoutesPayloadSpec.scala index 43f9d338e5..8448660eb3 100644 --- a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/AclsRoutesPayloadSpec.scala +++ b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/AclsRoutesPayloadSpec.scala @@ -1,7 +1,8 @@ package ch.epfl.bluebrain.nexus.delta.routes import ch.epfl.bluebrain.nexus.delta.routes.AclsRoutes.PatchAcl.{Append, Subtract} -import ch.epfl.bluebrain.nexus.delta.routes.AclsRoutes.{AclValues, PatchAcl, ReplaceAcl} +import ch.epfl.bluebrain.nexus.delta.routes.AclsRoutes.{PatchAcl, ReplaceAcl} +import ch.epfl.bluebrain.nexus.delta.sdk.acls.model.AclValues import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.resources import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity import ch.epfl.bluebrain.nexus.testkit.scalatest.BaseSpec diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclAddress.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclAddress.scala index 4f9fa0b7dc..49f41afcee 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclAddress.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclAddress.scala @@ -5,7 +5,7 @@ import ch.epfl.bluebrain.nexus.delta.kernel.error.FormatError import ch.epfl.bluebrain.nexus.delta.sdk.error.FormatErrors.IllegalAclAddressFormatError import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Label, ProjectRef} import doobie.{Get, Put} -import io.circe.{Decoder, Encoder} +import io.circe.{Decoder, Encoder, KeyDecoder} /** * Enumeration of possible ACL addresses. An ACL address is the address where a certain ACL is anchored. @@ -100,6 +100,8 @@ object AclAddress { implicit val aclAddressOrdering: Ordering[AclAddress] = Ordering.by(_.string) + implicit val aclAddressKeyDecoder: KeyDecoder[AclAddress] = KeyDecoder.instance(AclAddress.fromString(_).toOption) + implicit val aclAddressEncoder: Encoder[AclAddress] = Encoder.encodeString.contramap(_.string) implicit val aclAddressDecoder: Decoder[AclAddress] = Decoder.decodeString.emap { str => AclAddress.fromString(str).leftMap(_.getMessage) diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclBatchReplace.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclBatchReplace.scala new file mode 100644 index 0000000000..513965434a --- /dev/null +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclBatchReplace.scala @@ -0,0 +1,15 @@ +package ch.epfl.bluebrain.nexus.delta.sdk.acls.model + +import io.circe.Decoder + +final case class AclBatchReplace(acls: Vector[Acl]) + +object AclBatchReplace { + + implicit val aclBatchReplaceDecoder: Decoder[AclBatchReplace] = Decoder[Map[AclAddress, AclValues]].map { valuesMap => + val acls = valuesMap.foldLeft(Vector.empty[Acl]) { case (acc, (address, values)) => + acc :+ Acl(address, values.value: _*) + } + AclBatchReplace(acls) + } +} diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclValues.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclValues.scala new file mode 100644 index 0000000000..c034673bea --- /dev/null +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclValues.scala @@ -0,0 +1,25 @@ +package ch.epfl.bluebrain.nexus.delta.sdk.acls.model + +import ch.epfl.bluebrain.nexus.delta.sdk.permissions.model.Permission +import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity +import io.circe.Decoder +import io.circe.generic.extras.Configuration +import io.circe.generic.extras.semiauto.deriveConfiguredDecoder + +final case class AclValues(value: Seq[(Identity, Set[Permission])]) + +object AclValues { + + final private case class IdentityPermissions(identity: Identity, permissions: Set[Permission]) + + implicit private val identityPermsDecoder: Decoder[IdentityPermissions] = { + implicit val config: Configuration = Configuration.default.withStrictDecoding + deriveConfiguredDecoder[IdentityPermissions] + } + + implicit val aclValuesDecoder: Decoder[AclValues] = + Decoder + .decodeSeq[IdentityPermissions] + .map(seq => AclValues(seq.map(value => value.identity -> value.permissions))) + +} diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclBatchReplaceSuite.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclBatchReplaceSuite.scala new file mode 100644 index 0000000000..98ce4da33b --- /dev/null +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/acls/model/AclBatchReplaceSuite.scala @@ -0,0 +1,82 @@ +package ch.epfl.bluebrain.nexus.delta.sdk.acls.model + +import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Group, User} +import ch.epfl.bluebrain.nexus.delta.sourcing.model.Label +import ch.epfl.bluebrain.nexus.testkit.mu.NexusSuite +import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.{acls, projects, resources} + +class AclBatchReplaceSuite extends NexusSuite { + + test("Deserialize properly the json payload") { + val payload = json""" + { + "/" : [ + { + "permissions": [ + "acls/read", + "acls/write" + ], + "identity": { + "realm": "realm", + "subject": "superuser" + } + }, + { + "permissions": [ + "projects/read", + "projects/write" + ], + "identity": { + "realm": "realm", + "group": "admins" + } + } + ], + "/bbp": [ + { + "permissions": [ + "resources/read", + "resources/write" + ], + "identity": { + "realm": "realm", + "group": "bbp-users" + } + } + ], + "/bbp/atlas": [ + { + "permissions": [ + "resources/read", + "resources/write" + ], + "identity": { + "realm": "realm", + "group": "atlas-users" + } + } + ] + }""" + + val realm = Label.unsafe("realm") + val bbp = Label.unsafe("bbp") + val atlas = Label.unsafe("atlas") + val expected = AclBatchReplace( + Vector( + Acl( + AclAddress.Root, + User("superuser", realm) -> Set(acls.read, acls.write), + Group("admins", realm) -> Set(projects.read, projects.write) + ), + Acl(AclAddress.Organization(bbp), Group("bbp-users", realm) -> Set(resources.read, resources.write)), + Acl(AclAddress.Project(bbp, atlas), Group("atlas-users", realm) -> Set(resources.read, resources.write)) + ) + ) + + assertEquals( + payload.as[AclBatchReplace], + Right(expected) + ) + } + +}