OBP-API/obp-api/src/test/scala/code/api/oauthTest.scala
hongwei e471601714 Merge remote-tracking branch 'Simon/develop' into develop
# Conflicts:
#	obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala
#	obp-api/src/test/scala/code/api/oauthTest.scala
2023-01-26 10:13:41 +01:00

370 lines
16 KiB
Scala

/**
Open Bank Project - API
Copyright (C) 2011-2019, TESOBE GmbH.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Email: contact@tesobe.com
TESOBE GmbH.
Osloer Strasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api
import java.util.ResourceBundle
import code.api.oauth1a.OauthParams._
import code.api.util.APIUtil.OAuth._
import code.api.util.ErrorMessages
import code.api.util.ErrorMessages._
import code.consumer.Consumers
import code.loginattempts.LoginAttempt
import code.model.dataAccess.{AuthUser, ResourceUser}
import code.setup.ServerSetup
import code.util.Helper.MdcLoggable
import dispatch.Defaults._
import dispatch._
import net.liftweb.common.{Box, Failure}
import net.liftweb.http.LiftRules
import net.liftweb.mapper.By
import net.liftweb.util.Helpers._
import org.scalatest._
import org.scalatestplus.selenium._
import scala.concurrent.Await
import scala.concurrent.duration._
case class OAuthResponse(
code: Int,
body: String
)
class OAuthTest extends ServerSetup {
override def beforeAll() = {
super.beforeAll()
AuthUser.bulkDelete_!!(By(AuthUser.username, "username with_space"))
ResourceUser.bulkDelete_!!(By(ResourceUser.providerId, "username with_space"))
AuthUser.bulkDelete_!!(By(AuthUser.username, "username with more than 1 space"))
ResourceUser.bulkDelete_!!(By(ResourceUser.providerId, "username with more than 1 space"))
}
def oauthRequest = baseRequest / "oauth"
//a url that will be guaranteed to resolve when the oauth redirects us to it
val selfCallback = Constant.HostName
val accountValidationError = ResourceBundle.getBundle(LiftRules.liftCoreResourceName).getObject("account.validation.error").toString
lazy val testConsumer = Consumers.consumers.vend.createConsumer(Some(randomString(40).toLowerCase), Some(randomString(40).toLowerCase), Some(true), Some("test application"), None, Some("description"), Some("eveline@example.com"), Some(selfCallback), None).openOrThrowException(attemptedToOpenAnEmptyBox)
lazy val disabledTestConsumer = Consumers.consumers.vend.createConsumer(Some(randomString(40).toLowerCase), Some(randomString(40).toLowerCase), Some(false), Some("test application disabled"), None, Some("description"), Some("eveline@example.com"), Some(selfCallback), None).openOrThrowException(attemptedToOpenAnEmptyBox)
lazy val user1Password = randomString(10)
lazy val user1 =
AuthUser.create.
email(randomString(3)+"@example.com").
username("username with_space").
password(user1Password).
validated(true).
firstName(randomString(10)).
lastName(randomString(10)).
saveMe
lazy val user2Password = randomString(10)
lazy val user2 =
AuthUser.create.
email(randomString(3)+"@example.com").
username("username with more than 1 space").
password(user2Password).
validated(false).
firstName(randomString(10)).
lastName(randomString(10)).
saveMe
lazy val consumer = new Consumer (testConsumer.key.get,testConsumer.secret.get)
lazy val disabledConsumer = new Consumer (disabledTestConsumer.key.get, disabledTestConsumer.secret.get)
lazy val notRegisteredConsumer = new Consumer (randomString(5),randomString(5))
private def getAPIResponse(req : Req) : OAuthResponse = {
Await.result(
for(response <- Http.default(req > as.Response(p => p)))
yield OAuthResponse(response.getStatusCode, response.getResponseBody)
, Duration.Inf)
}
def sendPostRequest(req: Req): OAuthResponse = {
val postReq = req.POST
getAPIResponse(postReq)
}
def getRequestToken(consumer: Consumer, callback: String): OAuthResponse = {
val request = (oauthRequest / "initiate").POST <@ (consumer, callback)
sendPostRequest(request)
}
def getAccessToken(consumer: Consumer, requestToken : Token, verifier : String): OAuthResponse = {
val request = (oauthRequest / "token").POST <@ (consumer, requestToken, verifier)
sendPostRequest(request)
}
def extractToken(body: String) : Token = {
val oauthParams = body.split("&")
val token = oauthParams(0).split("=")(1)
val secret = oauthParams(1).split("=")(1)
Token(token, secret)
}
case class Browser() extends HtmlUnit with MdcLoggable{
implicit val driver = webDriver
def getVerifier(loginPage: String, userName: String, password: String) : Box[String] = {
tryo{
go.to(loginPage)
textField("username").value = userName
val pwField = NameQuery("password").webElement
pwField.clear()
pwField.sendKeys(password)
click on XPathQuery("""//input[@type='submit']""")
val newURL = currentUrl
val newPageSource = pageSource
val verifier =
if(newURL.contains("verifier"))
{
logger.info("redirected during oauth")
val params = newURL.split("&")
params(1).split("=")(1)
}
else if(newPageSource.contains(ErrorMessages.UsernameHasBeenLocked ))
{
logger.info("the username has been locked")
XPathQuery(ErrorMessages.UsernameHasBeenLocked).element.text
}
else if(newPageSource.contains(accountValidationError))
{
logger.info(accountValidationError)
accountValidationError
}
else{
logger.info("the verifier is in the page")
XPathQuery("""//div[@id='verifier']""").element.text
}
close()
quit()
verifier
}
}
}
def getVerifier(requestToken: String, userName: String, password: String): Box[String] = {
val b = Browser()
val loginPage = (oauthRequest / "authorize" <<? List((TokenName, requestToken))).toRequest.getUrl
b.getVerifier(loginPage, userName, password)
}
def getVerifier(userName: String, password: String): Box[String] = {
val b = Browser()
val loginPage = (oauthRequest / "authorize").toRequest.getUrl
b.getVerifier(loginPage, userName, password)
}
/************************ the tags ************************/
object Current extends Tag("current")
object Oauth extends Tag("oauth")
object RequestToken extends Tag("requestToken")
object AccessToken extends Tag("accessToken")
object Verifier extends Tag("verifier")
/************************ the tests ************************/
feature("request token"){
scenario("we get a request token", RequestToken, Oauth) {
Given("The application is registered and does not have a callback URL")
When("the request is sent")
val reply = getRequestToken(consumer, oob)
Then("we should get a 200 code")
reply.code should equal (200)
And("we can extract the token form the body")
val requestToken = extractToken(reply.body)
}
scenario("we get a request token with a callback URL", RequestToken, Oauth) {
Given("The application is registered and have a callback URL")
When("the request is sent")
val reply = getRequestToken(consumer, "localhost:8080/app")
Then("we should get a 200 code")
reply.code should equal (200)
And("we can extract the token form the body")
val requestToken = extractToken(reply.body)
}
scenario("we don't get a request token since the application is not registered", RequestToken, Oauth) {
Given("The application not registered")
When("the request is sent")
val reply = getRequestToken(notRegisteredConsumer, oob)
Then("we should get a 401 code")
reply.code should equal (401)
}
scenario("we don't get a request token since the application is not registered even with a callback URL", RequestToken, Oauth) {
Given("The application not registered")
When("the request is sent")
val reply = getRequestToken(notRegisteredConsumer, "localhost:8080/app")
Then("we should get a 401 code")
reply.code should equal (401)
}
scenario("We don't get a request token since the application is not enabled", RequestToken, Oauth) {
Given("The application is not enabled")
When("The request is sent")
val reply = getRequestToken(disabledConsumer, oob)
Then("We should get a 401 code")
reply.code should equal (401)
And("We should get message")
reply.body should equal (ErrorMessages.InvalidConsumerCredentials)
}
}
feature("Verifier"){
scenario("the user login and get redirected to the application back", Verifier, Oauth){
Given("we will use a valid request token")
val reply = getRequestToken(consumer, selfCallback)
val requestToken = extractToken(reply.body)
When("the browser is launched to login")
val verifier = getVerifier(requestToken.value, user1.username.get, user1Password)
Then("we should get a verifier")
verifier.openOrThrowException(attemptedToOpenAnEmptyBox).nonEmpty should equal (true)
}
scenario("the user login and is asked to enter the verifier manually", Verifier, Oauth){
Given("we will use a valid request token")
val reply = getRequestToken(consumer, oob)
val requestToken = extractToken(reply.body)
When("the browser is launched to login")
val verifier = getVerifier(requestToken.value, user1.username.get, user1Password)
Then("we should get a verifier")
verifier.isEmpty should equal (false)
}
scenario("the user cannot login because there is no token", Verifier, Oauth){
Given("there will be no token")
When("the browser is launched to login")
val verifier = getVerifier(user1.username.get, user1Password)
Then("we should not get a verifier")
verifier.isEmpty should equal (true)
}
scenario("the user cannot login because the token does not exist", Verifier, Oauth){
Given("we will use a random request token")
When("the browser is launched to login")
val verifier = getVerifier(randomString(4), user1.username.get, user1Password)
Then("we should not get a verifier")
verifier.isEmpty should equal (true)
}
}
feature("access token"){
scenario("we get an access token without a callback", AccessToken, Oauth){
Given("we will first get a request token and a verifier")
val reply = getRequestToken(consumer, oob)
val requestToken = extractToken(reply.body)
val verifier = getVerifier(requestToken.value, user1.username.get, user1Password)
When("when we ask for an access token")
val accessToken = getAccessToken(consumer, requestToken, verifier.openOrThrowException(attemptedToOpenAnEmptyBox))
Then("we should get an access token")
extractToken(accessToken.body)
}
scenario("we get an access token with a callback", AccessToken, Oauth){
Given("we will first get a request token and a verifier")
val reply = getRequestToken(consumer, selfCallback)
val requestToken = extractToken(reply.body)
val verifier = getVerifier(requestToken.value, user1.username.get, user1Password)
When("when we ask for an access token")
val accessToken = getAccessToken(consumer, requestToken, verifier.openOrThrowException(attemptedToOpenAnEmptyBox))
Then("we should get an access token")
extractToken(accessToken.body)
}
scenario("we don't get an access token because the verifier is wrong", AccessToken, Oauth){
Given("we will first get a request token and a random verifier")
val reply = getRequestToken(consumer, oob)
val requestToken = extractToken(reply.body)
When("when we ask for an access token")
val accessTokenReply = getAccessToken(consumer, requestToken, randomString(5))
Then("we should get a 401")
accessTokenReply.code should equal (401)
}
scenario("we don't get an access token because the request token is wrong", AccessToken, Oauth){
Given("we will first get request token and a verifier")
val reply = getRequestToken(consumer, selfCallback)
val requestToken = extractToken(reply.body)
val verifier = getVerifier(requestToken.value, user1.username.get, user1Password)
When("when we ask for an access token with a request token")
val randomRequestToken = Token(randomString(5), randomString(5))
val accessTokenReply = getAccessToken(consumer, randomRequestToken, verifier.openOrThrowException(attemptedToOpenAnEmptyBox))
Then("we should get a 401")
accessTokenReply.code should equal (401)
}
scenario("we don't get an access token because the requestToken and the verifier are wrong", AccessToken, Oauth){
Given("we will first get request token and a verifier")
val reply = getRequestToken(consumer, selfCallback)
When("when we ask for an access token with a request token")
val randomRequestToken = Token(randomString(5), randomString(5))
val accessTokenReply = getAccessToken(consumer, randomRequestToken, randomString(5))
Then("we should get a 401")
accessTokenReply.code should equal (401)
}
}
feature("Login is locked") {
scenario("valid Username, invalid password, login in too many times. The username will be locked", Verifier, Oauth) {
Given("we will use a valid request token to get the valid username and password")
val reply = getRequestToken(consumer, selfCallback)
val requestToken = extractToken(reply.body)
Then("we set the valid username, invalid password and try more than 2 times")
setPropsValues("max.bad.login.attempts"-> "2")
val invalidPassword = "wrongpassword"
var verifier = getVerifier(requestToken.value, user1.username.get, invalidPassword)
verifier = getVerifier(requestToken.value, user1.username.get, invalidPassword)
verifier = getVerifier(requestToken.value, user1.username.get, invalidPassword)
verifier = getVerifier(requestToken.value, user1.username.get, invalidPassword)
Then("we should get a locked account verifier")
verifier.asInstanceOf[Failure].msg.contains(ErrorMessages.UsernameHasBeenLocked) should equal (true)
Then("We login in with valid username and password, it will still be failed")
verifier = getVerifier(requestToken.value, user1.username.get, user1Password)
Then("we should get a locked account verifier")
verifier.asInstanceOf[Failure].msg.contains(ErrorMessages.UsernameHasBeenLocked) should equal (true)
Then("We unlock the username")
LoginAttempt.resetBadLoginAttempts(user1.getProvider(), user1.username.get)
setPropsValues("max.bad.login.attempts"-> "5")
}
}
feature("We try to login with a user which is not verified") {
scenario("Valid username, valid password but user is not verified", Verifier, Oauth) {
Given("we will use a valid request token to get the valid username and password")
val reply = getRequestToken(consumer, selfCallback)
val requestToken = extractToken(reply.body)
Then("we set the valid username, valid password and try to login")
val verifier = getVerifier(requestToken.value, user2.username.get, user2Password)
org.scalameta.logger.elem(verifier)
Then("we should get a message: " + accountValidationError)
println(verifier)
verifier.contains(accountValidationError) should equal (true)
}
}
}