From 10ebf07df2def5f6a67f045f6564277e1aecf02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Wed, 8 Jan 2025 16:50:48 +0100 Subject: [PATCH] feature/Add endpoint validate user v5.1.0 --- .../main/scala/code/api/util/ApiRole.scala | 5 ++- .../main/scala/code/api/util/NewStyle.scala | 4 +++ .../scala/code/api/v5_1_0/APIMethods510.scala | 35 +++++++++++++++++++ .../code/api/v5_1_0/JSONFactory5.1.0.scala | 2 ++ .../code/model/dataAccess/AuthUser.scala | 7 ++++ .../test/scala/code/api/v5_1_0/UserTest.scala | 33 ++++++++++++++--- 6 files changed, 81 insertions(+), 5 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/ApiRole.scala b/obp-api/src/main/scala/code/api/util/ApiRole.scala index d24d7a1af2..aca12c413b 100644 --- a/obp-api/src/main/scala/code/api/util/ApiRole.scala +++ b/obp-api/src/main/scala/code/api/util/ApiRole.scala @@ -419,7 +419,10 @@ object ApiRole extends MdcLoggable{ lazy val canLockUser = CanLockUser() case class CanDeleteUser (requiresBankId: Boolean = false) extends ApiRole - lazy val canDeleteUser = CanDeleteUser() + lazy val canDeleteUser = CanDeleteUser() + + case class CanValidateUser (requiresBankId: Boolean = false) extends ApiRole + lazy val canValidateUser = CanValidateUser() case class CanGetUsersWithAttributes (requiresBankId: Boolean = false) extends ApiRole lazy val canGetUsersWithAttributes = CanGetUsersWithAttributes() diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index aad0c6572d..064c1682f1 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -1120,6 +1120,10 @@ object NewStyle extends MdcLoggable{ (false, callContext) } } + def validateUser(userPrimaryKey: UserPrimaryKey, callContext: Option[CallContext]): OBPReturnType[AuthUser] = Future { + val response = AuthUser.validateAuthUser(userPrimaryKey) + (unboxFullOrFail(response, callContext, s"$UserNotFoundById", 404), callContext) + } def findByUserId(userId: String, callContext: Option[CallContext]): OBPReturnType[User] = { Future { UserX.findByUserId(userId).map(user =>(user, callContext))} map { diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 2ad2ea9e4e..a5a0643bac 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2082,6 +2082,41 @@ trait APIMethods510 { } } + staticResourceDocs += ResourceDoc( + validateUserByUserId, + implementedInApiVersion, + nameOf(validateUserByUserId), + "PUT", + "/management/users/USER_ID", + "Validate a user", + s""" + |Validate the User by USER_ID. + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, + EmptyBody, + userLockStatusJson, + List( + $UserNotLoggedIn, + UserNotFoundByUserId, + UserHasMissingRoles, + UnknownError + ), + List(apiTagUser), + Some(List(canValidateUser))) + lazy val validateUserByUserId: OBPEndpoint = { + case "management" :: "users" :: userId :: Nil JsonPut req => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (user, callContext) <- NewStyle.function.findByUserId(userId, cc.callContext) + (userValidated, callContext) <- NewStyle.function.validateUser(user.userPrimaryKey, callContext) + } yield { + (UserValidatedJson(userValidated.validated.get), HttpCode.`200`(callContext)) + } + } + } + resourceDocs += ResourceDoc( getAggregateMetrics, implementedInApiVersion, diff --git a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala index 0a6da1e685..6df48da38c 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala @@ -578,6 +578,8 @@ case class TransactionRequestsJsonV510( case class SyncExternalUserJson(user_id: String) +case class UserValidatedJson(is_validated: Boolean) + object JSONFactory510 extends CustomJsonFormats { def createTransactionRequestJson(tr : TransactionRequest, transactionRequestAttributes: List[TransactionRequestAttributeTrait] ) : TransactionRequestJsonV510 = { diff --git a/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala b/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala index b138229c68..bd0c039966 100644 --- a/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala +++ b/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala @@ -1670,5 +1670,12 @@ def restoreSomeSessions(): Unit = { case _ => false // Error case } } + + def validateAuthUser(userPrimaryKey: UserPrimaryKey): Box[AuthUser] = tryo { + AuthUser.find(By(AuthUser.user, userPrimaryKey.value)) match { + case Full(user) => + user.validated(true).saveMe() + } + } } diff --git a/obp-api/src/test/scala/code/api/v5_1_0/UserTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/UserTest.scala index 44c380ac02..a13dab6f25 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/UserTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/UserTest.scala @@ -1,9 +1,7 @@ package code.api.v5_1_0 -import java.util.UUID - import code.api.util.APIUtil.OAuth._ -import code.api.util.ApiRole.{CanGetAnyUser, CanGetEntitlementsForAnyUserAtAnyBank} +import code.api.util.ApiRole.{CanGetAnyUser, CanGetEntitlementsForAnyUserAtAnyBank, CanValidateUser} import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn, attemptedToOpenAnEmptyBox} import code.api.v3_0_0.UserJsonV300 import code.api.v4_0_0.UserJsonV400 @@ -14,8 +12,11 @@ import code.users.Users import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.model.ErrorMessage import com.openbankproject.commons.util.ApiVersion +import net.liftweb.json.Serialization.write import org.scalatest.Tag +import java.util.UUID + class UserTest extends V510ServerSetup { /** * Test tags @@ -27,7 +28,8 @@ class UserTest extends V510ServerSetup { object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString) object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.getUserByProviderAndUsername)) object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.getEntitlementsAndPermissions)) - + object ValidateUserByUserId extends Tag(nameOf(Implementations5_1_0.validateUserByUserId)) + feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { When("We make a request v5.1.0") @@ -103,6 +105,29 @@ class UserTest extends V510ServerSetup { Users.users.vend.deleteResourceUser(user.id.get) } } + + + feature(s"test $ValidateUserByUserId version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ValidateUserByUserId, VersionOfApi) { + When("We make a request v5.1.0") + val request = (v5_1_0_Request / "management" / "users" / resourceUser1.userId ).PUT + val response = makePutRequest(request, write(UserValidatedJson(true))) + Then("We should get a 401") + response.code should equal(401) + response.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + + feature(s"test $ValidateUserByUserId version $VersionOfApi - Authorized access") { + scenario("We will call the endpoint with user credentials but without a proper entitlement", ValidateUserByUserId, VersionOfApi) { + When("We make a request v5.1.0") + val request = (v5_1_0_Request / "management" / "users" / resourceUser1.userId ).PUT <@ (user1) + val response = makePutRequest(request, write(UserValidatedJson(true))) + Then("error should be " + UserHasMissingRoles + CanValidateUser) + response.code should equal(403) + response.body.extract[ErrorMessage].message should be(UserHasMissingRoles + CanValidateUser) + } + } }