Skip to content

Commit

Permalink
#2309 Add extraData related to user lockout
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Jan 21, 2022
1 parent 5db2247 commit f5baad1
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 20 deletions.
5 changes: 3 additions & 2 deletions dto/src/main/scala/org/thp/thehive/dto/v1/User.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.thp.thehive.dto.v1

import org.thp.scalligraph.controllers.FFile
import play.api.libs.json.{Json, OFormat, Writes}
import play.api.libs.json.{JsObject, Json, OFormat, Writes}

import java.util.Date

Expand Down Expand Up @@ -32,7 +32,8 @@ case class OutputUser(
permissions: Set[String],
organisation: String,
avatar: Option[String],
organisations: Seq[OutputOrganisationProfile]
organisations: Seq[OutputOrganisationProfile],
extraData: JsObject
)

object OutputUser {
Expand Down
57 changes: 39 additions & 18 deletions thehive/app/org/thp/thehive/controllers/v1/UserCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,23 @@ class UserCtrl @Inject() (
auditSrv: AuditSrv,
attachmentSrv: AttachmentSrv,
implicit val db: Database
) extends QueryableCtrl {
) extends QueryableCtrl
with UserRenderer {

override val entityName: String = "user"
override val publicProperties: PublicProperties = properties.user
lazy val localPasswordAuthSrv: Try[LocalPasswordAuthSrv] = {
def getLocalPasswordAuthSrv(authSrv: AuthSrv): Option[LocalPasswordAuthSrv] =
authSrv match {
case lpas: LocalPasswordAuthSrv => Some(lpas)
case mas: MultiAuthSrv => mas.authProviders.flatMap(getLocalPasswordAuthSrv).headOption
case _ => None
}
getLocalPasswordAuthSrv(authSrv) match {
case Some(lpas) => Success(lpas)
case None => Failure(NotSupportedError("The local password authentication is not enabled"))
}
}

override val initialQuery: Query =
Query.init[Traversal.V[User]]("listUser", (graph, authContext) => organisationSrv.get(authContext.organisation)(graph).users)
Expand All @@ -53,12 +66,32 @@ class UserCtrl @Inject() (
override def pageQuery(limitedCountThreshold: Long): ParamQuery[UserOutputParam] =
Query.withParam[UserOutputParam, Traversal.V[User], IteratorOutput](
"page",
(params, userSteps, authContext) =>
params
.organisation
.fold(userSteps.richUser(authContext))(org => userSteps.richUser(authContext, EntityIdOrName(org)))
.page(params.from, params.to, params.extraData.contains("total"), limitedCountThreshold)
{
case (UserOutputParam(from, to, extraData, organisation), userSteps, authContext) =>
userSteps.richPage(from, to, extraData.contains("total"), limitedCountThreshold) {
_.richUserWithCustomRenderer(
organisation.fold(authContext.organisation)(EntityIdOrName(_)),
userStatsRenderer(extraData - "Total", localPasswordAuthSrv.toOption)(authContext)
)(authContext)
}
}
)
//
// authContext
// }))(org => userSteps.richUser(authContext, EntityIdOrName(org)))
// .page(params.from, params.to, params.extraData.contains("total"), limitedCountThreshold)
// )
// override def pageQuery(limitedCountThreshold: Long): ParamQuery[OutputParam] =
// Query.withParam[OutputParam, Traversal.V[Case], IteratorOutput](
// "page",
// {
// case (OutputParam(from, to, extraData), caseSteps, authContext) =>
// caseSteps.richPage(from, to, extraData.contains("total"), limitedCountThreshold) {
// _.richCaseWithCustomRenderer(caseStatsRenderer(extraData - "total")(authContext))(authContext)
// }
// }
// )

override val outputQuery: Query =
Query.outputWithContext[RichUser, Traversal.V[User]]((userSteps, authContext) => userSteps.richUser(authContext))

Expand Down Expand Up @@ -123,18 +156,6 @@ class UserCtrl @Inject() (
} yield Results.NoContent
}

lazy val localPasswordAuthSrv: Try[LocalPasswordAuthSrv] = {
def getLocalPasswordAuthSrv(authSrv: AuthSrv): Option[LocalPasswordAuthSrv] =
authSrv match {
case lpas: LocalPasswordAuthSrv => Some(lpas)
case mas: MultiAuthSrv => mas.authProviders.flatMap(getLocalPasswordAuthSrv).headOption
}
getLocalPasswordAuthSrv(authSrv) match {
case Some(lpas) => Success(lpas)
case None => Failure(NotSupportedError("The local password authentication is not enabled"))
}
}

def resetFailedAttempts(userIdOrName: String): Action[AnyContent] =
entrypoint("reset user")
.authTransaction(db) { implicit request => implicit graph =>
Expand Down
42 changes: 42 additions & 0 deletions thehive/app/org/thp/thehive/controllers/v1/UserRenderer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.thp.thehive.controllers.v1

import org.thp.scalligraph.auth.AuthContext
import org.thp.scalligraph.traversal.TraversalOps._
import org.thp.scalligraph.traversal.{Converter, Traversal}
import org.thp.thehive.models.{Permissions, User}
import org.thp.thehive.services.LocalPasswordAuthSrv
import org.thp.thehive.services.OrganisationOps._
import org.thp.thehive.services.UserOps._
import play.api.libs.json._

import java.util.{Map => JMap}

trait UserRenderer extends BaseRenderer[User] {

def lockout(
localPasswordAuthSrv: Option[LocalPasswordAuthSrv]
)(implicit authContext: AuthContext): Traversal.V[User] => Traversal[JsObject, JMap[String, Any], Converter[JsObject, JMap[String, Any]]] =
_.project(_.by.by(_.organisations.users(Permissions.manageUser).current.option))
.domainMap {
case (user, Some(_)) =>
Json.obj(
"lastFailed" -> user.lastFailed,
"failedAttempts" -> user.failedAttempts,
"lockedUntil" -> localPasswordAuthSrv.flatMap(_.lockedUntil(user))
)
case _ => JsObject.empty
}

def userStatsRenderer(extraData: Set[String], authSrv: Option[LocalPasswordAuthSrv])(implicit
authContext: AuthContext
): Traversal.V[User] => JsTraversal = { implicit traversal =>
baseRenderer(
extraData,
traversal,
{
case (f, "lockout") => addData("lockout", f)(lockout(authSrv))
case (f, _) => f
}
)
}
}

0 comments on commit f5baad1

Please sign in to comment.