Skip to content

Commit

Permalink
Impersonate: Add the ability to bypass credentials during tests with …
Browse files Browse the repository at this point in the history
…become user admin tool (#1)

* Add impersonate feature to stress testing

* Add some refactoring
  • Loading branch information
juanjmerono authored Jul 20, 2016
1 parent b5b8f6c commit e5a429f
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 32 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ There are several things you can tune in this test:

```$mvn gatling:execute -Dtesturl=https://my-sakai-instance```

- **Pause Between Requests**: You can change the pause between requests the process stops a random time between min and max values (in seconds)

```$mvn gatling:execute -Dminpause=N -Dmaxpause=M```

- **Concurrent Users and RampUp time**: You can change the number of concurrent users of each type and the time to rampup them by typing

```$mvn gatling:execute -DrandomUsers=<RandomUsers> -DexhausUsers=<ExhaustiveUsers> -DrampUpTime=<Seconds>```
Expand All @@ -59,6 +63,9 @@ There are several things you can tune in this test:

- **User credentials**: There is one csv file _data/user_credentials.csv_ to add test users to your test case.

- **Impersonate Users**: For production environments probably you won't be able to know user credentials. In that case you can login as admin and impersonate test users. In that case you need to provide admin credentials in _data/admin_credentials.csv_ file.

```$mvn gatling:execute -DimpersonateUsers=true```

Exploring the results
=====================
Expand Down
3 changes: 3 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
<!-- Browse Loop -->
<siteLoop>1</siteLoop>
<toolLoop>1</toolLoop>
<!-- Impersonate Users to bypass credentials -->
<impersonateUsers>false</impersonateUsers>
</properties>

<dependencies>
Expand Down Expand Up @@ -96,6 +98,7 @@
<jvmArg>-Duser-loop=${userLoop}</jvmArg>
<jvmArg>-Dsite-loop=${siteLoop}</jvmArg>
<jvmArg>-Dtool-loop=${toolLoop}</jvmArg>
<jvmArg>-Dimpersonate-users=${impersonateUsers}</jvmArg>
</jvmArgs>
</configuration>
<executions>
Expand Down
2 changes: 2 additions & 0 deletions src/test/resources/data/admin_credentials.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
adminusername,adminpassword
admin,admin
140 changes: 108 additions & 32 deletions src/test/scala/computerdatabase/SakaiSimulation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class SakaiSimulation extends Simulation {
.baseURL(System.getProperty("test-url"))
/**.inferHtmlResources(BlackList(".*(\.css|\.js|\.png|\.jpg|\.gif|thumb).*"), WhiteList())*/
.userAgentHeader("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36")
.disableCaching
.extraInfoExtractor(extraInfo => List(extraInfo.request.getUrl,extraInfo.response.statusCode,extraInfo.response.bodyLength))

val headers = Map(
Expand All @@ -34,7 +35,32 @@ class SakaiSimulation extends Simulation {
"Upgrade-Insecure-Requests" -> "1")

val users = csv("user_credentials.csv").random
val admins = csv("admin_credentials.csv").random
val jsfViewStateCheck = css("input[name=com\\.sun\\.faces\\.VIEW]", "value").saveAs("viewState")

def join(first: Vector[String], second: Vector[String]) : Vector[(String,String)] = (first zip second.map(s => URLDecoder.decode(s,"UTF-8")))
def checkAttrs(cssSelector: String, attrName: String, varName: String) = css(cssSelector,attrName).findAll.saveAs(varName)
def checkElement(cssSelector: String, varName: String) = css(cssSelector).findAll.saveAs(varName)

def checkItsMe (username: String) =
http("GetCurrentUser")
.get("/direct/user/current.json")
.headers(headers)
.check(status.is(successStatus))
.check(jsonPath("$[?(@.eid=='" + username + "')]"))

def joinInSessionOneFiltered(session: Session, firstName: String, secondName: String, finalName: String, filteredBy: String) =
session
.remove(firstName)
.remove(secondName)
.set(finalName, join(session(firstName).as[Vector[String]],session(secondName).as[Vector[String]]).filter(_._1 contains filteredBy)(0))

def joinInSession(session: Session, firstName: String, secondName: String, finalName: String) =
session
.remove(firstName)
.remove(secondName)
.set(finalName, util.Random.shuffle(join(session(firstName).as[Vector[String]],session(secondName).as[Vector[String]])))

object Gateway {
val gateway = group("Gateway") {
exec(http("Portal")
Expand All @@ -46,22 +72,70 @@ class SakaiSimulation extends Simulation {
}

object Login {
val login = group("Login") {
feed(users)
.exec(http("XLogin")
.post("/portal/xlogin")
.headers(headers)
.formParam("eid", "${username}")
.formParam("pw", "${password}")
.formParam("submit", "Log+In")
.check(status.is(successStatus))
.check(css("div.fav-title > a","href").findAll.saveAs("siteUrls"))
.check(css("div.fav-title > a","title").findAll.saveAs("siteTitles")))
.pause(pauseMin,pauseMax)
.exec(session => {
val mySites: Vector[(String,String)] = (session("siteTitles").as[Vector[String]] zip session("siteUrls").as[Vector[String]])
session.set("sites", util.Random.shuffle(mySites))
})
val login = (impersonate: Boolean) =>
group("Login") {
doIfOrElse(impersonate) { /** Login as admin and then impersonate user */
feed(admins)
.exec(http("XLogin")
.post("/portal/xlogin")
.headers(headers)
.formParam("eid", "${adminusername}")
.formParam("pw", "${adminpassword}")
.formParam("submit", "Log+In")
.check(status.is(successStatus)))
.pause(pauseMin,pauseMax)
.exec(checkItsMe("${adminusername}"))
.group("BecomeUser") {
exec(http("AdminWorkspace")
.get("/portal/site/!admin")
.headers(headers)
.check(status.is(successStatus))
.check(checkAttrs("a.Mrphs-toolsNav__menuitem--link","href","adminToolUrls"))
.check(checkAttrs("span.Mrphs-toolsNav__menuitem--icon","class","adminToolIds")))
.exec(session => { joinInSessionOneFiltered(session,"adminToolIds","adminToolUrls","sutool","icon-sakai-su") })
.pause(pauseMin,pauseMax)
.exec(http("BecomeUser")
.get("${sutool._2}")
.headers(headers)
.check(status.is(successStatus))
.check(jsfViewStateCheck)
.check(css("form[id=su]","action").saveAs("supost")))
.pause(pauseMin,pauseMax)
.feed(users)
.exec(http("BecomeUserPost")
.post("${supost}")
.headers(headers)
.formParam("su:username", "${username}")
.formParam("su:become", "Become user")
.formParam("com.sun.faces.VIEW", "${viewState}")
.formParam("su", "su")
.check(status.is(successStatus)))
.pause(pauseMin,pauseMax)
.exec(checkItsMe("${username}"))
.exec(http("UserHome")
.get("/portal")
.headers(headers)
.check(status.is(successStatus))
.check(checkAttrs("div.fav-title > a","href","siteUrls"))
.check(checkAttrs("div.fav-title > a","title","siteTitles")))
.exec(session => { joinInSession(session,"siteTitles","siteUrls","sites") })
}
}
{ /** Use real credentials */
feed(users)
.exec(http("XLogin")
.post("/portal/xlogin")
.headers(headers)
.formParam("eid", "${username}")
.formParam("pw", "${password}")
.formParam("submit", "Log+In")
.check(status.is(successStatus))
.check(checkAttrs("div.fav-title > a","href","siteUrls"))
.check(checkAttrs("div.fav-title > a","title","siteTitles")))
.pause(pauseMin,pauseMax)
.exec(session => { joinInSession(session,"siteTitles","siteUrls","sites") })
.exec(checkItsMe("${username}"))
}
}
}

Expand All @@ -79,10 +153,7 @@ class SakaiSimulation extends Simulation {
.pause(pauseMin,pauseMax)
/** Take care of all iframed tools */
.doIf("${frameUrls.exists()}") {
exec(session => {
val myFrames: Vector[(String,String)] = (session("frameNames").as[Vector[String]] zip session("frameUrls").as[Vector[String]].map(s => URLDecoder.decode(s,"UTF-8")))
session.set("frames", util.Random.shuffle(myFrames)).remove("frameUrls").remove("frameNames")
})
exec(session => { joinInSession(session,"frameNames","frameUrls","frames") })
.foreach("${frames}","frame") {
exec(http("${frame._1}")
.get("${frame._2}")
Expand Down Expand Up @@ -124,13 +195,10 @@ class SakaiSimulation extends Simulation {
.headers(headers)
.check(status.is(successStatus))
.check(css("title:contains('Sakai : ${site._1} :')").exists)
.check(css("a.Mrphs-toolsNav__menuitem--link","href").findAll.saveAs("toolUrls"))
.check(css("span.Mrphs-toolsNav__menuitem--title").findAll.saveAs("toolNames")))
.check(checkAttrs("a.Mrphs-toolsNav__menuitem--link","href","toolUrls"))
.check(checkElement("span.Mrphs-toolsNav__menuitem--title","toolNames")))
.pause(pauseMin,pauseMax)
.exec(session => {
val myTools: Vector[(String,String)] = (session("toolNames").as[Vector[String]] zip session("toolUrls").as[Vector[String]].map(s => URLDecoder.decode(s,"UTF-8")))
session.set("tools", util.Random.shuffle(myTools))
})
.exec(session => { joinInSession(session,"toolNames","toolUrls","tools") })
},
BrowseTools.browse(random)
)
Expand All @@ -156,23 +224,31 @@ class SakaiSimulation extends Simulation {
}

object Logout {
val logout = group("Logout") {
val logout = (impersonate: Boolean) =>
group("Logout") {
exec(http("Logout")
.get("/portal/logout")
.headers(headers)
.check(status.is(successStatus)))
.doIf(impersonate) {
exec(checkItsMe("${adminusername}"))
.exec(http("AdminLogout")
.get("/portal/logout")
.headers(headers)
.check(status.is(successStatus)))
}
}
}

object SakaiSimulationSteps {
val test = (random: Boolean) => repeat(userLoop) {
exec(Gateway.gateway,Login.login,BrowseSites.browse(random),Logout.logout)
val test = (random: Boolean, impersonate: Boolean) => repeat(userLoop) {
exec(Gateway.gateway,Login.login(impersonate),BrowseSites.browse(random),Logout.logout(impersonate))
}
}


val randomUsersScn = scenario("SakaiRandomUserSimulation").exec(SakaiSimulationSteps.test(true))
val exhaustiveUsersScn = scenario("SakaiExhaustiveUserSimulation").exec(SakaiSimulationSteps.test(false))
val impersonateUsers = System.getProperty("impersonate-users") == "true"
val randomUsersScn = scenario("SakaiRandomUserSimulation").exec(SakaiSimulationSteps.test(true,impersonateUsers))
val exhaustiveUsersScn = scenario("SakaiExhaustiveUserSimulation").exec(SakaiSimulationSteps.test(false,impersonateUsers))

object Setup {
val scenario = ListBuffer[io.gatling.core.structure.PopulationBuilder]()
Expand Down

0 comments on commit e5a429f

Please sign in to comment.