Functional testing with Spring Security plugin in a Grails app - grails

I have a secured grails application with the spring security plugin and right now I am trying to do some tests with the login process with no success till now. Has anybody an idea of what is the problem?
This is my LoginPage.groovy
package pages.login
import geb.Page
class LoginPage extends Page {
static url = "login/auth"
static at = {
title ==~ /Login/
}
static content = {
loginForm { $("form") }
username { $("input", type:"text", name:"j_username") }
password { $("input", type:"password", name:"j_password") }
loginButton{ $("input", type:"submit", value:"Login") }
}
}
And this is the test using junit4:
import geb.junit4.GebReportingTest
import pages.copyright.*
import pages.login.LoginPage
import org.junit.Test
class CopyrightCRUDTests extends GebReportingTest {
#Test
void doSomeCrud() {
to LoginPage
at LoginPage
$("form").j_username() << "admin"
$("form").j_password() << "XXXXX"
loginButton.click()
to AuthenticatedPage
at AuthenticatedPage
}
}
The AuthenticatedPage is a page which needs authentication, but in this moment it is imposible to be authenticated using geb. Does anybody know anything about this issue?
Thanks in advance!

Try rewriting doSomeCrud() as follows:
to LoginPage
loginForm.j_username = 'admin'
loginForm.j_password = 'XXXXX'
loginButton.click()
waitFor { at AuthenticatedPage }

I don't know if this will help you since you are not using Spock, but I had a few issues with login as well (can't remember exactly what though)
I eventually found this code that I put in a spec extending GebSpec.
This gets called before each of the tests requiring a login:
def setupSpec() {
Browser.drive {
go baseUrl
$("form.login").with {
username = System.getenv("APP_USERNAME")
password = System.getenv("APP_PASSWORD")
}
$("input[name='submit']").click()
}
}
It doesn't seem like much, but the use of 'with' on the form worked for me.

Related

Grails functional test Geb page object module not working No signature of method

