diff --git a/ScalliGraph b/ScalliGraph index 6a337eb9a7..49b3994be6 160000 --- a/ScalliGraph +++ b/ScalliGraph @@ -1 +1 @@ -Subproject commit 6a337eb9a76b3e572a6ae657ff83320e067bd149 +Subproject commit 49b3994be62dc892ea0ff8cf46adbcc54411a5a7 diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionOperationSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionOperationSrv.scala index 11eb3ca8da..8ff31506c7 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionOperationSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionOperationSrv.scala @@ -72,7 +72,13 @@ class ActionOperationSrv @Inject() ( case CloseTask() => for { t <- relatedTask.fold[Try[Task with Entity]](Failure(InternalError("Unable to apply action CloseTask without task")))(Success(_)) - _ <- taskSrv.get(t).update(_.status, TaskStatus.Completed).getOrFail("Task") + _ <- + taskSrv + .get(t) + .update(_.status, TaskStatus.Completed) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Task") } yield updateOperation(operation) case MarkAlertAsRead() => diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala index 1ee8097ba2..320fc9e1a3 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala @@ -167,6 +167,8 @@ class ActionSrv @Inject() ( .update(_.report, cortexJob.report.map(r => Json.toJsObject(r.copy(operations = Nil)))) .update(_.endDate, Some(new Date())) .update(_.operations, operations.map(o => Json.toJsObject(o))) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .getOrFail("Action") .map { updated => auditSrv diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/AnalyzerTemplateSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/AnalyzerTemplateSrv.scala index fa4d3f1ae6..a1b2cd1581 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/AnalyzerTemplateSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/AnalyzerTemplateSrv.scala @@ -14,6 +14,7 @@ import org.thp.thehive.controllers.v0.Conversion._ import org.thp.thehive.services.OrganisationSrv import play.api.libs.json.{JsObject, Json} +import java.util.Date import java.util.zip.{ZipEntry, ZipFile} import javax.inject.{Inject, Singleton} import scala.collection.JavaConverters._ @@ -89,8 +90,12 @@ class AnalyzerTemplateSrv @Inject() ( .flatMap { content => db.tryTransaction { implicit graph => (for { - updated <- get(EntityName(analyzerId)).update(_.content, content).getOrFail("AnalyzerTemplate") - _ <- auditSrv.analyzerTemplate.update(updated, Json.obj("content" -> content)) + updated <- get(EntityName(analyzerId)) + .update(_.content, content) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("AnalyzerTemplate") + _ <- auditSrv.analyzerTemplate.update(updated, Json.obj("content" -> content)) } yield updated).recoverWith { case _ => for { diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala index 8c93d7ba48..519644bce9 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala @@ -191,6 +191,8 @@ class JobSrv @Inject() ( .update(_.report, report) .update(_.status, status) .update(_.endDate, endDate) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .getOrFail("Job") observable <- get(job).observable.getOrFail("Observable") _ <- diff --git a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala index e63afa5c8e..98348adffe 100644 --- a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala +++ b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala @@ -228,6 +228,13 @@ class MispImportSrv @Inject() ( .when(richObservable.ioc != observable.ioc)(_.update(_.ioc, observable.ioc)) .when(richObservable.sighted != observable.sighted)(_.update(_.sighted, observable.sighted)) .when(richObservable.tags.toSet != observable.tags.toSet)(_.update(_.tags, observable.tags)) + .when( + richObservable.message != observable.message || + richObservable.tlp != observable.tlp || + richObservable.ioc != observable.ioc || + richObservable.sighted != observable.sighted || + richObservable.tags.toSet != observable.tags.toSet + )(_.update(_._updatedAt, Some(new Date)).update(_._updatedBy, Some(authContext.userId))) .getOrFail("Observable") } yield () } @@ -390,7 +397,10 @@ class MispImportSrv @Inject() ( .map(ra => (ra.alert, None, ra.toJson.asInstanceOf[JsObject])) case Some(richAlert) => logger.debug(s"Event ${client.name}#${event.id} have already been imported for organisation ${organisation.name}, updating the alert") - val (updatedAlertTraversal, updatedFields) = (alertSrv.get(richAlert.alert).update(_.read, false), Json.obj("read" -> false)) + val (updatedAlertTraversal, updatedFields) = ( + alertSrv.get(richAlert.alert).update(_.read, false).update(_._updatedAt, Some(new Date)).update(_._updatedBy, Some(authContext.userId)), + Json.obj("read" -> false) + ) .when(richAlert.title != alert.title)(_.update(_.title, alert.title), _ + ("title" -> JsString(alert.title))) .when(richAlert.lastSyncDate != alert.lastSyncDate)( _.update(_.lastSyncDate, alert.lastSyncDate), diff --git a/thehive/app/org/thp/thehive/services/AlertSrv.scala b/thehive/app/org/thp/thehive/services/AlertSrv.scala index 0e10da152b..fb9f208389 100644 --- a/thehive/app/org/thp/thehive/services/AlertSrv.scala +++ b/thehive/app/org/thp/thehive/services/AlertSrv.scala @@ -111,7 +111,11 @@ class AlertSrv @Inject() ( tagsToRemove = get(alert).tags.toSeq.filterNot(t => tags.contains(t.toString)) _ <- tagsToAdd.toTry(alertTagSrv.create(AlertTag(), alert, _)) _ = if (tags.nonEmpty) get(alert).outE[AlertTag].filter(_.otherV.hasId(tagsToRemove.map(_._id): _*)).remove() - _ <- get(alert).update(_.tags, tags.toSeq).getOrFail("Alert") + _ <- get(alert) + .update(_.tags, tags.toSeq) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Alert") _ <- auditSrv.alert.update(alert, Json.obj("tags" -> tags)) } yield (tagsToAdd, tagsToRemove) @@ -186,26 +190,42 @@ class AlertSrv @Inject() ( def markAsUnread(alertId: EntityIdOrName)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = for { - alert <- get(alertId).update[Boolean](_.read, false).getOrFail("Alert") - _ <- auditSrv.alert.update(alert, Json.obj("read" -> false)) + alert <- get(alertId) + .update[Boolean](_.read, false) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Alert") + _ <- auditSrv.alert.update(alert, Json.obj("read" -> false)) } yield () def markAsRead(alertId: EntityIdOrName)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = for { - alert <- get(alertId).update[Boolean](_.read, true).getOrFail("Alert") - _ <- auditSrv.alert.update(alert, Json.obj("read" -> true)) + alert <- get(alertId) + .update[Boolean](_.read, true) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Alert") + _ <- auditSrv.alert.update(alert, Json.obj("read" -> true)) } yield () def followAlert(alertId: EntityIdOrName)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = for { - alert <- get(alertId).update[Boolean](_.follow, true).getOrFail("Alert") - _ <- auditSrv.alert.update(alert, Json.obj("follow" -> true)) + alert <- get(alertId) + .update[Boolean](_.follow, true) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Alert") + _ <- auditSrv.alert.update(alert, Json.obj("follow" -> true)) } yield () def unfollowAlert(alertId: EntityIdOrName)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = for { - alert <- get(alertId).update[Boolean](_.follow, false).getOrFail("Alert") - _ <- auditSrv.alert.update(alert, Json.obj("follow" -> false)) + alert <- get(alertId) + .update[Boolean](_.follow, false) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Alert") + _ <- auditSrv.alert.update(alert, Json.obj("follow" -> false)) } yield () def createCase(alert: RichAlert, assignee: Option[User with Entity], organisation: Organisation with Entity)(implicit @@ -268,7 +288,13 @@ class AlertSrv @Inject() ( _ <- caseSrv.addTags(`case`, alert.tags.toSet) _ <- alertCaseSrv.create(AlertCase(), alert, `case`) _ <- get(alert).update(_.caseId, `case`._id).getOrFail("Alert") - c <- caseSrv.get(`case`).update(_.description, description).getOrFail("Case") + c <- + caseSrv + .get(`case`) + .update(_.description, description) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Case") details <- Success( Json.obj( "customFields" -> get(alert).richCustomFields.toSeq.map(_.toOutput.toJson), diff --git a/thehive/app/org/thp/thehive/services/CaseSrv.scala b/thehive/app/org/thp/thehive/services/CaseSrv.scala index 27304f1520..665870b313 100644 --- a/thehive/app/org/thp/thehive/services/CaseSrv.scala +++ b/thehive/app/org/thp/thehive/services/CaseSrv.scala @@ -186,7 +186,11 @@ class CaseSrv @Inject() ( tagsToRemove = get(`case`).tags.toSeq.filterNot(t => tags.contains(t.toString)) _ <- tagsToAdd.toTry(caseTagSrv.create(CaseTag(), `case`, _)) _ = if (tagsToRemove.nonEmpty) get(`case`).outE[CaseTag].filter(_.otherV.hasId(tagsToRemove.map(_._id): _*)).remove() - _ <- get(`case`).update(_.tags, tags.toSeq).getOrFail("Case") + _ <- get(`case`) + .update(_.tags, tags.toSeq) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Case") _ <- auditSrv.`case`.update(`case`, Json.obj("tags" -> tags)) } yield (tagsToAdd, tagsToRemove) @@ -310,13 +314,23 @@ class CaseSrv @Inject() ( `case`: Case with Entity, impactStatus: ImpactStatus with Entity )(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { - get(`case`).update(_.impactStatus, Some(impactStatus.value)).outE[CaseImpactStatus].remove() + get(`case`) + .update(_.impactStatus, Some(impactStatus.value)) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .outE[CaseImpactStatus] + .remove() caseImpactStatusSrv.create(CaseImpactStatus(), `case`, impactStatus) auditSrv.`case`.update(`case`, Json.obj("impactStatus" -> impactStatus.value)) } def unsetImpactStatus(`case`: Case with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { - get(`case`).update(_.impactStatus, None).outE[CaseImpactStatus].remove() + get(`case`) + .update(_.impactStatus, None) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .outE[CaseImpactStatus] + .remove() auditSrv.`case`.update(`case`, Json.obj("impactStatus" -> JsNull)) } @@ -330,24 +344,44 @@ class CaseSrv @Inject() ( `case`: Case with Entity, resolutionStatus: ResolutionStatus with Entity )(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { - get(`case`).update(_.resolutionStatus, Some(resolutionStatus.value)).outE[CaseResolutionStatus].remove() + get(`case`) + .update(_.resolutionStatus, Some(resolutionStatus.value)) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .outE[CaseResolutionStatus] + .remove() caseResolutionStatusSrv.create(CaseResolutionStatus(), `case`, resolutionStatus) auditSrv.`case`.update(`case`, Json.obj("resolutionStatus" -> resolutionStatus.value)) } def unsetResolutionStatus(`case`: Case with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { - get(`case`).update(_.resolutionStatus, None).outE[CaseResolutionStatus].remove() + get(`case`) + .update(_.resolutionStatus, None) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .outE[CaseResolutionStatus] + .remove() auditSrv.`case`.update(`case`, Json.obj("resolutionStatus" -> JsNull)) } def assign(`case`: Case with Entity, user: User with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { - get(`case`).update(_.assignee, Some(user.login)).outE[CaseUser].remove() + get(`case`) + .update(_.assignee, Some(user.login)) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .outE[CaseUser] + .remove() caseUserSrv.create(CaseUser(), `case`, user) auditSrv.`case`.update(`case`, Json.obj("owner" -> user.login)) } def unassign(`case`: Case with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { - get(`case`).update(_.assignee, None).outE[CaseUser].remove() + get(`case`) + .update(_.assignee, None) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .outE[CaseUser] + .remove() auditSrv.`case`.update(`case`, Json.obj("owner" -> JsNull)) } diff --git a/thehive/app/org/thp/thehive/services/CaseTemplateSrv.scala b/thehive/app/org/thp/thehive/services/CaseTemplateSrv.scala index 8e2c071dd1..613557f3d5 100644 --- a/thehive/app/org/thp/thehive/services/CaseTemplateSrv.scala +++ b/thehive/app/org/thp/thehive/services/CaseTemplateSrv.scala @@ -97,7 +97,11 @@ class CaseTemplateSrv @Inject() ( tagsToRemove = get(caseTemplate).tags.toSeq.filterNot(t => tags.contains(t.toString)) _ <- tagsToAdd.toTry(caseTemplateTagSrv.create(CaseTemplateTag(), caseTemplate, _)) _ = if (tags.nonEmpty) get(caseTemplate).outE[CaseTemplateTag].filter(_.otherV.hasId(tagsToRemove.map(_._id): _*)).remove() - _ <- get(caseTemplate).update(_.tags, tags.toSeq).getOrFail("CaseTemplate") + _ <- get(caseTemplate) + .update(_.tags, tags.toSeq) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("CaseTemplate") _ <- auditSrv.caseTemplate.update(caseTemplate, Json.obj("tags" -> tags)) } yield (tagsToAdd, tagsToRemove) diff --git a/thehive/app/org/thp/thehive/services/ConfigSrv.scala b/thehive/app/org/thp/thehive/services/ConfigSrv.scala index cbbbc0459b..287c555dda 100644 --- a/thehive/app/org/thp/thehive/services/ConfigSrv.scala +++ b/thehive/app/org/thp/thehive/services/ConfigSrv.scala @@ -14,6 +14,7 @@ import org.thp.thehive.services.notification.NotificationSrv import org.thp.thehive.services.notification.triggers.Trigger import play.api.libs.json.{JsValue, Reads} +import java.util.Date import javax.inject.{Inject, Singleton} import scala.util.Try @@ -33,7 +34,13 @@ class ConfigSrv @Inject() ( def setConfigValue(organisationName: EntityIdOrName, name: String, value: JsValue)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = getConfigValue(organisationName, name) match { - case Some(config) => get(config).update(_.value, value).domainMap(_ => ()).getOrFail("Config") + case Some(config) => + get(config) + .update(_.value, value) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .domainMap(_ => ()) + .getOrFail("Config") case None => for { createdConfig <- createEntity(Config(name, value)) @@ -54,7 +61,13 @@ class ConfigSrv @Inject() ( def setConfigValue(userName: EntityIdOrName, name: String, value: JsValue)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = getConfigValue(userName, name) match { - case Some(config) => get(config).update(_.value, value).domainMap(_ => ()).getOrFail("Config") + case Some(config) => + get(config) + .update(_.value, value) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .domainMap(_ => ()) + .getOrFail("Config") case None => for { createdConfig <- createEntity(Config(name, value)) diff --git a/thehive/app/org/thp/thehive/services/DashboardSrv.scala b/thehive/app/org/thp/thehive/services/DashboardSrv.scala index 9da90a3c19..1878759853 100644 --- a/thehive/app/org/thp/thehive/services/DashboardSrv.scala +++ b/thehive/app/org/thp/thehive/services/DashboardSrv.scala @@ -13,7 +13,7 @@ import org.thp.thehive.services.OrganisationOps._ import org.thp.thehive.services.UserOps._ import play.api.libs.json.{JsObject, Json} -import java.util.{List => JList, Map => JMap} +import java.util.{Date, List => JList, Map => JMap} import javax.inject.{Inject, Singleton} import scala.util.{Success, Try} @@ -52,6 +52,8 @@ class DashboardSrv @Inject() (organisationSrv: OrganisationSrv, userSrv: UserSrv .inE[OrganisationDashboard] .filter(_.outV.v[Organisation].getEntity(org)) .update(_.writable, writable) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .fold .getOrFail("Dashboard") .flatMap { diff --git a/thehive/app/org/thp/thehive/services/LocalKeyAuthSrv.scala b/thehive/app/org/thp/thehive/services/LocalKeyAuthSrv.scala index 9887005101..17346d00bf 100644 --- a/thehive/app/org/thp/thehive/services/LocalKeyAuthSrv.scala +++ b/thehive/app/org/thp/thehive/services/LocalKeyAuthSrv.scala @@ -8,7 +8,7 @@ import org.thp.thehive.services.UserOps._ import play.api.Configuration import play.api.mvc.RequestHeader -import java.util.Base64 +import java.util.{Base64, Date} import javax.inject.{Inject, Provider, Singleton} import scala.concurrent.ExecutionContext import scala.util.{Failure, Random, Success, Try} @@ -45,6 +45,8 @@ class LocalKeyAuthSrv( userSrv .get(EntityIdOrName(username)) .update(_.apikey, Some(newKey)) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .domainMap(_ => newKey) .getOrFail("User") } @@ -61,6 +63,8 @@ class LocalKeyAuthSrv( userSrv .get(EntityIdOrName(username)) .update(_.apikey, None) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .domainMap(_ => ()) .getOrFail("User") } diff --git a/thehive/app/org/thp/thehive/services/LocalPasswordAuthSrv.scala b/thehive/app/org/thp/thehive/services/LocalPasswordAuthSrv.scala index 2c24ce5a86..06e943f0dd 100644 --- a/thehive/app/org/thp/thehive/services/LocalPasswordAuthSrv.scala +++ b/thehive/app/org/thp/thehive/services/LocalPasswordAuthSrv.scala @@ -10,6 +10,7 @@ import org.thp.thehive.models.User import play.api.mvc.RequestHeader import play.api.{Configuration, Logger} +import java.util.Date import javax.inject.{Inject, Singleton} import scala.util.{Failure, Success, Try} @@ -62,6 +63,8 @@ class LocalPasswordAuthSrv(db: Database, userSrv: UserSrv, localUserSrv: LocalUs userSrv .get(EntityIdOrName(username)) .update(_.password, Some(hashPassword(newPassword))) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .getOrFail("User") .map(_ => ()) } diff --git a/thehive/app/org/thp/thehive/services/ObservableSrv.scala b/thehive/app/org/thp/thehive/services/ObservableSrv.scala index 4b8d857381..985b7a2302 100644 --- a/thehive/app/org/thp/thehive/services/ObservableSrv.scala +++ b/thehive/app/org/thp/thehive/services/ObservableSrv.scala @@ -19,7 +19,7 @@ import org.thp.thehive.services.OrganisationOps._ import org.thp.thehive.services.ShareOps._ import play.api.libs.json.{JsObject, JsString, Json} -import java.util.{Map => JMap} +import java.util.{Date, Map => JMap} import javax.inject.{Inject, Provider, Singleton} import scala.util.{Failure, Success, Try} @@ -118,7 +118,11 @@ class ObservableSrv @Inject() ( for { createdTags <- newTags.filterNot(_.isEmpty).toTry(tagSrv.getOrCreate) _ <- createdTags.toTry(observableTagSrv.create(ObservableTag(), observable, _)) - _ <- get(observable).update(_.tags, (currentTags ++ newTags).toSeq).getOrFail("Observable") + _ <- get(observable) + .update(_.tags, (currentTags ++ newTags).toSeq) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Observable") // _ <- auditSrv.observable.update(observable, Json.obj("tags" -> (currentTags ++ tags))) } yield createdTags } @@ -132,7 +136,11 @@ class ObservableSrv @Inject() ( tagsToRemove = get(observable).tags.toSeq.filterNot(t => tags.contains(t.toString)) _ <- tagsToAdd.toTry(observableTagSrv.create(ObservableTag(), observable, _)) _ = if (tags.nonEmpty) get(observable).outE[ObservableTag].filter(_.otherV.hasId(tagsToRemove.map(_._id): _*)).remove() - _ <- get(observable).update(_.tags, tags.toSeq).getOrFail("Observable") + _ <- get(observable) + .update(_.tags, tags.toSeq) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("Observable") _ <- auditSrv.observable.update(observable, Json.obj("tags" -> tags)) } yield (tagsToAdd, tagsToRemove) @@ -199,6 +207,8 @@ class ObservableSrv @Inject() ( ): Try[Unit] = { get(observable) .update(_.dataType, observableType.name) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .outE[ObservableObservableType] .remove() observableObservableTypeSrv diff --git a/thehive/app/org/thp/thehive/services/PatternSrv.scala b/thehive/app/org/thp/thehive/services/PatternSrv.scala index 8ba0e6bb16..80d30786ce 100644 --- a/thehive/app/org/thp/thehive/services/PatternSrv.scala +++ b/thehive/app/org/thp/thehive/services/PatternSrv.scala @@ -12,7 +12,7 @@ import org.thp.thehive.services.CaseOps._ import org.thp.thehive.services.PatternOps._ import org.thp.thehive.services.ProcedureOps._ -import java.util.{Map => JMap} +import java.util.{Date, Map => JMap} import javax.inject.{Inject, Singleton} import scala.util.{Success, Try} @@ -42,7 +42,7 @@ class PatternSrv @Inject() ( def update( pattern: Pattern with Entity, input: Pattern - )(implicit graph: Graph): Try[Pattern with Entity] = + )(implicit graph: Graph, authContext: AuthContext): Try[Pattern with Entity] = for { updatedPattern <- get(pattern) .when(pattern.patternId != input.patternId)(_.update(_.patternId, input.patternId)) @@ -62,6 +62,7 @@ class PatternSrv @Inject() ( .when(pattern.remoteSupport != input.remoteSupport)(_.update(_.remoteSupport, input.remoteSupport)) .when(pattern.systemRequirements != input.systemRequirements)(_.update(_.systemRequirements, input.systemRequirements)) .when(pattern.revision != input.revision)(_.update(_.revision, input.revision)) + .when(input != pattern)(_.update(_._updatedAt, Some(new Date)).update(_._updatedBy, Some(authContext.userId))) .getOrFail("Pattern") } yield updatedPattern diff --git a/thehive/app/org/thp/thehive/services/ShareSrv.scala b/thehive/app/org/thp/thehive/services/ShareSrv.scala index 557c15f873..ad76d8fbf1 100644 --- a/thehive/app/org/thp/thehive/services/ShareSrv.scala +++ b/thehive/app/org/thp/thehive/services/ShareSrv.scala @@ -16,7 +16,7 @@ import org.thp.thehive.services.OrganisationOps._ import org.thp.thehive.services.ShareOps._ import org.thp.thehive.services.TaskOps._ -import java.util.{Map => JMap} +import java.util.{Date, Map => JMap} import javax.inject.{Inject, Provider, Singleton} import scala.util.{Failure, Try} @@ -226,6 +226,8 @@ class ShareSrv @Inject() (implicit val (orgsToAdd, orgsToRemove) = taskSrv .get(task) .update(_.organisationIds, organisations.map(_._id)) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .shares .organisation .toIterator @@ -306,6 +308,8 @@ class ShareSrv @Inject() (implicit val (orgsToAdd, orgsToRemove) = observableSrv .get(observable) .update(_.organisationIds, organisations.map(_._id)) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .shares .organisation .toIterator diff --git a/thehive/app/org/thp/thehive/services/TOTPAuthSrv.scala b/thehive/app/org/thp/thehive/services/TOTPAuthSrv.scala index 01f3176010..61d728006b 100644 --- a/thehive/app/org/thp/thehive/services/TOTPAuthSrv.scala +++ b/thehive/app/org/thp/thehive/services/TOTPAuthSrv.scala @@ -11,6 +11,7 @@ import play.api.Configuration import play.api.mvc.RequestHeader import java.net.URI +import java.util.Date import java.util.concurrent.TimeUnit import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec @@ -74,7 +75,13 @@ class TOTPAuthSrv( userSrv.get(EntityIdOrName(username)).headOption.flatMap(_.totpSecret) def unsetSecret(username: String)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = - userSrv.get(EntityIdOrName(username)).update(_.totpSecret, None).domainMap(_ => ()).getOrFail("User") + userSrv + .get(EntityIdOrName(username)) + .update(_.totpSecret, None) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .domainMap(_ => ()) + .getOrFail("User") def generateSecret(): String = { val key = Array.ofDim[Byte](20) @@ -89,6 +96,8 @@ class TOTPAuthSrv( userSrv .get(EntityIdOrName(username)) .update(_.totpSecret, Some(secret)) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .domainMap(_ => secret) .getOrFail("User") diff --git a/thehive/app/org/thp/thehive/services/TagSrv.scala b/thehive/app/org/thp/thehive/services/TagSrv.scala index 2af4390578..32cd727355 100644 --- a/thehive/app/org/thp/thehive/services/TagSrv.scala +++ b/thehive/app/org/thp/thehive/services/TagSrv.scala @@ -15,7 +15,7 @@ import org.thp.thehive.models._ import org.thp.thehive.services.OrganisationOps._ import org.thp.thehive.services.TagOps._ -import java.util.{Map => JMap} +import java.util.{Date, Map => JMap} import javax.inject.{Inject, Named, Provider, Singleton} import scala.util.matching.Regex import scala.util.{Success, Try} @@ -85,11 +85,12 @@ class TagSrv @Inject() ( def update( tag: Tag with Entity, input: Tag - )(implicit graph: Graph): Try[Tag with Entity] = + )(implicit graph: Graph, authContext: AuthContext): Try[Tag with Entity] = for { updatedTag <- get(tag) .when(tag.description != input.description)(_.update(_.description, input.description)) .when(tag.colour != input.colour)(_.update(_.colour, input.colour)) + .when(input != tag)(_.update(_._updatedAt, Some(new Date)).update(_._updatedBy, Some(authContext.userId))) .getOrFail("Tag") } yield updatedTag diff --git a/thehive/app/org/thp/thehive/services/TaskSrv.scala b/thehive/app/org/thp/thehive/services/TaskSrv.scala index cb8fe96ec9..9214a0d548 100644 --- a/thehive/app/org/thp/thehive/services/TaskSrv.scala +++ b/thehive/app/org/thp/thehive/services/TaskSrv.scala @@ -47,7 +47,12 @@ class TaskSrv @Inject() ( } yield RichTask(createdTask) def unassign(task: Task with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { - get(task).update(_.assignee, None).outE[TaskUser].remove() + get(task) + .update(_.assignee, None) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .outE[TaskUser] + .remove() auditSrv.task.update(task, Json.obj("assignee" -> JsNull)) } @@ -96,7 +101,11 @@ class TaskSrv @Inject() ( graph: Graph, authContext: AuthContext ): Try[Task with Entity] = { - def setStatus(): Traversal.V[Task] = get(task).update(_.status, status) + def setStatus(): Traversal.V[Task] = + get(task) + .update(_.status, status) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) status match { case TaskStatus.Cancel | TaskStatus.Waiting => setStatus().getOrFail("Task") @@ -117,7 +126,12 @@ class TaskSrv @Inject() ( } def assign(task: Task with Entity, user: User with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { - get(task).update(_.assignee, Some(user.login)).outE[TaskUser].remove() + get(task) + .update(_.assignee, Some(user.login)) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .outE[TaskUser] + .remove() for { _ <- taskUserSrv.create(TaskUser(), task, user) _ <- auditSrv.task.update(task, Json.obj("assignee" -> user.login)) @@ -137,6 +151,8 @@ class TaskSrv @Inject() ( .outE[ShareTask] .filter(_.inV.v[Task].hasId(task._id)) .update(_.actionRequired, actionRequired) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) .iterate() } } @@ -257,7 +273,7 @@ class TaskIntegrityCheckOps @Inject() (val db: Database, val service: TaskSrv, o val orgStats = multiIdLink[Organisation]("organisationIds", organisationSrv)(_.remove) .check(task, task.organisationIds, organisationIds) - val removeOrphan: OrphanStrategy[Task, EntityId] = { (a, entity) => + val removeOrphan: OrphanStrategy[Task, EntityId] = { (_, entity) => service.get(entity).remove() Map("Task-relatedId-removeOrphan" -> 1) } diff --git a/thehive/app/org/thp/thehive/services/TaxonomySrv.scala b/thehive/app/org/thp/thehive/services/TaxonomySrv.scala index dd7929c1aa..ab0bac70ee 100644 --- a/thehive/app/org/thp/thehive/services/TaxonomySrv.scala +++ b/thehive/app/org/thp/thehive/services/TaxonomySrv.scala @@ -14,7 +14,7 @@ import org.thp.thehive.services.OrganisationOps._ import org.thp.thehive.services.TagOps._ import org.thp.thehive.services.TaxonomyOps._ -import java.util.{Map => JMap} +import java.util.{Date, Map => JMap} import javax.inject.{Inject, Singleton} import scala.util.{Failure, Success, Try} @@ -49,13 +49,14 @@ class TaxonomySrv @Inject() (organisationSrv: OrganisationSrv, tagSrv: TagSrv) e override def getByName(name: String)(implicit graph: Graph): Traversal.V[Taxonomy] = startTraversal.getByNamespace(name) - def update(taxonomy: Taxonomy with Entity, input: Taxonomy)(implicit graph: Graph): Try[RichTaxonomy] = + def update(taxonomy: Taxonomy with Entity, input: Taxonomy)(implicit graph: Graph, authContext: AuthContext): Try[RichTaxonomy] = for { updatedTaxonomy <- get(taxonomy) .when(taxonomy.namespace != input.namespace)(_.update(_.namespace, input.namespace)) .when(taxonomy.description != input.description)(_.update(_.description, input.description)) .when(taxonomy.version != input.version)(_.update(_.version, input.version)) + .when(input != taxonomy)(_.update(_._updatedAt, Some(new Date)).update(_._updatedBy, Some(authContext.userId))) .richTaxonomy .getOrFail("Taxonomy") } yield updatedTaxonomy diff --git a/thehive/app/org/thp/thehive/services/UserSrv.scala b/thehive/app/org/thp/thehive/services/UserSrv.scala index da95d9091d..ae87da0d09 100644 --- a/thehive/app/org/thp/thehive/services/UserSrv.scala +++ b/thehive/app/org/thp/thehive/services/UserSrv.scala @@ -22,7 +22,7 @@ import play.api.Configuration import play.api.libs.json.{JsObject, Json} import java.util.regex.Pattern -import java.util.{List => JList, Map => JMap} +import java.util.{Date, List => JList, Map => JMap} import javax.inject.{Inject, Named, Singleton} import scala.util.{Failure, Success, Try} @@ -102,14 +102,22 @@ class UserSrv @Inject() ( Failure(AuthorizationError("You cannot lock yourself")) else for { - updatedUser <- get(user).update(_.locked, true: Boolean).getOrFail("User") - _ <- auditSrv.user.update(updatedUser, Json.obj("locked" -> true)) + updatedUser <- get(user) + .update(_.locked, true: Boolean) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("User") + _ <- auditSrv.user.update(updatedUser, Json.obj("locked" -> true)) } yield updatedUser def unlock(user: User with Entity)(implicit graph: Graph, authContext: AuthContext): Try[User with Entity] = for { - updatedUser <- get(user).update(_.locked, false: Boolean).getOrFail("User") - _ <- auditSrv.user.update(updatedUser, Json.obj("locked" -> false)) + updatedUser <- get(user) + .update(_.locked, false: Boolean) + .update(_._updatedAt, Some(new Date)) + .update(_._updatedBy, Some(authContext.userId)) + .getOrFail("User") + _ <- auditSrv.user.update(updatedUser, Json.obj("locked" -> false)) } yield updatedUser def current(implicit graph: Graph, authContext: AuthContext): Traversal.V[User] = get(EntityName(authContext.userId))