From 5616e38ed700cb566048ecdcb7813f5d9f762257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9arch?= Date: Thu, 5 Sep 2019 15:55:59 +0200 Subject: [PATCH] Improved error and debugging messages --- thehive-backend/app/services/OAuth2Srv.scala | 9 +-- .../services/mappers/GroupUserMapper.scala | 60 +++++++++---------- .../services/mappers/SimpleUserMapper.scala | 2 +- 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/thehive-backend/app/services/OAuth2Srv.scala b/thehive-backend/app/services/OAuth2Srv.scala index af8ad47163..d52078d5b8 100644 --- a/thehive-backend/app/services/OAuth2Srv.scala +++ b/thehive-backend/app/services/OAuth2Srv.scala @@ -79,7 +79,7 @@ class OAuth2Srv( } private def getAuthTokenAndAuthenticate(clientId: String, code: String)(implicit request: RequestHeader): Future[AuthContext] = { - logger.debug("Getting user token with the code from the response!") + logger.debug("Getting user token with the code from the response") withOAuth2Config { cfg ⇒ ws.url(cfg.tokenUrl) .post( @@ -99,6 +99,7 @@ class OAuth2Srv( .flatMap { r ⇒ r.status match { case Status.OK ⇒ + logger.debug("Getting user info using access token") val accessToken = (r.json \ "access_token").asOpt[String].getOrElse("") val authHeader = "Authorization" → s"bearer $accessToken" ws.url(cfg.userUrl) @@ -106,15 +107,15 @@ class OAuth2Srv( .get() .flatMap { userResponse ⇒ if (userResponse.status != Status.OK) { - Future.failed(AuthenticationError(s"unexpected response from server: ${userResponse.status} ${userResponse.body}")) + Future.failed(AuthenticationError(s"Unexpected response from server: ${userResponse.status} ${userResponse.body}")) } else { val response = userResponse.json.asInstanceOf[JsObject] getOrCreateUser(response, authHeader) } } case _ ⇒ - logger.error(s"unexpected response from server: ${r.status} ${r.body}") - Future.failed(AuthenticationError("unexpected response from server")) + logger.error(s"Unexpected response from server: ${r.status} ${r.body}") + Future.failed(AuthenticationError("Unexpected response from server")) } } } diff --git a/thehive-backend/app/services/mappers/GroupUserMapper.scala b/thehive-backend/app/services/mappers/GroupUserMapper.scala index d0b50c4bfd..cf036ce379 100644 --- a/thehive-backend/app/services/mappers/GroupUserMapper.scala +++ b/thehive-backend/app/services/mappers/GroupUserMapper.scala @@ -55,14 +55,6 @@ class GroupUserMapper( case None ⇒ Seq.empty[String] } } - - def apply(input: String): Seq[String] = parseAll(expr, input) match { - case Success(result, _) ⇒ result - case failure : NoSuccess ⇒ { - logger.error(s"Can't parse groups list: ${failure.msg}") - Seq.empty[String] - } - } } override def getUserFields(jsValue: JsValue, authHeader: Option[(String, String)]): Future[Fields] = { @@ -70,30 +62,36 @@ class GroupUserMapper( case Some(groupsEndpointUrl) ⇒ { logger.debug(s"Retreiving groups from ${groupsEndpointUrl}") val apiCall = authHeader.fold(ws.url(groupsEndpointUrl))(headers ⇒ ws.url(groupsEndpointUrl).addHttpHeaders(headers)) - apiCall.get.flatMap { r ⇒ - val jsonGroups = (r.json \ groupsAttrName).as[Seq[String]] - mapGroupsAndBuildUserFields(jsValue, jsonGroups) - } + apiCall.get.flatMap { r ⇒ extractGroupsThenBuildUserFields(jsValue, r.json) } } case None ⇒ { - val jsonGroups: Seq[String] = (jsValue \ groupsAttrName) match { - // Groups received as valid JSON array - case JsDefined(JsArray(groupsList)) ⇒ groupsList.map(_.as[String]).toList - // Groups list received as string (invalid JSON, for example: "ROLE" or "['Role 1', ROLE2, 'Role_3']") - case JsDefined(JsString(groupsStr)) ⇒ (new RoleListParser).apply(groupsStr) - // Invalid group list - case JsDefined(error) ⇒ { - logger.error(s"Invalid groups list received in user info: '${error}' of type ${error.getClass}") - Seq.empty[String] - } - // Groups field is undefined - case _: JsUndefined ⇒ { - logger.error(s"OAuth2 not configured properly: groups attribute ${groupsAttrName} doesn't exist in user info") - Seq.empty[String] - } + logger.debug(s"Extracting groups from user info") + extractGroupsThenBuildUserFields(jsValue, jsValue) + } + } + } + + private def extractGroupsThenBuildUserFields(jsValue: JsValue, groupsContainer: JsValue): Future[Fields] = { + (groupsContainer \ groupsAttrName) match { + // Groups received as valid JSON array + case JsDefined(JsArray(groupsList)) ⇒ mapGroupsAndBuildUserFields(jsValue, groupsList.map(_.as[String]).toList) + + // Groups list received as string (invalid JSON, for example: "ROLE" or "['Role 1', ROLE2, 'Role_3']") + case JsDefined(JsString(groupsStr)) ⇒ { + val parser = new RoleListParser + parser.parseAll(parser.expr, groupsStr) match { + case parser.Success(result, _) ⇒ mapGroupsAndBuildUserFields(jsValue, result) + case err: parser.NoSuccess ⇒ Future.failed(AuthenticationError(s"User info fails: can't parse groups list (${err.msg})")) } - mapGroupsAndBuildUserFields(jsValue, jsonGroups) } + + // Invalid group list + case JsDefined(error) ⇒ + Future.failed(AuthenticationError(s"User info fails: invalid groups list received in user info ('${error}' of type ${error.getClass})")) + + // Groups field is undefined + case _: JsUndefined ⇒ + Future.failed(AuthenticationError(s"User info fails: groups attribute ${groupsAttrName} doesn't exist in user info")) } } @@ -102,10 +100,10 @@ class GroupUserMapper( val roles = if (mappedRoles.nonEmpty) mappedRoles else defaultRoles if (roles.isEmpty) { - Future.failed(AuthorizationError(s"No matched roles for user.")) + Future.failed(AuthorizationError(s"No matched roles for user")) } else { - logger.debug(s"Computed roles: ${roles}") + logger.debug(s"Computed roles: ${roles.mkString(", ")}") val fields = for { login ← (jsValue \ loginAttrName).validate[String] @@ -113,7 +111,7 @@ class GroupUserMapper( } yield Fields(Json.obj("login" → login, "name" → name, "roles" → roles)) fields match { case JsSuccess(f, _) ⇒ Future.successful(f) - case JsError(errors) ⇒ Future.failed(AuthenticationError(s"User info fails: ${errors.map(_._1).mkString}")) + case JsError(errors) ⇒ Future.failed(AuthenticationError(s"User info fails: ${errors.map(_._2).map(_.map(_.messages.mkString(", ")).mkString("; ")).mkString}")) } } } diff --git a/thehive-backend/app/services/mappers/SimpleUserMapper.scala b/thehive-backend/app/services/mappers/SimpleUserMapper.scala index 323d6f9715..598d9d2ece 100644 --- a/thehive-backend/app/services/mappers/SimpleUserMapper.scala +++ b/thehive-backend/app/services/mappers/SimpleUserMapper.scala @@ -37,7 +37,7 @@ class SimpleUserMapper( } yield Fields(Json.obj("login" → login, "name" → name, "roles" → roles)) fields match { case JsSuccess(f, _) ⇒ Future.successful(f) - case JsError(errors) ⇒ Future.failed(AuthenticationError(s"User info fails: ${errors.map(_._1).mkString}")) + case JsError(errors) ⇒ Future.failed(AuthenticationError(s"User info fails: ${errors.map(_._2).map(_.map(_.messages.mkString(", ")).mkString("; ")).mkString}")) } } }