diff --git a/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala index 4a0b5bee89..7b3a2e5c92 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala @@ -2,12 +2,11 @@ package org.thp.thehive.controllers.v0 import org.apache.tinkerpop.gremlin.process.traversal.P import org.thp.scalligraph._ -import org.thp.scalligraph.traversal.Graph import org.thp.scalligraph.controllers.{Entrypoint, FPathElem, FPathEmpty, FieldsParser} import org.thp.scalligraph.models.{Database, Entity, UMapping} import org.thp.scalligraph.query._ import org.thp.scalligraph.traversal.TraversalOps._ -import org.thp.scalligraph.traversal.{IteratorOutput, Traversal} +import org.thp.scalligraph.traversal.{Graph, IteratorOutput, Traversal} import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.dto.v0.{InputCase, InputTask} import org.thp.thehive.dto.v1.InputCustomFieldValue @@ -19,7 +18,6 @@ import org.thp.thehive.services.CustomFieldOps._ import org.thp.thehive.services.ObservableOps._ import org.thp.thehive.services.OrganisationOps._ import org.thp.thehive.services.ShareOps._ -import org.thp.thehive.services.TagOps._ import org.thp.thehive.services.UserOps._ import org.thp.thehive.services._ import play.api.libs.json._ @@ -153,34 +151,10 @@ class CaseCtrl @Inject() ( for { caze <- caseSrv.get(EntityIdOrName(caseId)).visible(organisationSrv).getOrFail("Case") toMerge <- caseSrv.get(EntityIdOrName(caseToMerge)).visible(organisationSrv).getOrFail("Case") - _ <- sameOrga(Seq(caze, toMerge)) - _ <- sameProfile(Seq(caze, toMerge)) merged <- caseSrv.merge(Seq(caze, toMerge)) } yield Results.Created(merged.toJson) } - private def sameOrga(cases: Seq[Case with Entity])(implicit graph: Graph): Try[Seq[Case with Entity]] = - for { - orgas <- cases.toTry(c => caseSrv.get(c).organisations.getOrFail("Organisation")) - firstOrga <- orgas.headOption match { - case Some(o) => Success(o) - case None => Failure(BadRequestError("No organisations found")) - } - sameOrga = orgas.forall(_.name == firstOrga.name) - res <- if (sameOrga) Success(cases) else Failure(BadRequestError(s"Cases to merge have different organisations")) - } yield res - - private def sameProfile(cases: Seq[Case with Entity])(implicit graph: Graph): Try[Seq[Case with Entity]] = - for { - profiles <- cases.toTry(c => caseSrv.get(c).shares.profile.getOrFail("Profile")) - firstProfile <- profiles.headOption match { - case Some(o) => Success(o) - case None => Failure(BadRequestError("No profiles found")) - } - sameProfile = profiles.forall(_.name == firstProfile.name) - res <- if (sameProfile) Success(cases) else Failure(BadRequestError(s"Cases to merge have different profiles")) - } yield res - def linkedCases(caseIdOrNumber: String): Action[AnyContent] = entrypoint("case link") .authRoTransaction(db) { implicit request => implicit graph => diff --git a/thehive/app/org/thp/thehive/controllers/v1/CaseCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/CaseCtrl.scala index 6707054326..46a053ed8b 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/CaseCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/CaseCtrl.scala @@ -2,11 +2,11 @@ package org.thp.thehive.controllers.v1 import org.apache.tinkerpop.gremlin.process.traversal.P import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser} -import org.thp.scalligraph.models.{Database, Entity} +import org.thp.scalligraph.models.Database import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperties, Query} import org.thp.scalligraph.traversal.TraversalOps._ -import org.thp.scalligraph.traversal.{Graph, IteratorOutput, Traversal} -import org.thp.scalligraph.{BadRequestError, EntityIdOrName, RichOptionTry, RichSeq} +import org.thp.scalligraph.traversal.{IteratorOutput, Traversal} +import org.thp.scalligraph.{EntityIdOrName, RichOptionTry, RichSeq} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.dto.v1.{InputCase, InputTask} import org.thp.thehive.models._ @@ -22,7 +22,6 @@ import org.thp.thehive.services._ import play.api.mvc.{Action, AnyContent, Results} import javax.inject.{Inject, Singleton} -import scala.util.{Failure, Success, Try} @Singleton class CaseCtrl @Inject() ( @@ -156,31 +155,7 @@ class CaseCtrl @Inject() ( .visible(organisationSrv) .getOrFail("Case") ) - _ <- sameOrga(cases) - _ <- sameProfile(cases) mergedCase <- caseSrv.merge(cases) } yield Results.Created(mergedCase.toJson) } - - private def sameOrga(cases: Seq[Case with Entity])(implicit graph: Graph): Try[Seq[Case with Entity]] = - for { - orgas <- cases.toTry(c => caseSrv.get(c).organisations.getOrFail("Organisation")) - firstOrga <- orgas.headOption match { - case Some(o) => Success(o) - case None => Failure(BadRequestError("No organisations found")) - } - sameOrga = orgas.forall(_.name == firstOrga.name) - res <- if (sameOrga) Success(cases) else Failure(BadRequestError(s"Cases to merge have different organisations")) - } yield res - - private def sameProfile(cases: Seq[Case with Entity])(implicit graph: Graph): Try[Seq[Case with Entity]] = - for { - profiles <- cases.toTry(c => caseSrv.get(c).shares.profile.getOrFail("Profile")) - firstProfile <- profiles.headOption match { - case Some(o) => Success(o) - case None => Failure(BadRequestError("No profiles found")) - } - sameProfile = profiles.forall(_.name == firstProfile.name) - res <- if (sameProfile) Success(cases) else Failure(BadRequestError(s"Cases to merge have different profiles")) - } yield res } diff --git a/thehive/app/org/thp/thehive/services/CaseSrv.scala b/thehive/app/org/thp/thehive/services/CaseSrv.scala index 80b2dc70ff..160c060060 100644 --- a/thehive/app/org/thp/thehive/services/CaseSrv.scala +++ b/thehive/app/org/thp/thehive/services/CaseSrv.scala @@ -11,7 +11,7 @@ import org.thp.scalligraph.query.PropertyUpdater import org.thp.scalligraph.services._ import org.thp.scalligraph.traversal.TraversalOps._ import org.thp.scalligraph.traversal._ -import org.thp.scalligraph.{EntityId, EntityIdOrName, EntityName, RichOptionTry, RichSeq} +import org.thp.scalligraph.{BadRequestError, EntityId, EntityIdOrName, EntityName, RichOptionTry, RichSeq} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.dto.v1.InputCustomFieldValue import org.thp.thehive.models._ @@ -29,7 +29,7 @@ import play.api.libs.json.{JsNull, JsObject, JsValue, Json} import java.lang.{Long => JLong} import java.util.{Date, List => JList, Map => JMap} import javax.inject.{Inject, Named, Provider, Singleton} -import scala.util.{Success, Try} +import scala.util.{Failure, Success, Try} @Singleton class CaseSrv @Inject() ( @@ -320,58 +320,111 @@ class CaseSrv @Inject() ( auditSrv.`case`.update(`case`, Json.obj("owner" -> JsNull)) } - def merge(cases: Seq[Case with Entity])(implicit graph: Graph, authContext: AuthContext): Try[RichCase] = { - val mergedCase = Case( - cases.map(_.title).mkString(" / "), - cases.map(_.description).mkString("\n\n"), - cases.map(_.severity).max, - cases.map(_.startDate).min, - None, - cases.exists(_.flag), - cases.map(_.tlp).max, - cases.map(_.pap).max, - CaseStatus.Open, - cases.map(_.summary).fold(None)((s1, s2) => (s1 ++ s2).reduceOption(_ + "\n\n" + _)), - cases.flatMap(_.tags).distinct - ) - for { - user <- userSrv.get(EntityIdOrName(authContext.userId)).getOrFail("User") - orga <- organisationSrv.get(authContext.organisation).getOrFail("Organisation") - richCase <- create(mergedCase, Some(user), orga, Seq(), None, Seq()) - _ <- cases.toTry { c => - for { - _ <- - get(c) - .tasks - .richTask - .toList - .toTry(shareSrv.shareTask(_, richCase.`case`, orga._id)) - _ <- - get(c) - .observables - .richObservable - .toList - .toTry(shareSrv.shareObservable(_, richCase.`case`, orga._id)) - _ <- - get(c) - .alert - .toList - .toTry(alertSrv.alertCaseSrv.create(AlertCase(), _, richCase.`case`)) - _ <- - get(c) - .procedure - .toList - .toTry(caseProcedureSrv.create(CaseProcedure(), richCase.`case`, _)) - _ <- - get(c) - .richCustomFields - .toList - .toTry(c => createCustomField(richCase.`case`, EntityIdOrName(c.customField.name), c.value, c.order)) - } yield Success(()) - } - _ = cases.map(remove(_)) - } yield richCase + def merge(cases: Seq[Case with Entity])(implicit graph: Graph, authContext: AuthContext): Try[RichCase] = + if (cases.size > 1 && canMerge(cases)) { + val mergedCase = Case( + cases.map(_.title).mkString(" / "), + cases.map(_.description).mkString("\n\n"), + cases.map(_.severity).max, + cases.map(_.startDate).min, + None, + cases.exists(_.flag), + cases.map(_.tlp).max, + cases.map(_.pap).max, + CaseStatus.Open, + cases.map(_.summary).fold(None)((s1, s2) => (s1 ++ s2).reduceOption(_ + "\n\n" + _)), + cases.flatMap(_.tags).distinct + ) + + val allProfilesOrgas = get(cases.head) + .shares + .project(_.by(_.profile).by(_.organisation)) + .toSeq + + for { + user <- userSrv.get(EntityIdOrName(authContext.userId)).getOrFail("User") + orga <- organisationSrv.current.getOrFail("Organisation") + richCase <- create(mergedCase, Some(user), orga, Seq(), None, Seq()) + _ <- cases.toTry { c => + for { + // Share case with all organisations except the one who created the merged case + _ <- + allProfilesOrgas + .filter(_._2._id != organisationSrv.currentId) + .toTry(profileOrg => shareSrv.shareCase(owner = false, richCase.`case`, profileOrg._2, profileOrg._1)) + + _ <- shareMergedCaseTasks(allProfilesOrgas.map(_._2), c, richCase.`case`) + _ <- shareMergedCaseObservables(allProfilesOrgas.map(_._2), c, richCase.`case`) + _ <- + get(c) + .alert + .toList + .toTry(alertSrv.alertCaseSrv.create(AlertCase(), _, richCase.`case`)) + _ <- + get(c) + .procedure + .toList + .toTry(caseProcedureSrv.create(CaseProcedure(), richCase.`case`, _)) + _ <- + get(c) + .richCustomFields + .toList + .toTry(c => createCustomField(richCase.`case`, EntityIdOrName(c.customField.name), c.value, c.order)) + } yield Success(()) + } + _ = cases.map(remove(_)) + } yield richCase + } else + Failure(BadRequestError("To be able to merge, cases must have same organisation / profile pair and user must be org-admin")) + + private def canMerge(cases: Seq[Case with Entity])(implicit graph: Graph, authContext: AuthContext): Boolean = { + val allOrgProfiles = getByIds(cases.map(_._id): _*) + .shares + .project(_.by(_.profile.value(_.name)).by(_.organisation._id)) + .fold + .toSeq + .map(_.toSet) + .distinct + + // All cases must have the same organisation / profile pair && + // case organisation must match current organisation and be of org-admin profile + allOrgProfiles.size == 1 && allOrgProfiles + .head + .find(_._2 == organisationSrv.currentId) + .map(_._1) + .contains(Profile.orgAdmin.name) } + + private def shareMergedCaseTasks(orgs: Seq[Organisation with Entity], fromCase: Case with Entity, mergedCase: Case with Entity)(implicit + graph: Graph, + authContext: AuthContext + ): Try[Unit] = + for { + _ <- orgs.toTry(org => + get(fromCase) + .share(org._id) + .tasks + .richTask + .toList + .toTry(shareSrv.shareTask(_, mergedCase, org._id)) + ) + } yield Success() + + private def shareMergedCaseObservables(orgs: Seq[Organisation with Entity], fromCase: Case with Entity, mergedCase: Case with Entity)(implicit + graph: Graph, + authContext: AuthContext + ): Try[Unit] = + for { + _ <- orgs.toTry(org => + get(fromCase) + .share(org._id) + .observables + .richObservable + .toList + .toTry(shareSrv.shareObservable(_, mergedCase, org._id)) + ) + } yield Success() + } object CaseOps { diff --git a/thehive/test/org/thp/thehive/DatabaseBuilder.scala b/thehive/test/org/thp/thehive/DatabaseBuilder.scala index 05db588e9d..f784121cf2 100644 --- a/thehive/test/org/thp/thehive/DatabaseBuilder.scala +++ b/thehive/test/org/thp/thehive/DatabaseBuilder.scala @@ -125,7 +125,7 @@ class DatabaseBuilder @Inject() ( // createEdge(taskSrv.taskUserSrv, taskSrv, userSrv, FieldsParser[TaskUser], idMap) createEdge(taskSrv.taskLogSrv, taskSrv, logSrv, FieldsParser[TaskLog], idMap) - createEdge(caseSrv.caseUserSrv, caseSrv, userSrv, FieldsParser[CaseUser], idMap) +// createEdge(caseSrv.caseUserSrv, caseSrv, userSrv, FieldsParser[CaseUser], idMap) // createEdge(caseSrv.mergedFromSrv, caseSrv, caseSrv, FieldsParser[MergedFrom], idMap) // createEdge(caseSrv.caseCaseTemplateSrv, caseSrv, caseTemplateSrv, FieldsParser[CaseCaseTemplate], idMap) // createEdge(caseSrv.caseResolutionStatusSrv, caseSrv, resolutionStatusSrv, FieldsParser[CaseResolutionStatus], idMap) diff --git a/thehive/test/org/thp/thehive/controllers/v0/CaseCtrlTest.scala b/thehive/test/org/thp/thehive/controllers/v0/CaseCtrlTest.scala index 1b80d851a7..1d9f537af8 100644 --- a/thehive/test/org/thp/thehive/controllers/v0/CaseCtrlTest.scala +++ b/thehive/test/org/thp/thehive/controllers/v0/CaseCtrlTest.scala @@ -326,7 +326,7 @@ class CaseCtrlTest extends PlaySpecification with TestAppBuilder { status(result) must_=== 200 val resultCase = contentAsJson(result) - (resultCase \ "count").asOpt[Int] must beSome(3) + (resultCase \ "count").asOpt[Int] must beSome(7) (resultCase \ "t1" \ "count").asOpt[Int] must beSome(2) (resultCase \ "t2" \ "count").asOpt[Int] must beSome(1) (resultCase \ "t3" \ "count").asOpt[Int] must beSome(1) @@ -418,7 +418,6 @@ class CaseCtrlTest extends PlaySpecification with TestAppBuilder { val result = app[CaseCtrl].merge("21", "25")(request) status(result) must beEqualTo(400).updateMessage(s => s"$s\n${contentAsString(result)}") (contentAsJson(result) \ "type").as[String] must beEqualTo("BadRequest") - (contentAsJson(result) \ "message").as[String] must contain("different profiles") } } } diff --git a/thehive/test/org/thp/thehive/controllers/v1/CaseCtrlTest.scala b/thehive/test/org/thp/thehive/controllers/v1/CaseCtrlTest.scala index 5521900ab9..b3cb5570bf 100644 --- a/thehive/test/org/thp/thehive/controllers/v1/CaseCtrlTest.scala +++ b/thehive/test/org/thp/thehive/controllers/v1/CaseCtrlTest.scala @@ -245,7 +245,6 @@ class CaseCtrlTest extends PlaySpecification with TestAppBuilder { val result = app[CaseCtrl].merge("21,25")(request) status(result) must beEqualTo(400).updateMessage(s => s"$s\n${contentAsString(result)}") (contentAsJson(result) \ "type").as[String] must beEqualTo("BadRequest") - (contentAsJson(result) \ "message").as[String] must contain("different profiles") } } } diff --git a/thehive/test/org/thp/thehive/services/CaseSrvTest.scala b/thehive/test/org/thp/thehive/services/CaseSrvTest.scala index 99b4307106..ff5f21fd9b 100644 --- a/thehive/test/org/thp/thehive/services/CaseSrvTest.scala +++ b/thehive/test/org/thp/thehive/services/CaseSrvTest.scala @@ -178,7 +178,7 @@ class CaseSrvTest extends PlaySpecification with TestAppBuilder { "get correct next case number" in testApp { app => app[Database].roTransaction { implicit graph => - app[CaseSrv].nextCaseNumber shouldEqual 26 + app[CaseSrv].nextCaseNumber shouldEqual 27 } } @@ -456,7 +456,7 @@ class CaseSrvTest extends PlaySpecification with TestAppBuilder { pending } - "merge cases" in testApp { app => + "merge cases, happy path with one organisation" in testApp { app => app[Database].tryTransaction { implicit graph => def case21 = app[CaseSrv].get(EntityName("21")).clone() def case22 = app[CaseSrv].get(EntityName("22")).clone() @@ -483,9 +483,9 @@ class CaseSrvTest extends PlaySpecification with TestAppBuilder { case23.alert.toSeq.size mustEqual 0 for { - c21 <- case21.clone().getOrFail("Case") - c22 <- case22.clone().getOrFail("Case") - c23 <- case23.clone().getOrFail("Case") + c21 <- case21.getOrFail("Case") + c22 <- case22.getOrFail("Case") + c23 <- case23.getOrFail("Case") newCase <- app[CaseSrv].merge(Seq(c21, c22, c23)) } yield newCase } must beASuccessfulTry.which { richCase => @@ -504,5 +504,58 @@ class CaseSrvTest extends PlaySpecification with TestAppBuilder { } } + "merge cases, happy path with three organisations" in testApp { app => + app[Database].tryTransaction { implicit graph => + def case21 = app[CaseSrv].get(EntityName("21")).clone() + def case24 = app[CaseSrv].get(EntityName("24")).clone() + def case26 = app[CaseSrv].get(EntityName("26")).clone() + // Tasks + case21.tasks.toSeq.size mustEqual 2 + case24.tasks.toSeq.size mustEqual 0 + case26.tasks.toSeq.size mustEqual 0 + // Observables + case21.observables.toSeq.size mustEqual 1 + case24.observables.toSeq.size mustEqual 0 + case26.observables.toSeq.size mustEqual 0 + + for { + c21 <- case21.getOrFail("Case") + c24 <- case24.getOrFail("Case") + c26 <- case26.getOrFail("Case") + newCase <- app[CaseSrv].merge(Seq(c21, c24, c26)) + } yield newCase + } must beASuccessfulTry.which { richCase => + app[Database].roTransaction { implicit graph => + def mergedCase = app[CaseSrv].get(EntityName(richCase.number.toString)).clone() + mergedCase.tasks.toSeq.size mustEqual 2 + mergedCase.observables.toSeq.size mustEqual 1 + + app[CaseSrv].get(EntityName("21")).getOrFail("Case") must beAFailedTry + app[CaseSrv].get(EntityName("24")).getOrFail("Case") must beAFailedTry + app[CaseSrv].get(EntityName("26")).getOrFail("Case") must beAFailedTry + } + + app[Database].roTransaction { implicit graph => + implicit val authContext: AuthContext = + DummyUserSrv(userId = "socuser@thehive.local", organisation = "soc", permissions = Profile.analyst.permissions).authContext + + def mergedCase = app[CaseSrv].get(EntityName(richCase.number.toString)).clone() + mergedCase.getOrFail("Case") must beASuccessfulTry + mergedCase.tasks.toSeq.size mustEqual 1 + mergedCase.observables.toSeq.size mustEqual 1 + } + + app[Database].roTransaction { implicit graph => + implicit val authContext: AuthContext = + DummyUserSrv(userId = "puguser@thehive.local", organisation = "pug", permissions = Profile.analyst.permissions).authContext + + def mergedCase = app[CaseSrv].get(EntityName(richCase.number.toString)).clone() + mergedCase.getOrFail("Case") must beASuccessfulTry + mergedCase.tasks.toSeq.size mustEqual 0 + mergedCase.observables.toSeq.size mustEqual 0 + } + } + } + } } diff --git a/thehive/test/resources/data/Case.json b/thehive/test/resources/data/Case.json index 9dd3c43015..c421e40cd9 100644 --- a/thehive/test/resources/data/Case.json +++ b/thehive/test/resources/data/Case.json @@ -78,7 +78,8 @@ "tlp": 4, "pap": 3, "status": "Open", - "tags": ["toMerge:pred1=\"value1\""] + "tags": ["toMerge:pred1=\"value1\""], + "assignee": "certuser@thehive.local" }, { "id": "caseMerge22", @@ -91,7 +92,8 @@ "tlp": 1, "pap": 2, "status": "Open", - "tags": ["toMerge:pred2=\"value2\""] + "tags": ["toMerge:pred2=\"value2\""], + "assignee": "certuser@thehive.local" }, { "id": "caseMerge23", @@ -103,7 +105,8 @@ "flag": false, "tlp": 2, "pap": 2, - "status": "Open" + "status": "Open", + "assignee": "certuser@thehive.local" }, { "id": "caseMerge24", @@ -115,7 +118,8 @@ "flag": false, "tlp": 2, "pap": 2, - "status": "Open" + "status": "Open", + "assignee": "socuser@thehive.local" }, { "id": "caseMerge25", @@ -127,6 +131,20 @@ "flag": false, "tlp": 2, "pap": 2, - "status": "Open" + "status": "Open", + "assignee": "certuser@thehive.local" + }, + { + "id": "caseMerge26", + "number": 26, + "title": "case#26", + "description": "description of case #26", + "severity": 2, + "startDate": 1531667370000, + "flag": false, + "tlp": 1, + "pap": 2, + "status": "Open", + "assignee": "puguser@thehive.local" } ] diff --git a/thehive/test/resources/data/CaseUser.json b/thehive/test/resources/data/CaseUser.json deleted file mode 100644 index 23e7904c48..0000000000 --- a/thehive/test/resources/data/CaseUser.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { "from": "case1", "to": "certuser@thehive.local"}, - { "from": "case2", "to": "certuser@thehive.local"}, - { "from": "case3", "to": "socuser@thehive.local"}, - { "from": "caseMerge21", "to": "certuser@thehive.local"}, - { "from": "caseMerge22", "to": "certuser@thehive.local"}, - { "from": "caseMerge23", "to": "certuser@thehive.local"}, - { "from": "caseMerge24", "to": "socuser@thehive.local"}, - { "from": "caseMerge25", "to": "certuser@thehive.local"} -] \ No newline at end of file diff --git a/thehive/test/resources/data/Organisation.json b/thehive/test/resources/data/Organisation.json index a89248400c..ffdd9d0df7 100644 --- a/thehive/test/resources/data/Organisation.json +++ b/thehive/test/resources/data/Organisation.json @@ -8,5 +8,10 @@ "id": "soc", "name": "soc", "description": "soc" + }, + { + "id": "pug", + "name": "pug", + "description": "pug" } ] \ No newline at end of file diff --git a/thehive/test/resources/data/OrganisationShare.json b/thehive/test/resources/data/OrganisationShare.json index aa6e0e28b3..c662183547 100644 --- a/thehive/test/resources/data/OrganisationShare.json +++ b/thehive/test/resources/data/OrganisationShare.json @@ -10,5 +10,6 @@ {"from": "cert", "to": "case23-merge-cert"}, {"from": "cert", "to": "case24-merge-cert"}, {"from": "soc", "to": "case24-merge-soc"}, - {"from": "cert", "to": "case25-merge-cert"} + {"from": "cert", "to": "case25-merge-cert"}, + {"from": "pug", "to": "case26-merge-pug"} ] \ No newline at end of file diff --git a/thehive/test/resources/data/Role.json b/thehive/test/resources/data/Role.json index 4b4654e618..2ed22cde24 100644 --- a/thehive/test/resources/data/Role.json +++ b/thehive/test/resources/data/Role.json @@ -4,5 +4,8 @@ {"id": "admin-cert"}, {"id": "ro-soc"}, {"id": "user-soc"}, - {"id": "admin-soc"} + {"id": "admin-soc"}, + {"id": "ro-pug"}, + {"id": "user-pug"}, + {"id": "admin-pug"} ] \ No newline at end of file diff --git a/thehive/test/resources/data/RoleOrganisation.json b/thehive/test/resources/data/RoleOrganisation.json index 5a7df8b559..be2a75ba2b 100644 --- a/thehive/test/resources/data/RoleOrganisation.json +++ b/thehive/test/resources/data/RoleOrganisation.json @@ -4,5 +4,8 @@ {"from": "admin-cert", "to": "cert"}, {"from": "ro-soc", "to": "soc"}, {"from": "user-soc", "to": "soc"}, - {"from": "admin-soc", "to": "soc"} + {"from": "admin-soc", "to": "soc"}, + {"from": "ro-pug", "to": "pug"}, + {"from": "user-pug", "to": "pug"}, + {"from": "admin-pug", "to": "pug"} ] \ No newline at end of file diff --git a/thehive/test/resources/data/RoleProfile.json b/thehive/test/resources/data/RoleProfile.json index 6e3e5f6a2f..793bd9b497 100644 --- a/thehive/test/resources/data/RoleProfile.json +++ b/thehive/test/resources/data/RoleProfile.json @@ -4,5 +4,8 @@ {"from": "admin-cert", "to": "org-admin"}, {"from": "ro-soc", "to": "read-only"}, {"from": "user-soc", "to": "analyst"}, - {"from": "admin-soc", "to": "org-admin"} + {"from": "admin-soc", "to": "org-admin"}, + {"from": "ro-pug", "to": "read-only"}, + {"from": "user-pug", "to": "analyst"}, + {"from": "admin-pug", "to": "org-admin"} ] diff --git a/thehive/test/resources/data/Share.json b/thehive/test/resources/data/Share.json index 2e62d05427..6edfc53e81 100644 --- a/thehive/test/resources/data/Share.json +++ b/thehive/test/resources/data/Share.json @@ -10,5 +10,6 @@ {"id": "case23-merge-cert", "owner": true}, {"id": "case24-merge-soc", "owner": true}, {"id": "case24-merge-cert", "owner": true}, - {"id": "case25-merge-cert", "owner": true} + {"id": "case25-merge-cert", "owner": true}, + {"id": "case26-merge-pug", "owner": true} ] diff --git a/thehive/test/resources/data/ShareCase.json b/thehive/test/resources/data/ShareCase.json index 92bfdc1812..b0d0c970c3 100644 --- a/thehive/test/resources/data/ShareCase.json +++ b/thehive/test/resources/data/ShareCase.json @@ -9,5 +9,6 @@ {"from": "case22-merge-cert", "to": "caseMerge22"}, {"from": "case23-merge-cert", "to": "caseMerge23"}, {"from": "case24-merge-soc", "to": "caseMerge24"}, - {"from": "case25-merge-cert", "to": "caseMerge25"} + {"from": "case25-merge-cert", "to": "caseMerge25"}, + {"from": "case26-merge-pug", "to": "caseMerge26"} ] \ No newline at end of file diff --git a/thehive/test/resources/data/ShareObservable.json b/thehive/test/resources/data/ShareObservable.json index d9f433f4eb..a425d768c2 100644 --- a/thehive/test/resources/data/ShareObservable.json +++ b/thehive/test/resources/data/ShareObservable.json @@ -4,5 +4,6 @@ {"from": "case2-soc", "to": "helloworld"}, {"from": "case21-merge-cert", "to": "mergeObs211"}, {"from": "case23-merge-cert", "to": "mergeObs231"}, - {"from": "case23-merge-cert", "to": "mergeObs232"} + {"from": "case23-merge-cert", "to": "mergeObs232"}, + {"from": "case24-merge-soc", "to": "mergeObs211"} ] \ No newline at end of file diff --git a/thehive/test/resources/data/ShareProfile.json b/thehive/test/resources/data/ShareProfile.json index 6359fa2471..a58379ea2c 100644 --- a/thehive/test/resources/data/ShareProfile.json +++ b/thehive/test/resources/data/ShareProfile.json @@ -7,5 +7,7 @@ {"from": "case22-merge-cert", "to": "org-admin"}, {"from": "case23-merge-cert", "to": "org-admin"}, {"from": "case24-merge-soc", "to": "org-admin"}, - {"from": "case25-merge-cert", "to": "read-only"} + {"from": "case24-merge-cert", "to": "read-only"}, + {"from": "case25-merge-cert", "to": "read-only"}, + {"from": "case26-merge-pug", "to": "read-only"} ] \ No newline at end of file diff --git a/thehive/test/resources/data/ShareTask.json b/thehive/test/resources/data/ShareTask.json index 21a42bcdd5..8f4a804323 100644 --- a/thehive/test/resources/data/ShareTask.json +++ b/thehive/test/resources/data/ShareTask.json @@ -10,5 +10,6 @@ {"from": "case-actionRequired-cert", "to": "taskActionRequired2", "actionRequired": true}, {"from": "case21-merge-cert", "to": "taskMerge211", "actionRequired": false}, {"from": "case21-merge-cert", "to": "taskMerge212", "actionRequired": false}, - {"from": "case23-merge-cert", "to": "taskMerge231", "actionRequired": false} + {"from": "case23-merge-cert", "to": "taskMerge231", "actionRequired": false}, + {"from": "case24-merge-soc", "to": "taskMerge211", "actionRequired": false} ] \ No newline at end of file diff --git a/thehive/test/resources/data/User.json b/thehive/test/resources/data/User.json index 04b8bbf603..2bc2293038 100644 --- a/thehive/test/resources/data/User.json +++ b/thehive/test/resources/data/User.json @@ -12,6 +12,12 @@ "name": "socuser", "locked": false }, + { + "id": "puguser", + "login": "puguser@thehive.local", + "name": "puguser", + "locked": false + }, { "id": "certadmin", "login": "certadmin@thehive.local", @@ -24,6 +30,12 @@ "name": "socadmin", "locked": false }, + { + "id": "pugadmin", + "login": "pugadmin@thehive.local", + "name": "pugadmin", + "locked": false + }, { "id": "certro", "login": "certro@thehive.local", @@ -37,5 +49,12 @@ "name": "socro", "locked": false, "password": "*random*seed*,d57cb64654764ab43edfc76a0a84e4e2fda867e761ab5a82859b00ad8412b2a1" + }, + { + "id": "pugro", + "login": "pugro@thehive.local", + "name": "pugro", + "locked": false, + "password": "*random*seed*,d57cb64654764ab43edfc76a0a84e4e2fda867e761ab5a82859b00ad8412b2a1" } ] \ No newline at end of file diff --git a/thehive/test/resources/data/UserRole.json b/thehive/test/resources/data/UserRole.json index 8c0e886f19..7434c5b0b8 100644 --- a/thehive/test/resources/data/UserRole.json +++ b/thehive/test/resources/data/UserRole.json @@ -4,5 +4,8 @@ {"from": "certadmin@thehive.local","to": "admin-cert"}, {"from": "socro@thehive.local","to": "ro-soc"}, {"from": "socuser@thehive.local","to": "user-soc"}, - {"from": "socadmin@thehive.local","to": "admin-soc"} + {"from": "socadmin@thehive.local","to": "admin-soc"}, + {"from": "pugro@thehive.local","to": "ro-pug"}, + {"from": "puguser@thehive.local","to": "user-pug"}, + {"from": "pugadmin@thehive.local","to": "admin-pug"} ]