This is the page object.
package myapp.pages
import geb.Page
class LoginPage extends Page {
static url = "http://localhost:8080/login/auth"
//static at = {title.contains("Login")}
static at = {
waitFor {title.contains("Login")} // Add waitFor here to verify on page
}
static content = {
loginForm { $( 'form') }
usernameField { $('form').userName }
passwordField { $('form').password }
submitButton { $('input#submit' )}
}
void loginSubmit(String email, String password) {
usernameField = "email#something.com"
assert $('form').username == "email#something.com"
passwordField = "secret"
assert $('form').password == "secret"
submitButton.click()
}
}
And this is the LoginSpec test file
package myapp.login
import geb.spock.GebSpec
import grails.testing.mixin.integration.Integration
import grails.transaction.*
import myapp.pages.LoginPage
#Integration
#Rollback
class LoginSpec extends GebSpec {
def setup() {
}
def cleanup() {
}
void "user successfully logs in, is redirected to homepage"() {
given:
to LoginPage
when:
LoginPage.loginSubmit("email#something.com", "secret")
then:
title.contains("Dashboard")
}
}
When i run this test, I get the following error:
groovy.lang.MissingMethodException: No signature of method: static myapp.pages.LoginPage.loginSubmit() is applicable for argument types: (java.lang.String, java.lang.String) values: [email#something.com.com, secret]
I basically get the same error when I hardcode the username and password into the login page loginsubmit function. The selectors are fine, when I use the same selectors directly in the LoginSpec test to set the username and password, the test passes. The issue only occurs when I try to use the page object.
Instead of this:
when:
LoginPage.loginSubmit("email#something.com", "secret")
Use this:
when:
loginSubmit("email#something.com", "secret")
The issue isn't really a Geb one. The JVM doesn't allow you to invoke an instance method on a class reference as the context necessary to carry out that invocation wouldn't exist. loginSubmit is an instance method, not a static method.
I hope that helps.
Geb remembers the current page and automatically dispatches method calls to the page, so you do not need to include the page class name: loginSubmit("email#something.com", "secret") in the test will call the method on the page.

Grails 4: Custom Domain Model Marshaller Not Working On Grails 4

My current problem right now is the custom object marshaller that I created since grails 2.4.5 to grails 3.3.0 is not working on grails 4.0.0. Grails 4 respond the domain model by default not the custom made I create.
Below are the codes I have. Please review and if you find theres something wrong please do let me know guys, I will be glad if you can help me with this.
ResponseSender.groovy
package com.problem.solve.common
import org.springframework.http.HttpStatus
trait ResponseSender {
void sendResponse() {
render status: HttpStatus.NO_CONTENT
}
void sendResponse(def responseData) {
respond (responseData)
}
void sendResponse(HttpStatus status, def responseData) {
response.status = status.value()
respond (responseData)
}
}
This ResponseSender.groovy trait is implemented on the controller.
MarshallerInitializer.groovy
package com.problem.solve.marshaller
class MarshallerInitializer {
CustomObjectMarshallers customObjectMarshallers
void initialize() {
customObjectMarshallers.register()
}
}
this MarshallerInitializer.groovy will be called when bootstrap initialize.
package com.problem.solve.marshaller
class CustomObjectMarshallers {
List marshallers = []
void register() {
marshallers.each {
it.register()
}
}
}
This CustomObjectMarshallers.groovy will going to register all marshallers.
UserMarshaller.groovy
package com.problem.solve.marshaller.marshalls
import com.problem.solve.security.User
import grails.converters.JSON
class UserMarshaller {
void register() {
JSON.registerObjectMarshaller(User) { User user ->
return [
id: user.id,
fullName: user.fullName,
username: user.username,
emailAddress: user.emailAddress,
roles: user.authorities.authority,
dateCreated: user.dateCreated,
lastUpdated: user.lastUpdated,
_entityType: 'User'
]
}
}
This UserMarshaller.groovy is a sample domain model that I want to convert from domain model to json response.
resources.groovy
import com.problem.solve.marshaller.CustomObjectMarshallers
import com.problem.solve.marshaller.MarshallerInitializer
import com.problem.solve.marshaller.marshalls.*
// Place your Spring DSL code here
beans = {
customObjectMarshallers(CustomObjectMarshallers) {
marshallers = [
new UserMarshaller()
]
}
marshallerInitializer(MarshallerInitializer) {
customObjectMarshallers = ref('customObjectMarshallers')
}
}
The problem with this setup is not working on grails 4, but this setup are working with grails 2.4.5 and grails 3.3.0.
I really need your help guys.
Thank you so much :)
I solve this marshaller problem by creating DomainModelResponseDto as respond domain model.
Example:
class UserResponseDto {
String id
String username
String email
UserResponseDto(User user) {
id = user.id
username = user.username
email = user.email
}
}
I have another solution to this problem. The marshaller is working the problem is the views. I have to delete the views that are generated when creating a domain class and all is working perfectly with the current marshaller and setup.

Grails 3.2.2 spring-security-ui override RegisterController not working

I am working on a Grails 3.2.2 Plugin in which I am trying to override the RegisterController from spring-security-ui as well as the register.gsp. I have used the s2ui-override command which has created me the required controller and at the moment I only added a println to see it working, but it seems to be still going to the original one. So far I have:
RegisterController
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UserDetails
import grails.plugin.springsecurity.ui.RegisterCommand
class RegisterController extends grails.plugin.springsecurity.ui.RegisterController {
static defaultAction = 'register'
UserDetailsService userDetailsService
#Override
def register(RegisterCommand registerCommand) {
println "IN MY CONTROLLER!!"
def registerReturn = super.register(registerCommand)
if (registerReturn?.emailSent) {
UserDetails createdUser = userDetailsService.loadUserByUsername(registerCommand.username)
println "New user ${createdUser}"
}
registerReturn
}
def otherAction() {
println "IN MY CONTROLLER!!"
println "userDetailsService = ${userDetailsService}"
"Hellloooo"
}
}
register.gsp same as the original, only added
<p style="color:red;"> HELLLOOOOOOO </p>
immediately after the body tag
UrlMappings
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
Result when running the app:
I can see my sample red text at the top, but cannot see the output in the console, although I am getting a warning when running the app. Console output when going to the "register" URL:
Running application...
WARNING: The [register] action accepts a parameter of type [grails.plugin.springsecurity.ui.RegisterCommand] which does not implement grails.validation.Validateable. Data binding will still be applied to this command object but the instance will not be validateable.
#Override
^
Configuring Spring Security Core ...
... finished configuring Spring Security Core
Configuring Spring Security UI ...
... finished configuring Spring Security UI
Configuring Spring Security REST 2.0.0.M2...
... finished configuring Spring Security REST
Grails application running at http://localhost:8060 in environment: development
Though when going to "register/otherAction" I am getting:
IN MY CONTROLLER
userDetailsService = grails.plugin.springsecurity.userdetails.GormUserDetailsService#10ed50b0
Interestingly, if I publish the plugin locally (my .m2 dir) and import it into an app, I am getting my printlns in the console, BUT my gsp is now not being used (I cannot see my red Hello text). Console output:
IN MY CONTROLLER!!
// unimportant mailing error
New user grails.plugin.springsecurity.userdetails.GrailsUser#364492: Username: test; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: false; Granted Authorities: ROLE_NO_ROLES
Anyone can help on where am I going wrong? I am happy to provide any other resources which may help.
So I have a fix (maybe a workaround):
RegisterController
class RegisterController extends grails.plugin.springsecurity.ui.RegisterController {
static namespace = 'my-great-plugin'
UserDetailsService userDetailsService
#Override
def register(RegisterCommand registerCommand) {
println "IN MY CONTROLLER!!"
def registerReturn = super.register(registerCommand)
println "Got return from super call ${registerReturn}"
if (registerReturn?.emailSent) {
UserDetails createdUser = userDetailsService.loadUserByUsername(registerCommand.username)
println "New user ${createdUser}"
}
registerReturn
}
def otherAction() {
println "IN MY CONTROLLER"
println "userDetailsService = ${userDetailsService}"
"Hellloooo"
}
MyPluginUrlMappings.groovy
static mappings = {
"/register/$action?/$id?(.$format)?" {
controller = 'register'
namespace = 'my-great-plugin'
}
}
MyPluginGrailsPlugn.groovy
def loadAfter = ['springSecurityCore', 'springSecurityUi', 'springSecurityRest']
And it works now!
I am still open to suggestions on how to handle this better

Geb testing of directly inserted link in address bar

I have an application developed in ggts with grails/groovy and there are users with different roles. I want to test if a user can access some parts of the application by directly inserting link into address bar. So, where is my problem? When I test my application on localhost, everything is OK. But when I want to test through http://shopapp then it isn't OK. The application is the same.
Here is my code:
LoginSpec.groovy
import geb.spock.GebReportingSpec
import spock.lang.*
import pages.*
#Stepwise
class LoginSpec extends GebReportingSpec {
def "opage"() {
when:
to LoginPage
loginUser()
acceptButton.click()
then:
to ProductPage
}
}
ProductPage.groovy
package pages
import geb.Browser
import grails.util.Holders
class ProductPage extends ScaffoldPage {
def config = org.codehaus.groovy.grails.commons.ConfigurationHolder.config
static url = {config.grails.serverURL + "/shop/catalog/show/123"}
static at = {
waitFor { title ==~ /Products/ }
title ==~ /Products/
}
static content = {
}
}
config.grails.serverURL should be http://shopapp.
When I start the test, it opens http://shopapp/login, the user logs in and the home page opens. But then it stays on home page. It should go to http://shopapp/shop/catalog/show/123 because I want to test if a user can access a product by directly inserting a link into address bar.
Please help, I don't have any more ideas.
Thanks.
The static url property should be a string, not a closure:
static url = config.grails.serverURL + "/shop/catalog/show/123"

How to use a custom codec in a Grails unit test

I am working through a Grails tutorial from InfoQ called Getting Started With Grails, Second Edition, and I am trying to add a custom codec to a unit test. My environment is Grails 1.3.7 and Groovy 1.7.8.
So the codec is SHACodec.groovy, and it lives in grails-app/utils. The contents are:
import java.security.MessageDigest
class SHACodec{
static encode = {target->
MessageDigest md = MessageDigest.getInstance('SHA')
md.update(target.getBytes('UTF-8'))
return new String(md.digest()).encodeAsBase64()
}
}
The codec works just fine when I log into the app. It's being used for a password field in my UserController.authenticate()
def authenticate = {
def user =
User.findByLoginAndPassword(params.login, params.password.encodeAsSHA())
if(user){
session.user = user
flash.message = "Hello ${user.login}!"
redirect(controller:"race", action:"list")
}else{
flash.message = "Sorry, ${params.login}. Please try again."
redirect(action:"login")
}
}
When I add this to a unit test, the following error displays:
No such property: SHACodec for class:
racetrack.UserControllerTests
groovy.lang.MissingPropertyException:
No such property: SHACodec for class:
racetrack.UserControllerTests at
racetrack.UserControllerTests.testAuthenticate(UserControllerTests.groovy:39)
The test is:
package racetrack
import org.codehaus.groovy.grails.plugins.codecs.*
import grails.test.*
class UserControllerTests extends ControllerUnitTestCase {
protected void setUp() {
super.setUp()
String.metaClass.encodeAsSHA = {->
SHACodec.encode(delegate)
}
}
protected void tearDown() {
super.tearDown()
}
void testAuthenticate(){
def jdoe = new User(login:"jdoe", password:"password".encodeAsSHA())
mockDomain(User, [jdoe])
controller.params.login = "jdoe"
controller.params.password = "password"
controller.authenticate()
assertNotNull controller.session.user
assertEquals "jdoe", controller.session.user.login
controller.params.password = "foo"
controller.authenticate()
assertTrue controller.flash.message.startsWith(
"Sorry, jdoe")
}
I found two different recommendations so far.
First, call the codec directly from the test:
SHACodec codec = new SHACodec()
codec.encode("password")
Second, use an integration test instead of a unit test.
The results were the same for each option. What is the small thing I am missing?
Thanks in advance for your time!
you are not importing your codec--you are importing the default grails codecs. You just need to make the import for the codec in the test match the exact package path of where the codec lives. If you are using an IDE, let it try to import the codec for you...
Use the loadCodec method of the GrailsUnitTestCase. It's very simple. See the example bellow:
import grails.test.*
import org.codehaus.groovy.grails.plugins.codecs.HTMLCodec
class SomeTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
loadCodec(HTMLCodec)
}
protected void tearDown() {
super.tearDown()
}
void testSomething() {
assertEquals "<p>Hello World</p>", "<p>Hello World</p>".encodeAsHTML()
}
}
The answer from #hvgotcodes is correct, but you also need to add the grails-app/utils folder to your Java Build Path before the import works (at least that's what I needed to do with SpringSource Eclipse IDE).

Resources