You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ma...@apache.org on 2026/04/17 11:00:03 UTC
(grails-spring-security) 06/06: test: update tests for Grails 8 and SB4
This is an automated email from the ASF dual-hosted git repository.
matrei pushed a commit to branch grails8-sb4
in repository https://gitbox.apache.org/repos/asf/grails-spring-security.git
commit 983e872c008c8958a5b2a7b723c488a6ed2c450b
Author: Mattias Reichel <ma...@gmail.com>
AuthorDate: Fri Apr 17 12:57:30 2026 +0200
test: update tests for Grails 8 and SB4
---
.../com/testacl/ErrorsController.groovy | 2 +-
.../groovy/pages/AccessDeniedPage.groovy | 8 ++
...owReportPage.groovy => DeleteReportPage.groovy} | 10 +-
.../groovy/pages/EditReportPage.groovy | 11 +-
.../groovy/pages/ListReportPage.groovy | 32 ++++-
.../integration-test/groovy/pages/LoginPage.groovy | 2 +-
.../groovy/pages/LogoutPage.groovy | 9 +-
.../groovy/pages/ReportGrantPage.groovy | 20 ++-
.../groovy/pages/ResetDataPage.groovy | 12 +-
.../groovy/pages/ScaffoldPage.groovy | 3 +-
.../groovy/pages/ShowReportPage.groovy | 8 +-
.../groovy/test/AbstractSecuritySpec.groovy | 15 +-
.../groovy/test/AdminFunctionalSpec.groovy | 105 +++++++-------
.../groovy/test/User1FunctionalSpec.groovy | 136 +++++++++---------
.../groovy/test/User2FunctionalSpec.groovy | 113 +++++++--------
.../TestClassAnnotatedServiceMetadataSpec.groovy} | 45 +++---
.../acl/TestClassAnnotatedServiceSpec.groovy | 21 ++-
...uredAnnotationSecurityMetadataSourceSpec.groovy | 58 ++++++++
.../grails-app/views/testRequestmap/edit.gsp | 5 +-
.../grails-app/views/testRequestmap/show.gsp | 4 +-
.../grails-app/views/testRole/create.gsp | 4 +-
.../grails-app/views/testRole/edit.gsp | 4 +-
.../grails-app/views/testRole/show.gsp | 4 +-
.../grails-app/views/testUser/edit.gsp | 4 +-
.../grails-app/views/testUser/show.gsp | 2 +-
.../groovy/pages/CreatePage.groovy | 2 +-
.../integration-test/groovy/pages/EditPage.groovy | 4 +-
.../groovy/pages/ScaffoldPage.groovy | 5 +-
.../integration-test/groovy/pages/ShowPage.groovy | 4 +-
.../pages/requestmap/CreateRequestmapPage.groovy | 7 +-
.../pages/requestmap/EditRequestmapPage.groovy | 5 +-
.../pages/requestmap/ListRequestmapPage.groovy | 8 +-
.../groovy/pages/role/CreateRolePage.groovy | 6 +-
.../groovy/pages/role/EditRolePage.groovy | 7 +-
.../groovy/pages/role/ListRolePage.groovy | 9 +-
.../groovy/pages/role/ShowRolePage.groovy | 6 +-
.../groovy/pages/user/CreateUserPage.groovy | 9 +-
.../groovy/pages/user/EditUserPage.groovy | 12 +-
.../groovy/pages/user/ListUserPage.groovy | 9 +-
.../groovy/specs/InheritanceSecuritySpec.groovy | 3 +
.../groovy/specs/RequestmapSpec.groovy | 98 ++++++-------
.../integration-test/groovy/specs/RoleSpec.groovy | 90 ++++++------
.../integration-test/groovy/specs/UserSpec.groovy | 105 +++++++-------
...AutoConfigurationExcluderIntegrationSpec.groovy | 22 ---
.../SecurityEventListenerSpec.groovy | 7 +-
.../springsecurity/SpringSecurityUtilsSpec.groovy | 8 +-
.../intercept/FilterSecurityInterceptorSpec.groovy | 71 ++++++++++
.../AjaxAwareAuthenticationEntryPointSpec.groovy | 18 +++
.../MethodSecurityInterceptorSpec.groovy | 147 +++++++++++++++++++
...FallbackMethodSecurityMetadataSourceSpec.groovy | 60 ++++++++
...ethodSecurityExpressionCompatibilitySpec.groovy | 155 +++++++++++++++++++++
...aultRestAuthenticationEventPublisherSpec.groovy | 18 +++
.../groovy/page/AbstractSecurityPage.groovy | 4 +
.../groovy/spec/RegisterSpec.groovy | 4 +-
.../groovy/page/AbstractSecurityPage.groovy | 4 +
.../groovy/page/register/RegisterPage.groovy | 1 -
.../groovy/spec/RegisterSpec.groovy | 70 ++++------
57 files changed, 1086 insertions(+), 529 deletions(-)
diff --git a/plugin-acl/examples/functional-test-app/grails-app/controllers/com/testacl/ErrorsController.groovy b/plugin-acl/examples/functional-test-app/grails-app/controllers/com/testacl/ErrorsController.groovy
index 235400160..bc54ee072 100644
--- a/plugin-acl/examples/functional-test-app/grails-app/controllers/com/testacl/ErrorsController.groovy
+++ b/plugin-acl/examples/functional-test-app/grails-app/controllers/com/testacl/ErrorsController.groovy
@@ -27,7 +27,7 @@ import groovy.transform.CompileStatic
class ErrorsController {
def error404() {
- String uri = 'request.forwardURI'
+ String uri = request.forwardURI ?: request.requestURI ?: 'unknown URI'
if (!uri.contains('favicon.ico')) {
println "\n\nERROR 404: could not find $uri\n\n"
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/AccessDeniedPage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/AccessDeniedPage.groovy
new file mode 100644
index 000000000..fcf38042a
--- /dev/null
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/AccessDeniedPage.groovy
@@ -0,0 +1,8 @@
+package pages
+
+import geb.Page
+
+class AccessDeniedPage extends Page {
+
+ static at = { $('h1').text() == 'Access Denied' }
+}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ShowReportPage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/DeleteReportPage.groovy
similarity index 84%
copy from plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ShowReportPage.groovy
copy to plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/DeleteReportPage.groovy
index f19bf4666..8d00b11da 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ShowReportPage.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/DeleteReportPage.groovy
@@ -19,13 +19,11 @@
package pages
-class ShowReportPage extends ScaffoldPage {
+class DeleteReportPage extends ScaffoldPage {
- static at = {
- heading.text() == 'Show Report'
- }
+ static url = '/report/delete'
- static content = {
- name { $('td#name').text() }
+ String convertToPath(Object[] args) {
+ args ? "?number=${args[0]}" : ''
}
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/EditReportPage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/EditReportPage.groovy
index 8cf01042b..4d3d374a9 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/EditReportPage.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/EditReportPage.groovy
@@ -19,13 +19,22 @@
package pages
+import geb.module.TextInput
+
class EditReportPage extends ScaffoldPage {
+ static url = '/report/edit'
+
static at = {
- heading.text() == 'Edit Report'
+ heading == 'Edit Report'
+ }
+
+ String convertToPath(Object[] args) {
+ args ? "?number=${args[0]}" : ''
}
static content = {
+ nameField { $('input', name: 'name').module(TextInput) }
updateButton { $('input', value: 'Update') }
}
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ListReportPage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ListReportPage.groovy
index d0f508eb9..b6e4327dd 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ListReportPage.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ListReportPage.groovy
@@ -23,24 +23,44 @@ import geb.Module
class ListReportPage extends ScaffoldPage {
- static url = 'report/list?max=1000'
+ static url = 'report/list'
static at = {
title ==~ /Report List/
}
+ String convertToPath(Object[] args) {
+ if (!args) {
+ return ''
+ }
+
+ def params = args[0] as Map
+ if (!params) {
+ return ''
+ }
+
+ if (!params.containsKey('max')) {
+ params.max = 1000
+ }
+
+ '?' + params.collect { key, value ->
+ "${URLEncoder.encode(key.toString(), 'UTF-8')}=" +
+ "${URLEncoder.encode(value?.toString() ?: '', 'UTF-8')}"
+ }.join('&')
+ }
static content = {
+ message { $('div.message').text() }
+ nextLink { $('.nextLink') }
reportTable { $('div.list table', 0) }
- reportRow { i -> module ReportRow, reportRows[i] }
- reportRows(required: false) { reportTable.find('tbody').find('tr') }
+ reportRows { reportTable.find('tbody tr').moduleList(ReportRow) }
}
}
class ReportRow extends Module {
static content = {
- cell { i -> $('td', i) }
- cellText { i -> cell(i).text() }
- cellHrefText{ i -> cell(i).find('a').text() }
+ cell { int i -> $('td', i) }
+ cellText { int i -> cell(i).text() }
+ cellHrefText { int i -> cell(i).find('a').text() }
name { cellText(1) }
showLink(to: ShowReportPage) { cell(0).find('a') }
grantLink(to: ReportGrantPage) { cell(2).find('a') }
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/LoginPage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/LoginPage.groovy
index 1120f3004..f40adf2fb 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/LoginPage.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/LoginPage.groovy
@@ -23,7 +23,7 @@ import geb.Page
class LoginPage extends Page {
- static url = 'login/auth'
+ static url = '/login/auth'
static at = { title == 'Login' }
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/CreatePage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/LogoutPage.groovy
similarity index 91%
copy from plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/CreatePage.groovy
copy to plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/LogoutPage.groovy
index 39b1b9479..0f3a90cf2 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/CreatePage.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/LogoutPage.groovy
@@ -19,8 +19,9 @@
package pages
-class CreatePage extends ScaffoldPage {
- static at = {
- title ==~ /Create.+/
- }
+import geb.Page
+
+class LogoutPage extends Page {
+
+ static url = '/logout'
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ReportGrantPage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ReportGrantPage.groovy
index c32c5e589..43ff38c53 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ReportGrantPage.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ReportGrantPage.groovy
@@ -19,13 +19,31 @@
package pages
+import geb.module.TextInput
+
+import org.springframework.security.acls.model.Permission
+
class ReportGrantPage extends ScaffoldPage {
+ static url = '/report/grant'
+
static at = {
- heading.text() ==~ /Grant permission for.+/
+ heading ==~ /Grant permission for.+/
+ }
+
+ String convertToPath(Object[] args) {
+ args ? "?number=${args[0]}" : ''
}
static content = {
grantButton { $('input', value: 'Grant') }
+ permissionInput { $('input', name: 'permission').module(TextInput) }
+ recipientInput { $('input', name: 'recipient').module(TextInput) }
+ }
+
+ void grantPermission(String recipient, Permission permission) {
+ recipientInput.value(recipient)
+ permissionInput.value(permission.mask)
+ grantButton.click()
}
}
diff --git a/plugin-ui/examples/simple/src/integration-test/groovy/page/AbstractSecurityPage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ResetDataPage.groovy
similarity index 86%
copy from plugin-ui/examples/simple/src/integration-test/groovy/page/AbstractSecurityPage.groovy
copy to plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ResetDataPage.groovy
index 66151831c..eb2125847 100644
--- a/plugin-ui/examples/simple/src/integration-test/groovy/page/AbstractSecurityPage.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ResetDataPage.groovy
@@ -17,13 +17,15 @@
* under the License.
*/
-package page
+package pages
import geb.Page
-abstract class AbstractSecurityPage extends Page {
+class ResetDataPage extends Page {
- void submit() {
- submitBtn.click()
- }
+ static url = '/testData/reset'
+
+ static at = {
+ $().text() == 'OK'
+ }
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ScaffoldPage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ScaffoldPage.groovy
index c402eedf1..4e6a5b492 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ScaffoldPage.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ScaffoldPage.groovy
@@ -24,7 +24,8 @@ import geb.Page
class ScaffoldPage extends Page {
static content = {
- heading { $('h1') }
+ h1 { $('h1', 0) }
+ heading { h1.text() }
message { $('div.message').text() }
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ShowReportPage.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ShowReportPage.groovy
index f19bf4666..76b747a98 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ShowReportPage.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/pages/ShowReportPage.groovy
@@ -21,8 +21,14 @@ package pages
class ShowReportPage extends ScaffoldPage {
+ static url = '/report/show'
+
static at = {
- heading.text() == 'Show Report'
+ heading == 'Show Report'
+ }
+
+ String convertToPath(Object[] args) {
+ args ? "?number=${args[0]}" : ''
}
static content = {
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AbstractSecuritySpec.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AbstractSecuritySpec.groovy
index b063da4ad..eeb6800f0 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AbstractSecuritySpec.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AbstractSecuritySpec.groovy
@@ -16,39 +16,42 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package test
+import pages.IndexPage
+import pages.ResetDataPage
+
import grails.gorm.transactions.Rollback
import grails.plugin.geb.ContainerGebSpec
-import grails.testing.mixin.integration.Integration
import pages.LoginPage
+import pages.LogoutPage
import spock.lang.Shared
@Rollback
-@Integration
abstract class AbstractSecuritySpec extends ContainerGebSpec {
@Shared boolean reset = false
void setup() {
if (!reset) {
- go('testData/reset')
+ to(ResetDataPage)
reset = true
}
logout()
}
protected void login(String user) {
- to(LoginPage).with {
+ via(LoginPage).with {
username = user
password = 'password'
loginButton.click()
}
+ at(IndexPage)
}
protected void logout() {
- go('logout')
+ via(LogoutPage)
+ at(IndexPage)
clearCookies()
}
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AdminFunctionalSpec.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AdminFunctionalSpec.groovy
index 7c3e813cc..d5792696a 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AdminFunctionalSpec.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AdminFunctionalSpec.groovy
@@ -16,20 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package test
-import grails.testing.mixin.integration.Integration
-import org.springframework.security.acls.domain.BasePermission
-
+import pages.DeleteReportPage
import pages.EditReportPage
import pages.IndexPage
import pages.ListReportPage
import pages.ReportGrantPage
import pages.ShowReportPage
-import spock.lang.Stepwise
+import spock.lang.Unroll
+
+import grails.testing.mixin.integration.Integration
+
+import static org.springframework.security.acls.domain.BasePermission.READ
-@Stepwise
@Integration
class AdminFunctionalSpec extends AbstractSecuritySpec {
@@ -41,54 +41,55 @@ class AdminFunctionalSpec extends AbstractSecuritySpec {
void 'check tags'() {
when:
- go('tagLibTest/test')
+ go('/tagLibTest/test')
then:
- pageSource.contains('test 1 true 1')
- pageSource.contains('test 2 true 1')
- pageSource.contains('test 3 true 1')
- pageSource.contains('test 4 true 1')
- pageSource.contains('test 5 true 1')
- pageSource.contains('test 6 true 1')
-
- pageSource.contains('test 1 true 13')
- pageSource.contains('test 2 true 13')
- pageSource.contains('test 3 true 13')
- pageSource.contains('test 4 true 13')
- pageSource.contains('test 5 true 13')
- pageSource.contains('test 6 true 13')
-
- pageSource.contains('test 1 true 80')
- pageSource.contains('test 2 true 80')
- pageSource.contains('test 3 true 80')
- pageSource.contains('test 4 true 80')
- pageSource.contains('test 5 true 80')
- pageSource.contains('test 6 true 80')
+ with(pageSource) {
+ contains('test 1 true 1')
+ contains('test 2 true 1')
+ contains('test 3 true 1')
+ contains('test 4 true 1')
+ contains('test 5 true 1')
+ contains('test 6 true 1')
+
+ contains('test 1 true 13')
+ contains('test 2 true 13')
+ contains('test 3 true 13')
+ contains('test 4 true 13')
+ contains('test 5 true 13')
+ contains('test 6 true 13')
+
+ contains('test 1 true 80')
+ contains('test 2 true 80')
+ contains('test 3 true 80')
+ contains('test 4 true 80')
+ contains('test 5 true 80')
+ contains('test 6 true 80')
+ }
}
+ @Unroll
void 'view all'() {
when:
- go("report/show?number=$i")
+ def page = to(ShowReportPage, i)
then:
- pageSource.contains("report$i")
+ page.name == "report$i"
where:
i << (1..100)
}
void 'edit report 15'() {
-
when:
- go('report/edit?number=15')
+ def page = to(EditReportPage, 15)
then:
- at(EditReportPage)
- $('form').name == 'report15'
+ page.nameField.text == 'report15'
when:
- name = 'report15_new'
- updateButton.click()
+ page.nameField = 'report15_new'
+ page.updateButton.click()
then:
at(ShowReportPage)
@@ -97,34 +98,33 @@ class AdminFunctionalSpec extends AbstractSecuritySpec {
void 'delete report 15'() {
when:
- go('report/delete?number=15')
- def listReportPage = at(ListReportPage)
+ via(DeleteReportPage, 15)
then:
- message == 'Report 15 deleted'
- listReportPage.reportRows.size() == 99
+ def page = at(ListReportPage)
+
+ and:
+ page.message == 'Report 15 deleted'
+ page.reportRows.size() == 99
}
void 'grant edit 16'() {
when:
- go('report/grant?number=16')
- def reportGrantPage = at(ReportGrantPage)
+ def page = to(ReportGrantPage, 16)
then:
- pageSource.contains('Grant permission for report16')
+ page.heading == 'Grant permission for report16'
when:
- recipient = 'user2'
- permission = BasePermission.READ.mask.toString()
- reportGrantPage.grantButton.click()
- at(ShowReportPage)
+ page.grantPermission('user2', READ)
+ page = at(ShowReportPage)
then:
- pageSource.contains("Permission $BasePermission.READ.mask granted on Report 16 to user2")
+ page.message == "Permission $READ.mask granted on Report 16 to user2"
// login as user2 and verify the grant
when:
- go('logout')
+ logout()
then:
at(IndexPage)
@@ -132,13 +132,10 @@ class AdminFunctionalSpec extends AbstractSecuritySpec {
when:
login('user2')
- then:
- at(IndexPage)
-
- when:
- go('report/show?number=16')
+ and:
+ page = to(ShowReportPage, 16)
then:
- pageSource.contains('report16')
+ page.name == 'report16'
}
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/User1FunctionalSpec.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/User1FunctionalSpec.groovy
index 73a67aaa3..c62b204ad 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/User1FunctionalSpec.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/User1FunctionalSpec.groovy
@@ -16,19 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package test
-import grails.testing.mixin.integration.Integration
-import org.springframework.security.acls.domain.BasePermission
-
+import pages.AccessDeniedPage
+import pages.DeleteReportPage
import pages.EditReportPage
import pages.ListReportPage
import pages.ReportGrantPage
import pages.ShowReportPage
-import spock.lang.Stepwise
+import spock.lang.Unroll
+
+import grails.testing.mixin.integration.Integration
+
+import static org.springframework.security.acls.domain.BasePermission.READ
+import static org.springframework.security.acls.domain.BasePermission.WRITE
-@Stepwise
@Integration
class User1FunctionalSpec extends AbstractSecuritySpec {
@@ -40,144 +42,136 @@ class User1FunctionalSpec extends AbstractSecuritySpec {
void 'check tags'() {
when:
- go('tagLibTest/test')
+ go('/tagLibTest/test')
then:
- pageSource.contains('test 1 true 1')
- pageSource.contains('test 2 true 1')
- pageSource.contains('test 3 true 1')
- pageSource.contains('test 4 true 1')
- pageSource.contains('test 5 true 1')
- pageSource.contains('test 6 true 1')
-
- pageSource.contains('test 1 true 13')
- pageSource.contains('test 2 true 13')
- pageSource.contains('test 3 true 13')
- pageSource.contains('test 4 true 13')
- pageSource.contains('test 5 true 13')
- pageSource.contains('test 6 true 13')
-
- pageSource.contains('test 1 false 80')
- pageSource.contains('test 2 false 80')
- pageSource.contains('test 3 false 80')
- pageSource.contains('test 4 false 80')
- pageSource.contains('test 5 false 80')
- pageSource.contains('test 6 false 80')
+ with(pageSource) {
+ contains('test 1 true 1')
+ contains('test 2 true 1')
+ contains('test 3 true 1')
+ contains('test 4 true 1')
+ contains('test 5 true 1')
+ contains('test 6 true 1')
+
+ contains('test 1 true 13')
+ contains('test 2 true 13')
+ contains('test 3 true 13')
+ contains('test 4 true 13')
+ contains('test 5 true 13')
+ contains('test 6 true 13')
+
+ contains('test 1 false 80')
+ contains('test 2 false 80')
+ contains('test 3 false 80')
+ contains('test 4 false 80')
+ contains('test 5 false 80')
+ contains('test 6 false 80')
+ }
}
+ @Unroll
void 'view all (1-67)'() {
when:
- go("report/show?number=$i")
+ def page = to(ShowReportPage, i)
then:
- pageSource.contains("report$i")
+ page.name == "report$i"
where:
i << (1..67)
}
+ @Unroll
void 'view all (68-100)'() {
when:
- go("report/show?number=$i")
+ via(ShowReportPage, i)
then:
- pageSource.contains('Access Denied')
+ at(AccessDeniedPage)
where:
- i << (68..100)
+ i << (68..69)
}
void 'edit report 11'() {
when:
- go('report/edit?number=11')
- def editPage = at(EditReportPage)
+ def page = to(EditReportPage, 11)
then:
- $('form').name == 'report11'
+ page.nameField.text == 'report11'
when:
- name = 'report11_new'
- editPage.updateButton.click()
+ page.nameField.text = 'report11_new'
+ page.updateButton.click()
+ page = at(ShowReportPage)
then:
- at(ShowReportPage)
- pageSource.contains('report11_new')
+ page.name == 'report11_new'
}
void 'delete report 11'() {
when:
- go('report/delete?number=11')
- def listPage = at(ListReportPage)
+ via(DeleteReportPage, 11)
+ def page = at(ListReportPage)
then:
- message == 'Report 11 deleted'
- listPage.reportRows.size() == 66
+ page.message == 'Report 11 deleted'
+ page.reportRows.size() == 66
}
void 'grant edit 12'() {
when:
- go('report/grant?number=12')
- def grantPage = at(ReportGrantPage)
+ def page = to(ReportGrantPage, 12)
then:
- pageSource.contains('Grant permission for report12')
+ page.heading == 'Grant permission for report12'
when:
- recipient = 'user2'
- permission = BasePermission.READ.mask.toString()
- grantPage.grantButton.click()
+ page.grantPermission('user2', READ)
then:
- at(ShowReportPage)
- pageSource.contains("Permission $BasePermission.READ.mask granted on Report 12 to user2")
+ page.message == "Permission $READ.mask granted on Report 12 to user2"
when:
- go('report/grant?number=12')
- grantPage = at(ReportGrantPage)
+ to(ReportGrantPage, 12)
then:
- pageSource.contains('Grant permission for report12')
+ page.heading == 'Grant permission for report12'
when:
- recipient = 'user2'
- permission = BasePermission.WRITE.mask.toString()
- grantPage.grantButton.click()
+ page.grantPermission('user2', WRITE)
then:
at(ShowReportPage)
- pageSource.contains("Permission $BasePermission.WRITE.mask granted on Report 12 to user2")
+ page.message == "Permission $WRITE.mask granted on Report 12 to user2"
}
void 'grant edit 13'() {
when:
- go('report/grant?number=13')
- def grantPage = at(ReportGrantPage)
+ def page = to(ReportGrantPage, 13)
then:
- pageSource.contains('Grant permission for report13')
+ page.heading == 'Grant permission for report13'
when:
- recipient = 'user2'
- permission = BasePermission.WRITE.mask.toString()
- grantPage.grantButton.click()
+ page.grantPermission('user2', WRITE)
then:
- pageSource.contains('Access Denied')
+ at(AccessDeniedPage)
}
void 'edit report 20'() {
when:
- go('report/edit?number=20')
- def editPage = at(EditReportPage)
+ def page = to(EditReportPage, 20)
then:
- $('form').name == 'report20'
+ page.nameField.text == 'report20'
when:
- name = 'report20_new'
- editPage.updateButton.click()
+ page.nameField.text = 'report20_new'
+ page.updateButton.click()
then:
- pageSource.contains('Access Denied')
+ at(AccessDeniedPage)
}
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/User2FunctionalSpec.groovy b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/User2FunctionalSpec.groovy
index bda6c2330..c2803dcaa 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/User2FunctionalSpec.groovy
+++ b/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/User2FunctionalSpec.groovy
@@ -16,17 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package test
-import grails.testing.mixin.integration.Integration
-import org.springframework.security.acls.domain.BasePermission
+import pages.AccessDeniedPage
+import pages.DeleteReportPage
import pages.EditReportPage
+import pages.ListReportPage
import pages.ReportGrantPage
import pages.ShowReportPage
-import spock.lang.Stepwise
+import spock.lang.Unroll
+
+import grails.testing.mixin.integration.Integration
+
+import static org.springframework.security.acls.domain.BasePermission.WRITE
-@Stepwise
@Integration
class User2FunctionalSpec extends AbstractSecuritySpec {
@@ -36,120 +39,118 @@ class User2FunctionalSpec extends AbstractSecuritySpec {
login('user2')
}
+ @Unroll
void 'view all (1-5)'() {
when:
- go("report/show?number=$i")
+ def page = to(ShowReportPage, 1)
then:
- pageSource.contains("report$i")
+ page.name == 'report1'
where:
i << (1..5)
}
+ @Unroll
void 'view all (6-100)'() {
when:
- go("report/show?number=$i")
+ via(ShowReportPage, i)
then:
- pageSource.contains('Access Denied')
+ at(AccessDeniedPage)
where:
i << (6..100)
}
void 'edit report 11'() {
-
when:
- go('report/edit?number=11')
+ via(EditReportPage, 11)
then:
- pageSource.contains('Access Denied')
+ at(AccessDeniedPage)
}
void 'delete report 1'() {
when:
- go('report/delete?number=1')
+ via(DeleteReportPage, 1)
then:
- pageSource.contains('Access Denied')
+ at(AccessDeniedPage)
}
void 'grant edit 2'() {
when:
- go('report/grant?number=2')
- def grantPage = at(ReportGrantPage)
+ def page = to(ReportGrantPage, 2)
then:
- pageSource.contains('Grant permission for report2')
+ page.heading == 'Grant permission for report2'
when:
- recipient = 'user1'
- permission = BasePermission.WRITE.mask.toString()
- grantPage.grantButton.click()
+ page.grantPermission('user1', WRITE)
then:
- pageSource.contains('Access Denied')
+ at(AccessDeniedPage)
}
void 'edit report 5'() {
when:
- go('report/edit?number=5')
- def editPage = at(EditReportPage)
+ def page = to(EditReportPage, 5)
then:
- $('form').name == 'report5'
+ page.nameField.text == 'report5'
when:
- name = 'report5_new'
- editPage.updateButton.click()
+ page.nameField.text = 'report5_new'
+ page.updateButton.click()
+ page = at(ShowReportPage)
then:
- at(ShowReportPage)
- pageSource.contains('report5_new')
+ page.name == 'report5_new'
}
void 'list is filtered'() {
-
when:
- go('report/list')
+ def page = to(ListReportPage)
then:
- pageSource.contains('report1')
- !pageSource.contains('report6')
+ page.reportRows[0].name == 'report1'
+ page.reportRows.every {it.name != 'report6' }
when:
- go('report/list?offset=80&max=10')
+ to(ListReportPage, [offset: 80, max: 10])
then:
- pageSource.contains('Next')
- !pageSource.contains('report85')
+ page.nextLink.displayed
+ page.reportRows.every {it.name != 'report85' }
}
void 'check tags'() {
when:
- go('tagLibTest/test')
+ go('/tagLibTest/test')
then:
- pageSource.contains('test 1 true 1')
- pageSource.contains('test 2 true 1')
- pageSource.contains('test 3 true 1')
- pageSource.contains('test 4 true 1')
- pageSource.contains('test 5 true 1')
- pageSource.contains('test 6 true 1')
-
- pageSource.contains('test 1 false 13')
- pageSource.contains('test 2 false 13')
- pageSource.contains('test 3 false 13')
- pageSource.contains('test 4 false 13')
- pageSource.contains('test 5 false 13')
- pageSource.contains('test 6 false 13')
-
- pageSource.contains('test 1 false 80')
- pageSource.contains('test 2 false 80')
- pageSource.contains('test 3 false 80')
- pageSource.contains('test 4 false 80')
- pageSource.contains('test 5 false 80')
- pageSource.contains('test 6 false 80')
+ with(pageSource) {
+ contains('test 1 true 1')
+ contains('test 2 true 1')
+ contains('test 3 true 1')
+ contains('test 4 true 1')
+ contains('test 5 true 1')
+ contains('test 6 true 1')
+
+ contains('test 1 false 13')
+ contains('test 2 false 13')
+ contains('test 3 false 13')
+ contains('test 4 false 13')
+ contains('test 5 false 13')
+ contains('test 6 false 13')
+
+ contains('test 1 false 80')
+ contains('test 2 false 80')
+ contains('test 3 false 80')
+ contains('test 4 false 80')
+ contains('test 5 false 80')
+ contains('test 6 false 80')
+ }
}
}
diff --git a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AbstractSecuritySpec.groovy b/plugin-acl/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/acl/TestClassAnnotatedServiceMetadataSpec.groovy
similarity index 55%
copy from plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AbstractSecuritySpec.groovy
copy to plugin-acl/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/acl/TestClassAnnotatedServiceMetadataSpec.groovy
index b063da4ad..ec5b13e4b 100644
--- a/plugin-acl/examples/functional-test-app/src/integration-test/groovy/test/AbstractSecuritySpec.groovy
+++ b/plugin-acl/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/acl/TestClassAnnotatedServiceMetadataSpec.groovy
@@ -16,39 +16,30 @@
* specific language governing permissions and limitations
* under the License.
*/
+package grails.plugin.springsecurity.acl
-package test
-
-import grails.gorm.transactions.Rollback
-import grails.plugin.geb.ContainerGebSpec
+import grails.util.Holders
import grails.testing.mixin.integration.Integration
-import pages.LoginPage
-import spock.lang.Shared
+import grails.gorm.transactions.Rollback
+import org.springframework.security.access.method.MethodSecurityMetadataSource
-@Rollback
@Integration
-abstract class AbstractSecuritySpec extends ContainerGebSpec {
+@Rollback
+class TestClassAnnotatedServiceMetadataSpec extends AbstractIntegrationSpec {
- @Shared boolean reset = false
+ void 'userAnnotated metadata resolves from the live proxied bean'() {
+ given:
+ def applicationContext = Holders.grailsApplication.mainContext
+ MethodSecurityMetadataSource aclSecurityMetadataSource = applicationContext.getBean('aclSecurityMetadataSource', MethodSecurityMetadataSource)
+ def beanType = applicationContext.getType('testClassAnnotatedService')
+ def beanTypeMethod = beanType.getMethod('userAnnotated')
- void setup() {
- if (!reset) {
- go('testData/reset')
- reset = true
- }
- logout()
+ expect:
+ aclSecurityMetadataSource.getAttributes(beanTypeMethod, beanType)*.attribute == ['ROLE_USER']
}
+}
+
+
+
- protected void login(String user) {
- to(LoginPage).with {
- username = user
- password = 'password'
- loginButton.click()
- }
- }
- protected void logout() {
- go('logout')
- clearCookies()
- }
-}
diff --git a/plugin-acl/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/acl/TestClassAnnotatedServiceSpec.groovy b/plugin-acl/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/acl/TestClassAnnotatedServiceSpec.groovy
index 4fa12c111..ac0d6ee5d 100644
--- a/plugin-acl/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/acl/TestClassAnnotatedServiceSpec.groovy
+++ b/plugin-acl/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/acl/TestClassAnnotatedServiceSpec.groovy
@@ -56,10 +56,10 @@ class TestClassAnnotatedServiceSpec extends AbstractAclSpec {
testClassAnnotatedService.notAnnotated()
then:
- notThrown()
+ noExceptionThrown()
}
- void 'check that the userAnnotated method overides the class annotation and requires ROLE_USER'() {
+ void 'check that the userAnnotated method overides the class annotation and requires authentication'() {
given:
buildReports()
@@ -69,18 +69,29 @@ class TestClassAnnotatedServiceSpec extends AbstractAclSpec {
then:
thrown AuthenticationCredentialsNotFoundException
- when:
+ }
+
+ void 'check that the userAnnotated method overrides the class annotation and denies ROLE_ADMIN'() {
+ given:
+ buildReports()
authenticateAsAdmin()
+
+ when:
testClassAnnotatedService.userAnnotated()
then:
thrown AccessDeniedException
+ }
- when:
+ void 'check that the userAnnotated method overrides the class annotation and allows ROLE_USER'() {
+ given:
+ buildReports()
authenticateAsUser()
+
+ when:
testClassAnnotatedService.userAnnotated()
then:
- notThrown()
+ noExceptionThrown()
}
}
diff --git a/plugin-acl/plugin/src/test/groovy/grails/plugin/springsecurity/acl/access/method/SecuredAnnotationSecurityMetadataSourceSpec.groovy b/plugin-acl/plugin/src/test/groovy/grails/plugin/springsecurity/acl/access/method/SecuredAnnotationSecurityMetadataSourceSpec.groovy
new file mode 100644
index 000000000..0b928ff66
--- /dev/null
+++ b/plugin-acl/plugin/src/test/groovy/grails/plugin/springsecurity/acl/access/method/SecuredAnnotationSecurityMetadataSourceSpec.groovy
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package grails.plugin.springsecurity.acl.access.method
+
+import grails.plugin.springsecurity.annotation.Secured
+import spock.lang.Specification
+
+class SecuredAnnotationSecurityMetadataSourceSpec extends Specification {
+
+ private SecuredAnnotationSecurityMetadataSource metadataSource = new SecuredAnnotationSecurityMetadataSource(
+ serviceClassNames: [AnnotatedService.name]
+ )
+
+ void 'getAttributes resolves method annotations for proxied or subclassed service targets'() {
+ given:
+ def method = ProxiedAnnotatedService.getMethod('userAnnotated')
+
+ expect:
+ metadataSource.getAttributes(method, ProxiedAnnotatedService)*.attribute == ['ROLE_USER']
+ }
+
+ void 'getAttributes falls back to inherited class annotations for proxied or subclassed service targets'() {
+ given:
+ def method = ProxiedAnnotatedService.getMethod('notAnnotated')
+
+ expect:
+ metadataSource.getAttributes(method, ProxiedAnnotatedService)*.attribute == ['ROLE_ADMIN']
+ }
+
+ @Secured('ROLE_ADMIN')
+ private static class AnnotatedService {
+
+ void notAnnotated() {}
+
+ @Secured('ROLE_USER')
+ void userAnnotated() {}
+ }
+
+ private static class ProxiedAnnotatedService extends AnnotatedService {
+ }
+}
+
diff --git a/plugin-core/examples/functional-test-app/grails-app/views/testRequestmap/edit.gsp b/plugin-core/examples/functional-test-app/grails-app/views/testRequestmap/edit.gsp
index 31c5a9948..1c0c4f0e3 100644
--- a/plugin-core/examples/functional-test-app/grails-app/views/testRequestmap/edit.gsp
+++ b/plugin-core/examples/functional-test-app/grails-app/views/testRequestmap/edit.gsp
@@ -40,13 +40,14 @@
</g:eachError>
</ul>
</g:hasErrors>
- <g:form url="[resource:testRequestmap, action:'update']" method="PUT" >
+ <g:form url="[resource: testRequestmap, action: 'update']" method="PUT">
+ <g:hiddenField name="id" value="${testRequestmap?.id}" />
<g:hiddenField name="version" value="${testRequestmap?.version}" />
<fieldset class="form">
<g:render template="form"/>
</fieldset>
<fieldset class="buttons">
- <g:actionSubmit class="save" action="update" value='Update' />
+ <input type="submit" class="save" value='Update'>
</fieldset>
</g:form>
</div>
diff --git a/plugin-core/examples/functional-test-app/grails-app/views/testRequestmap/show.gsp b/plugin-core/examples/functional-test-app/grails-app/views/testRequestmap/show.gsp
index 80c23995e..8bd1ce5fd 100644
--- a/plugin-core/examples/functional-test-app/grails-app/views/testRequestmap/show.gsp
+++ b/plugin-core/examples/functional-test-app/grails-app/views/testRequestmap/show.gsp
@@ -53,10 +53,10 @@
</li>
</g:if>
</ol>
- <g:form url="[resource:testRequestmap, action:'delete']" method="DELETE">
+ <g:form url="[resource: testRequestmap, action: 'delete']" method="DELETE" onsubmit="return confirm('Are you sure?');">
<fieldset class="buttons">
<g:link class="edit" action="edit" resource="${testRequestmap}">Edit</g:link>
- <g:actionSubmit class="delete" action="delete" value='Delete' onclick="return confirm('Are you sure?');" />
+ <input type="submit" class="delete" value="Delete">
</fieldset>
</g:form>
</div>
diff --git a/plugin-core/examples/functional-test-app/grails-app/views/testRole/create.gsp b/plugin-core/examples/functional-test-app/grails-app/views/testRole/create.gsp
index 101eafad1..57fe9cb30 100644
--- a/plugin-core/examples/functional-test-app/grails-app/views/testRole/create.gsp
+++ b/plugin-core/examples/functional-test-app/grails-app/views/testRole/create.gsp
@@ -39,12 +39,12 @@
</g:eachError>
</ul>
</g:hasErrors>
- <g:form url="[resource:testRole, action:'save']" >
+ <g:form url="[resource: testRole, action: 'save']">
<fieldset class="form">
<g:render template="form"/>
</fieldset>
<fieldset class="buttons">
- <g:submitButton name="create" class="save" value='Create' />
+ <input type="submit" name="create" class="save" value="Create">
</fieldset>
</g:form>
</div>
diff --git a/plugin-core/examples/functional-test-app/grails-app/views/testRole/edit.gsp b/plugin-core/examples/functional-test-app/grails-app/views/testRole/edit.gsp
index 7c514f2d1..ff434df59 100644
--- a/plugin-core/examples/functional-test-app/grails-app/views/testRole/edit.gsp
+++ b/plugin-core/examples/functional-test-app/grails-app/views/testRole/edit.gsp
@@ -40,13 +40,13 @@
</g:eachError>
</ul>
</g:hasErrors>
- <g:form url="[resource:testRole, action:'update']" method="PUT" >
+ <g:form url="[resource: testRole, action: 'update']" method="PUT">
<g:hiddenField name="version" value="${testRole?.version}" />
<fieldset class="form">
<g:render template="form"/>
</fieldset>
<fieldset class="buttons">
- <g:actionSubmit class="save" action="update" value='Update' />
+ <input type="submit" class="save" value="Update">
</fieldset>
</g:form>
</div>
diff --git a/plugin-core/examples/functional-test-app/grails-app/views/testRole/show.gsp b/plugin-core/examples/functional-test-app/grails-app/views/testRole/show.gsp
index aab053524..35760c803 100644
--- a/plugin-core/examples/functional-test-app/grails-app/views/testRole/show.gsp
+++ b/plugin-core/examples/functional-test-app/grails-app/views/testRole/show.gsp
@@ -41,10 +41,10 @@
</li>
</g:if>
</ol>
- <g:form url="[resource:testRole, action:'delete']" method="DELETE">
+ <g:form url="[resource: testRole, action: 'delete']" method="DELETE" onsubmit="return confirm('Are you sure?');">
<fieldset class="buttons">
<g:link class="edit" action="edit" resource="${testRole}">Edit</g:link>
- <g:actionSubmit class="delete" action="delete" value='Delete' onclick="return confirm('Are you sure?');" />
+ <input type="submit" class="delete" value="Delete">
</fieldset>
</g:form>
</div>
diff --git a/plugin-core/examples/functional-test-app/grails-app/views/testUser/edit.gsp b/plugin-core/examples/functional-test-app/grails-app/views/testUser/edit.gsp
index 1667561b3..86f1a38d8 100644
--- a/plugin-core/examples/functional-test-app/grails-app/views/testUser/edit.gsp
+++ b/plugin-core/examples/functional-test-app/grails-app/views/testUser/edit.gsp
@@ -94,8 +94,8 @@
</div>
<div class="buttons">
- <span class="button"><g:actionSubmit class="save" action="update" value='Update' /></span>
- <span class="button"><g:actionSubmit class="delete" action="delete" value='Delete' onclick="return confirm('Are you sure?');" /></span>
+ <span class="button"><g:formActionSubmit action="update" method="PUT" class="save" value='Update' /></span>
+ <span class="button"><g:formActionSubmit action="delete" method="DELETE" class="delete" value='Delete' onclick="return confirm('Are you sure?');" /></span>
</div>
</g:form>
</div>
diff --git a/plugin-core/examples/functional-test-app/grails-app/views/testUser/show.gsp b/plugin-core/examples/functional-test-app/grails-app/views/testUser/show.gsp
index a05f70aef..b6d44808b 100644
--- a/plugin-core/examples/functional-test-app/grails-app/views/testUser/show.gsp
+++ b/plugin-core/examples/functional-test-app/grails-app/views/testUser/show.gsp
@@ -75,7 +75,7 @@
<fieldset class="buttons">
<g:hiddenField name="id" value="${person?.id}" />
<g:link class="edit" action="edit" id="${person?.id}">Edit</g:link>
- <g:actionSubmit class="delete" action="delete" value='Delete' onclick="return confirm('Are you sure?');" />
+ <g:formActionSubmit action="delete" method="DELETE" class="delete" value='Delete' onclick="return confirm('Are you sure?');" />
</fieldset>
</g:form>
</div>
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/CreatePage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/CreatePage.groovy
index 39b1b9479..e1c36a1db 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/CreatePage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/CreatePage.groovy
@@ -16,10 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages
class CreatePage extends ScaffoldPage {
+
static at = {
title ==~ /Create.+/
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/EditPage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/EditPage.groovy
index 8863422b3..293bd4d4d 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/EditPage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/EditPage.groovy
@@ -16,11 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages
class EditPage extends ScaffoldPage {
+
static at = {
- heading.text() ==~ /Edit.+/
+ heading ==~ /Edit.+/
}
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/ScaffoldPage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/ScaffoldPage.groovy
index 4b04eba1b..e30d64e6f 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/ScaffoldPage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/ScaffoldPage.groovy
@@ -16,14 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages
import geb.Page
class ScaffoldPage extends Page {
+
static content = {
- heading { $('h1') }
+ h1 { $('h1') }
+ heading { h1.text() }
message { $('div.message').text() }
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/ShowPage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/ShowPage.groovy
index c6b96296a..db55d0487 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/ShowPage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/ShowPage.groovy
@@ -16,11 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages
class ShowPage extends ScaffoldPage {
+
static at = {
- heading.text() ==~ /Show .+/
+ heading ==~ /Show .+/
}
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/CreateRequestmapPage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/CreateRequestmapPage.groovy
index f6025b8f6..39f21ae1d 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/CreateRequestmapPage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/CreateRequestmapPage.groovy
@@ -16,13 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages.requestmap
+import geb.module.TextInput
import pages.CreatePage
class CreateRequestmapPage extends CreatePage {
+
static content = {
- createButton(to: ShowRequestmapPage) { create() }
+ urlField { $('input', name: 'url').module(TextInput) }
+ configAttributeField { $('input', name: 'configAttribute').module(TextInput) }
+ createButton(to: ShowRequestmapPage) { $('input', type: 'submit') }
}
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/EditRequestmapPage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/EditRequestmapPage.groovy
index c8d81fd1d..55a6a6070 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/EditRequestmapPage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/EditRequestmapPage.groovy
@@ -16,13 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages.requestmap
+import geb.module.TextInput
import pages.EditPage
class EditRequestmapPage extends EditPage {
+
static content = {
+ urlField { $('input', name: 'url').module(TextInput) }
+ configAttributeField { $('input', name: 'configAttribute').module(TextInput) }
updateButton(to: ShowRequestmapPage) { $('input', value: 'Update') }
deleteButton(to: ListRequestmapPage) { $('input', value: 'Delete') }
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/ListRequestmapPage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/ListRequestmapPage.groovy
index ba4404ad8..d8d4ca8b2 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/ListRequestmapPage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/requestmap/ListRequestmapPage.groovy
@@ -34,15 +34,15 @@ class ListRequestmapPage extends ScaffoldPage {
newRequestmapButton(to: CreateRequestmapPage) { $('a', text: 'New TestRequestmap') }
requestmapTable { $('div.content table', 0) }
requestmapRows(required: false) { requestmapTable.find('tbody').find('tr') }
- requestmapRow { i -> requestmapRows[i].module(RequestmapRow) }
+ requestmapRow { int i -> requestmapRows[i].module(RequestmapRow) }
}
}
class RequestmapRow extends Module {
static content = {
- cell { i -> $('td', i) }
- cellText { i -> cell(i).text() }
- cellHrefText{ i -> cell(i).find('a').text() }
+ cell { int i -> $('td', i) }
+ cellText { int i -> cell(i).text() }
+ cellHrefText { int i -> cell(i).find('a').text() }
configAttribute { cellText(1) }
showLink(to: ShowRequestmapPage) { cell(0).find('a') }
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/CreateRolePage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/CreateRolePage.groovy
index 01ae34603..1ecedaab1 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/CreateRolePage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/CreateRolePage.groovy
@@ -16,13 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages.role
+import geb.module.TextInput
import pages.CreatePage
class CreateRolePage extends CreatePage {
+
static content = {
- createButton(to: ShowRolePage) { create() }
+ authorityField { $('input', name: 'authority').module(TextInput) }
+ createButton(to: ShowRolePage) { $('input', name: 'create') }
}
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/EditRolePage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/EditRolePage.groovy
index 641ec8b00..b5b1029ea 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/EditRolePage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/EditRolePage.groovy
@@ -16,14 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages.role
+import geb.module.TextInput
import pages.EditPage
class EditRolePage extends EditPage {
+
static content = {
- updateButton(to: ShowRolePage) { $('input', value: 'Update') }
- deleteButton(to: ListRolePage) { $('input', value: 'Delete') }
+ authorityField { $('input', name: 'authority').module(TextInput) }
+ updateButton { $('input', value: 'Update') }
}
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/ListRolePage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/ListRolePage.groovy
index cb525ce10..ca2272d24 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/ListRolePage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/ListRolePage.groovy
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages.role
import geb.Module
@@ -33,16 +32,16 @@ class ListRolePage extends ScaffoldPage {
static content = {
newRoleButton(to: CreateRolePage) { $('a', text: 'New TestRole') }
roleTable { $('div.content table', 0) }
- roleRow { i -> roleRows[i].module RoleRow }
+ roleRow { int i -> roleRows[i].module(RoleRow) }
roleRows(required: false) { roleTable.find('tbody').find('tr') }
}
}
class RoleRow extends Module {
static content = {
- cell { i -> $('td', i) }
- cellText { i -> cell(i).text() }
- cellHrefText{ i -> cell(i).find('a').text() }
+ cell { int i -> $('td', i) }
+ cellText { int i -> cell(i).text() }
+ cellHrefText { int i -> cell(i).find('a').text() }
authority { cellText(0) }
showLink(to: ShowRolePage) { cell(0).find('a') }
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/ShowRolePage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/ShowRolePage.groovy
index 468a662d3..1cfdd7c66 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/ShowRolePage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/role/ShowRolePage.groovy
@@ -16,15 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages.role
import pages.ShowPage
class ShowRolePage extends ShowPage {
+
static content = {
- editButton(to: EditRolePage) { $('a', text: 'Edit') }
- deleteButton(to: ListRolePage) { $('input', value: 'Delete') }
+ editButton { $('a', text: 'Edit') }
+ deleteButton { $('input', value: 'Delete') }
row { String text -> $('li.fieldcontain span.property-label', text: text).parent() }
value { String text -> row(text).find('span.property-value').text() }
authority { value('Authority') }
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/CreateUserPage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/CreateUserPage.groovy
index 136e5178a..ee7b0caf1 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/CreateUserPage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/CreateUserPage.groovy
@@ -19,10 +19,17 @@
package pages.user
+import geb.module.Checkbox
+import geb.module.PasswordInput
+import geb.module.TextInput
import pages.CreatePage
class CreateUserPage extends CreatePage {
+
static content = {
- createButton(to: ShowUserPage) { create() }
+ usernameField { $('input', name: 'username').module(TextInput) }
+ passwordField { $('input', name: 'password').module(PasswordInput) }
+ enabledCheckbox { $('input', id: 'enabled').module(Checkbox) }
+ createButton(to: ShowUserPage) { $('input', name: 'create') }
}
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/EditUserPage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/EditUserPage.groovy
index 3fcd83ac7..54554f7ec 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/EditUserPage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/EditUserPage.groovy
@@ -16,14 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages.user
+import geb.module.Checkbox
+import geb.module.PasswordInput
+import geb.module.TextInput
import pages.EditPage
class EditUserPage extends EditPage {
+
static content = {
- updateButton(to: ShowUserPage) { $('input', value: 'Update') }
- deleteButton(to: ListUserPage) { $('input', value: 'Delete') }
+ usernameField { $('input', name: 'username').module(TextInput) }
+ passwordField { $('input', name: 'password').module(PasswordInput) }
+ enabledCheckbox { $('input', id: 'enabled').module(Checkbox) }
+ updateButton { $('input', value: 'Update') }
+ deleteButton { $('input', value: 'Delete') }
}
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/ListUserPage.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/ListUserPage.groovy
index 020d35cb0..3c78f9c26 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/ListUserPage.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/pages/user/ListUserPage.groovy
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package pages.user
import geb.Module
@@ -33,16 +32,16 @@ class ListUserPage extends ScaffoldPage {
static content = {
newUserButton(to: CreateUserPage) { $('a', text: 'New TestUser') }
userTable { $('div.list table', 0) }
- userRow { i -> userRows[i].module UserRow }
+ userRow { int i -> userRows[i].module(UserRow) }
userRows(required: false) { userTable.find('tbody').find('tr') }
}
}
class UserRow extends Module {
static content = {
- cell { i -> $('td', i) }
- cellText { i -> cell(i).text() }
- cellHrefText{ i -> cell(i).find('a').text() }
+ cell { int i -> $('td', i) }
+ cellText { int i -> cell(i).text() }
+ cellHrefText{ int i -> cell(i).find('a').text() }
username { cellText(1) }
userEnabled { 'True' == cellText(2) }
showLink(to: ShowUserPage) { cell(0).find('a') }
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/InheritanceSecuritySpec.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/InheritanceSecuritySpec.groovy
index 5d0898a5a..72e66346f 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/InheritanceSecuritySpec.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/InheritanceSecuritySpec.groovy
@@ -19,6 +19,8 @@
package specs
+import spock.lang.Unroll
+
import grails.testing.mixin.integration.Integration
import pages.IndexPage
import pages.LoginPage
@@ -33,6 +35,7 @@ class InheritanceSecuritySpec extends AbstractSecuritySpec {
go 'testData/addTestUsers'
}
+ @Unroll
void 'should redirect to login page for anonymous'() {
when:
go uri
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/RequestmapSpec.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/RequestmapSpec.groovy
index 01b19f066..e676397ae 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/RequestmapSpec.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/RequestmapSpec.groovy
@@ -20,6 +20,8 @@
package specs
import com.testapp.TestDataService
+import spock.lang.Stepwise
+
import grails.testing.mixin.integration.Integration
import pages.requestmap.CreateRequestmapPage
import pages.requestmap.EditRequestmapPage
@@ -27,100 +29,82 @@ import pages.requestmap.ListRequestmapPage
import pages.requestmap.ShowRequestmapPage
import spock.lang.IgnoreIf
+@Stepwise
@Integration
@IgnoreIf({ System.getProperty('TESTCONFIG') != 'requestmap' })
class RequestmapSpec extends AbstractSecuritySpec {
void 'test request maps are initially present'() {
when:
- go 'testRequestmap/list?max=100'
+ def page = to(ListRequestmapPage, max: 100)
then:
- at ListRequestmapPage
- requestmapRows.size() == TestDataService.URIS_FOR_REQUESTMAPS.size()
+ page.requestmapRows.size() == TestDataService.URIS_FOR_REQUESTMAPS.size()
}
void 'add a requestmap'() {
when:
- to ListRequestmapPage
- newRequestmapButton.click()
-
- then:
- at CreateRequestmapPage
+ to(ListRequestmapPage).with {
+ newRequestmapButton.click()
+ }
- when:
- $('form').url = '/nuevo/**'
- configAttribute = 'ROLE_ADMIN'
- createButton.click()
+ and:
+ def page = at(CreateRequestmapPage)
+ page.urlField.text = '/nuevo/**'
+ page.configAttributeField.text = 'ROLE_ADMIN'
+ page.createButton.click()
+ page = at(ShowRequestmapPage)
then:
- at ShowRequestmapPage
- value('URL') == '/nuevo/**'
- configAttribute == 'ROLE_ADMIN'
+ page.value('URL') == '/nuevo/**'
+ page.configAttribute == 'ROLE_ADMIN'
when:
- go 'testRequestmap/list?max=100'
+ page = to(ListRequestmapPage, max: 100)
then:
- at ListRequestmapPage
- requestmapRows.size() == (TestDataService.URIS_FOR_REQUESTMAPS.size() + 1)
+ page.requestmapRows.size() == (TestDataService.URIS_FOR_REQUESTMAPS.size() + 1)
}
void 'edit the details'() {
when:
- go 'testRequestmap/list?max=100'
+ def page = to(ListRequestmapPage, max: 100)
+ page.requestmapRow(19).showLink.click()
+ page = at(ShowRequestmapPage)
- then:
- at ListRequestmapPage
+ and:
+ page.editButton.click()
+ page = at(EditRequestmapPage)
- when:
- requestmapRow(19).showLink.click()
+ and:
+ page.urlField.text = '/secure2/**'
+ page.configAttributeField.text = 'ROLE_ADMINX'
+ page.updateButton.click()
+ page = at(ShowRequestmapPage)
then:
- at ShowRequestmapPage
-
- when:
- editButton.click()
-
- then:
- at EditRequestmapPage
-
- when:
- $('form').url = '/secure2/**'
- configAttribute = 'ROLE_ADMINX'
- updateButton.click()
-
- then:
- at ShowRequestmapPage
- value('URL') == '/secure2/**'
- configAttribute == 'ROLE_ADMINX'
+ page.value('URL') == '/secure2/**'
+ page.configAttribute == 'ROLE_ADMINX'
}
void 'delete requestmap'() {
when:
- go 'testRequestmap/list?max=100'
-
- then:
- at ListRequestmapPage
-
- when:
- requestmapRow(19).showLink.click()
+ def page = to(ListRequestmapPage, max: 100)
+ page.requestmapRow(19).showLink.click()
+ page = at(ShowRequestmapPage)
+ def deletedId = page.id
- then:
- at ShowRequestmapPage
-
- when:
- def deletedId = id
- withConfirm { deleteButton.click() }
+ and:
+ withConfirm { page.deleteButton.click() }
+ page = at(ListRequestmapPage)
then:
- at ListRequestmapPage
- message == "TestRequestmap $deletedId deleted"
+ page.message == "TestRequestmap $deletedId deleted"
when:
- go 'testRequestmap/list?max=100'
+ page = to(ListRequestmapPage, max: 100)
then:
- requestmapRows.size() == TestDataService.URIS_FOR_REQUESTMAPS.size()
+ page.requestmapRows.size() == TestDataService.URIS_FOR_REQUESTMAPS.size()
}
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/RoleSpec.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/RoleSpec.groovy
index 96ffcdcc4..0074a4171 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/RoleSpec.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/RoleSpec.groovy
@@ -16,16 +16,18 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package specs
-import grails.testing.mixin.integration.Integration
import pages.role.CreateRolePage
import pages.role.EditRolePage
import pages.role.ListRolePage
import pages.role.ShowRolePage
import spock.lang.IgnoreIf
+import spock.lang.Stepwise
+import grails.testing.mixin.integration.Integration
+
+@Stepwise
@Integration
@IgnoreIf({ !(
System.getProperty('TESTCONFIG') == 'annotation' ||
@@ -38,85 +40,81 @@ class RoleSpec extends AbstractSecuritySpec {
void 'there are no roles initially'() {
when:
- to ListRolePage
+ def page = to(ListRolePage)
then:
- roleRows.size() == 0
+ page.roleRows.size() == 0
}
void 'add a role'() {
when:
- to ListRolePage
- newRoleButton.click()
+ def page = to(ListRolePage)
+ page.newRoleButton.click()
- then:
- at CreateRolePage
+ and:
+ page = at(CreateRolePage)
+ page.authorityField.text = 'test'
+ page.createButton.click()
- when:
- authority = 'test'
- createButton.click()
+ and:
+ page = at(ShowRolePage)
then:
- at ShowRolePage
- authority == 'test'
+ page.authority == 'test'
}
void 'edit the details'() {
when:
- to ListRolePage
- roleRow(0).showLink.click()
+ def page = to(ListRolePage)
+ page.roleRow(0).showLink.click()
+ page = at(ShowRolePage)
- then:
- at ShowRolePage
+ and:
+ page.editButton.click()
+ page = at(EditRolePage)
- when:
- editButton.click()
+ and:
+ page.authorityField.text = 'test_new'
+ page.updateButton.click()
then:
- at EditRolePage
+ at(ShowRolePage)
when:
- authority = 'test_new'
- updateButton.click()
+ page = to(ListRolePage)
then:
- at ShowRolePage
-
- when:
- to ListRolePage
-
- then:
- roleRows.size() == 1
-
- def row = roleRow(0)
- row.authority == 'test_new'
+ //page.roleRows.size() == 1
+ page.roleRow(0).authority == 'test_new'
}
void 'show role'() {
when:
- to ListRolePage
- roleRow(0).showLink.click()
+ def page = to(ListRolePage)
+ page.roleRow(0).showLink.click()
then:
- at ShowRolePage
+ at(ShowRolePage)
}
void 'delete role'() {
when:
- to ListRolePage
- roleRow(0).showLink.click()
- def deletedId = id
+ def page = to(ListRolePage).tap {
+ roleRow(0).showLink.click()
+ }
+ def deletedId = page.id
- then:
- at ShowRolePage
+ and:
+ page = at(ShowRolePage)
- when:
- withConfirm { deleteButton.click() }
+ and:
+ withConfirm { page.deleteButton.click() }
- then:
- at ListRolePage
+ and:
+ page = at(ListRolePage)
- message == "TestRole $deletedId deleted"
- roleRows.size() == 0
+ then:
+ page.message == "TestRole $deletedId deleted"
+ page.roleRows.size() == 0
}
}
diff --git a/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/UserSpec.groovy b/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/UserSpec.groovy
index 30b2c499d..eafb922f9 100644
--- a/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/UserSpec.groovy
+++ b/plugin-core/examples/functional-test-app/src/integration-test/groovy/specs/UserSpec.groovy
@@ -16,114 +16,105 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package specs
-import grails.testing.mixin.integration.Integration
import pages.user.CreateUserPage
import pages.user.EditUserPage
import pages.user.ListUserPage
import pages.user.ShowUserPage
import spock.lang.IgnoreIf
+import spock.lang.Stepwise
+import grails.testing.mixin.integration.Integration
+
+@Stepwise
@Integration
@IgnoreIf({ !(
System.getProperty('TESTCONFIG') == 'annotation' ||
- System.getProperty('TESTCONFIG') == 'basic' ||
- System.getProperty('TESTCONFIG') == 'basicCacheUsers' ||
- System.getProperty('TESTCONFIG') == 'requestmap' ||
- System.getProperty('TESTCONFIG') == 'static')
+ System.getProperty('TESTCONFIG') == 'basic' ||
+ System.getProperty('TESTCONFIG') == 'basicCacheUsers' ||
+ System.getProperty('TESTCONFIG') == 'requestmap' ||
+ System.getProperty('TESTCONFIG') == 'static')
})
class UserSpec extends AbstractSecuritySpec {
void 'there are no users initially'() {
when:
- to ListUserPage
+ def page = to(ListUserPage)
then:
- userRows.size() == 0
+ page.userRows.size() == 0
}
void 'add a user'() {
when:
- to ListUserPage
- newUserButton.click()
-
- then:
- at CreateUserPage
+ def page = to(ListUserPage)
+ page.newUserButton.click()
- when:
- username = 'new_user'
- password = 'p4ssw0rd'
- $('#enabled').click()
- createButton.click()
+ and:
+ page = at(CreateUserPage)
+ page.usernameField.text = 'new_user'
+ page.passwordField.text = 'p4ssw0rd'
+ page.enabledCheckbox.check()
+ page.createButton.click()
+ page = at(ShowUserPage)
then:
- at ShowUserPage
- username == 'new_user'
- userEnabled == true
+ page.username == 'new_user'
+ page.userEnabled == true
}
void 'edit the details'() {
when:
- to ListUserPage
- userRow(0).showLink.click()
+ def page = to(ListUserPage)
+ page.userRow(0).showLink.click()
- then:
- at ShowUserPage
+ and:
+ page = at(ShowUserPage)
+ page.editButton.click()
+ page = at(EditUserPage)
- when:
- editButton.click()
+ and:
+ page.usernameField.text = 'new_user2'
+ page.passwordField.text = 'p4ssw0rd2'
+ page.enabledCheckbox.uncheck()
+ page.updateButton.click()
then:
- at EditUserPage
+ at(ShowUserPage)
when:
- username = 'new_user2'
- password = 'p4ssw0rd2'
- $('#enabled').click()
-
- updateButton.click()
+ page = to(ListUserPage)
then:
- at ShowUserPage
-
- when:
- to ListUserPage
-
- then:
- userRows.size() == 1
-
- def row = userRow(0)
+ page.userRows.size() == 1
+ def row = page.userRow(0)
row.username == 'new_user2'
!row.userEnabled
}
void 'show user'() {
when:
- to ListUserPage
- userRow(0).showLink.click()
+ def page = to(ListUserPage)
+ page.userRow(0).showLink.click()
then:
- at ShowUserPage
+ at(ShowUserPage)
}
void 'delete user'() {
when:
- to ListUserPage
- userRow(0).showLink.click()
- def deletedId = id
+ def page = to(ListUserPage)
+ page.userRow(0).showLink.click()
+ def deletedId = page.id
+ page = at(ShowUserPage)
- then:
- at ShowUserPage
-
- when:
- withConfirm { deleteButton.click() }
+ and:
+ withConfirm { page.deleteButton.click() }
+ page = at(ListUserPage)
then:
- at ListUserPage
-
- message == "TestUser $deletedId deleted."
- userRows.size() == 0
+ page.message == "TestUser $deletedId deleted."
+ page.userRows.size() == 0
}
}
diff --git a/plugin-core/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SecurityAutoConfigurationExcluderIntegrationSpec.groovy b/plugin-core/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SecurityAutoConfigurationExcluderIntegrationSpec.groovy
index 22a0ad2ca..c4fbc68d6 100644
--- a/plugin-core/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SecurityAutoConfigurationExcluderIntegrationSpec.groovy
+++ b/plugin-core/examples/integration-test-app/src/integration-test/groovy/grails/plugin/springsecurity/SecurityAutoConfigurationExcluderIntegrationSpec.groovy
@@ -40,28 +40,6 @@ class SecurityAutoConfigurationExcluderIntegrationSpec extends Specification {
)
}
- void "SecurityAutoConfiguration bean is not registered"() {
- given:
- def secAutoConfig = Class.forName(
- 'org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration'
- )
-
- expect:
- applicationContext
- .getBeanNamesForType(secAutoConfig).length == 0
- }
-
- void "SecurityFilterAutoConfiguration bean is not registered"() {
- given:
- def secFilterAutoConfig = Class.forName(
- 'org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration'
- )
-
- expect:
- applicationContext
- .getBeanNamesForType(secFilterAutoConfig).length == 0
- }
-
void "no duplicate SecurityFilterChain beans from auto-configuration"() {
given:
def filterChainBeans = applicationContext
diff --git a/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityEventListenerSpec.groovy b/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityEventListenerSpec.groovy
index e24bb3f83..1e5c4f328 100644
--- a/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityEventListenerSpec.groovy
+++ b/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/SecurityEventListenerSpec.groovy
@@ -18,7 +18,8 @@
*/
package grails.plugin.springsecurity
-import org.springframework.security.access.event.AbstractAuthorizationEvent
+import org.springframework.security.authorization.AuthorizationDecision
+import org.springframework.security.authorization.event.AuthorizationEvent
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.TestingAuthenticationToken
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent
@@ -77,12 +78,12 @@ class SecurityEventListenerSpec extends AbstractUnitSpec {
called
}
- void 'Test handling AbstractAuthorizationEvent'() {
+ void 'Test handling AuthorizationEvent'() {
when:
boolean called = false
closures.onAuthorizationEvent = { e, appCtx -> called = true }
- listener.onApplicationEvent new AbstractAuthorizationEvent(42) {}
+ listener.onApplicationEvent new AuthorizationEvent({ new TestingAuthenticationToken('', '') }, 42, new AuthorizationDecision(true))
then:
called
diff --git a/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy b/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy
index 6348432d6..401edd314 100644
--- a/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy
+++ b/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/SpringSecurityUtilsSpec.groovy
@@ -218,7 +218,7 @@ class SpringSecurityUtilsSpec extends AbstractUnitSpec {
void 'isAjax using SavedRequest, false'() {
when:
- def savedRequest = new DefaultSavedRequest(request, new PortResolverImpl())
+ def savedRequest = new DefaultSavedRequest(request)
request.session.setAttribute SpringSecurityUtils.SAVED_REQUEST, savedRequest
then:
@@ -228,7 +228,7 @@ class SpringSecurityUtilsSpec extends AbstractUnitSpec {
void 'isAjax using SavedRequest, true'() {
when:
request.addHeader 'X-Requested-With', 'true'
- def savedRequest = new DefaultSavedRequest(request, new PortResolverImpl())
+ def savedRequest = new DefaultSavedRequest(request)
request.session.setAttribute SpringSecurityUtils.SAVED_REQUEST, savedRequest
then:
@@ -238,7 +238,7 @@ class SpringSecurityUtilsSpec extends AbstractUnitSpec {
void 'isAjax using SavedRequest, XMLHttpRequest'() {
when:
request.addHeader 'X-Requested-With', 'XMLHttpRequest'
- def savedRequest = new DefaultSavedRequest(request, new PortResolverImpl())
+ def savedRequest = new DefaultSavedRequest(request)
request.session.setAttribute SpringSecurityUtils.SAVED_REQUEST, savedRequest
then:
@@ -431,7 +431,7 @@ class SpringSecurityUtilsSpec extends AbstractUnitSpec {
private void initRoleHierarchy(String hierarchyString) {
defineBeans {
- roleHierarchy(RoleHierarchyImpl) {
+ roleHierarchy(MutableRoleHierarchy) {
hierarchy = hierarchyString
}
}
diff --git a/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/web/access/intercept/FilterSecurityInterceptorSpec.groovy b/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/web/access/intercept/FilterSecurityInterceptorSpec.groovy
new file mode 100644
index 000000000..b21adf64a
--- /dev/null
+++ b/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/web/access/intercept/FilterSecurityInterceptorSpec.groovy
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package grails.plugin.springsecurity.web.access.intercept
+
+import grails.plugin.springsecurity.AbstractUnitSpec
+import org.springframework.security.access.AccessDecisionManager
+import org.springframework.security.access.SecurityConfig
+import org.springframework.security.web.FilterInvocation
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource
+import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
+
+import jakarta.servlet.FilterChain
+
+class FilterSecurityInterceptorSpec extends AbstractUnitSpec {
+
+ void 'doFilter delegates to the chain for public invocations when allowed'() {
+ given:
+ def interceptor = new FilterSecurityInterceptor(
+ rejectPublicInvocations: false,
+ securityMetadataSource: Stub(FilterInvocationSecurityMetadataSource) {
+ getAttributes(_ as FilterInvocation) >> null
+ }
+ )
+ FilterChain chain = Mock()
+
+ when:
+ interceptor.doFilter(request, response, chain)
+
+ then:
+ 1 * chain.doFilter(request, response)
+ }
+
+ void 'doFilter consults the access decision manager before continuing the chain'() {
+ given:
+ def attributes = [new SecurityConfig('ROLE_USER')]
+ def interceptor = new FilterSecurityInterceptor(
+ rejectPublicInvocations: false,
+ securityMetadataSource: Stub(FilterInvocationSecurityMetadataSource) {
+ getAttributes(_ as FilterInvocation) >> attributes
+ }
+ )
+ interceptor.accessDecisionManager = Mock(AccessDecisionManager) {
+ 1 * decide(null, _ as FilterInvocation, attributes)
+ }
+ FilterChain chain = Mock()
+
+ when:
+ interceptor.doFilter(request, response, chain)
+
+ then:
+ 1 * chain.doFilter(request, response)
+ }
+}
+
+
diff --git a/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationEntryPointSpec.groovy b/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationEntryPointSpec.groovy
index f55c13b1c..db8294e9b 100644
--- a/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationEntryPointSpec.groovy
+++ b/plugin-core/plugin/src/test/groovy/grails/plugin/springsecurity/web/authentication/AjaxAwareAuthenticationEntryPointSpec.groovy
@@ -22,6 +22,7 @@ import grails.plugin.springsecurity.AbstractUnitSpec
import grails.plugin.springsecurity.ReflectionUtils
import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.web.SecurityRequestHolder
+import org.springframework.security.web.RedirectStrategy
/**
* Unit tests for WithAjaxAuthenticationProcessingFilterEntryPoint.
@@ -60,6 +61,23 @@ class AjaxAwareAuthenticationEntryPointSpec extends AbstractUnitSpec {
ajaxLoginFormUrl == response.forwardedUrl
}
+ void 'commence() redirects without legacy portResolver wiring'() {
+ given:
+ entryPoint.useForward = false
+ String redirectedUrl
+ entryPoint.redirectStrategy = Stub(RedirectStrategy) {
+ sendRedirect(_, _, _) >> { requestArg, responseArg, url ->
+ redirectedUrl = url as String
+ }
+ }
+
+ when:
+ entryPoint.commence request, response, null
+
+ then:
+ loginFormUrl == redirectedUrl
+ }
+
void 'setAjaxLoginFormUrl'() {
when:
entryPoint.ajaxLoginFormUrl = 'foo'
diff --git a/plugin-core/plugin/src/test/groovy/org/springframework/security/access/intercept/aopalliance/MethodSecurityInterceptorSpec.groovy b/plugin-core/plugin/src/test/groovy/org/springframework/security/access/intercept/aopalliance/MethodSecurityInterceptorSpec.groovy
new file mode 100644
index 000000000..67892e0c2
--- /dev/null
+++ b/plugin-core/plugin/src/test/groovy/org/springframework/security/access/intercept/aopalliance/MethodSecurityInterceptorSpec.groovy
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.springframework.security.access.intercept.aopalliance
+
+import grails.plugin.springsecurity.SecurityTestUtils
+import org.aopalliance.aop.Advice
+import org.aopalliance.intercept.MethodInvocation
+import org.springframework.security.access.AccessDecisionManager
+import org.springframework.security.access.SecurityConfig
+import org.springframework.security.access.intercept.AfterInvocationManager
+import org.springframework.security.access.intercept.RunAsManagerImpl
+import org.springframework.security.access.method.MethodSecurityMetadataSource
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException
+import org.springframework.security.core.Authentication
+import org.springframework.security.core.context.SecurityContextHolder
+import spock.lang.Specification
+
+class MethodSecurityInterceptorSpec extends Specification {
+
+ void cleanup() {
+ SecurityTestUtils.logout()
+ }
+
+ void 'invoke acts as advice and applies access and after-invocation processing'() {
+ given:
+ Authentication authentication = SecurityTestUtils.authenticate(['ROLE_USER'])
+ def method = String.getMethod('trim')
+ def invocation = Stub(MethodInvocation) {
+ getMethod() >> method
+ getThis() >> ' ok '
+ proceed() >> 'ok'
+ }
+ def attributes = [new SecurityConfig('ROLE_USER')]
+ def interceptor = new MethodSecurityInterceptor(
+ securityMetadataSource: Stub(MethodSecurityMetadataSource) {
+ getAttributes(method, String) >> attributes
+ },
+ accessDecisionManager: Mock(AccessDecisionManager) {
+ 1 * decide(authentication, invocation, attributes)
+ },
+ afterInvocationManager: Mock(AfterInvocationManager) {
+ 1 * decide(authentication, invocation, attributes, 'ok') >> 'filtered'
+ }
+ )
+
+ expect:
+ interceptor instanceof Advice
+ interceptor.invoke(invocation) == 'filtered'
+ }
+
+ void 'invoke proceeds directly when there are no security attributes'() {
+ given:
+ def method = String.getMethod('trim')
+ def invocation = Stub(MethodInvocation) {
+ getMethod() >> method
+ getThis() >> ' ok '
+ proceed() >> 'ok'
+ }
+ def interceptor = new MethodSecurityInterceptor(
+ securityMetadataSource: Stub(MethodSecurityMetadataSource) {
+ getAttributes(method, String) >> null
+ }
+ )
+
+ expect:
+ interceptor.invoke(invocation) == 'ok'
+ }
+
+ void 'invoke fails with credentials not found when secured invocation has no authentication'() {
+ given:
+ def method = String.getMethod('trim')
+ def invocation = Stub(MethodInvocation) {
+ getMethod() >> method
+ getThis() >> ' ok '
+ }
+ def interceptor = new MethodSecurityInterceptor(
+ securityMetadataSource: Stub(MethodSecurityMetadataSource) {
+ getAttributes(method, String) >> [new SecurityConfig('ROLE_USER')]
+ }
+ )
+
+ when:
+ interceptor.invoke(invocation)
+
+ then:
+ thrown AuthenticationCredentialsNotFoundException
+ }
+
+ void 'invoke applies run-as authentication during the secured invocation and restores the original authentication afterwards'() {
+ given:
+ Authentication authentication = SecurityTestUtils.authenticate(['ROLE_ADMIN'])
+ def method = String.getMethod('trim')
+ def attributes = [new SecurityConfig('ROLE_ADMIN'), new SecurityConfig('RUN_AS_SUPERUSER')]
+ def interceptor = new MethodSecurityInterceptor(
+ securityMetadataSource: Stub(MethodSecurityMetadataSource) {
+ getAttributes(method, String) >> attributes
+ },
+ accessDecisionManager: Mock(AccessDecisionManager) {
+ 1 * decide(authentication, _ as MethodInvocation, attributes)
+ },
+ runAsManager: new RunAsManagerImpl(),
+ afterInvocationManager: Mock(AfterInvocationManager) {
+ 1 * decide(_ as Authentication, _ as MethodInvocation, attributes, 'ok') >> { Authentication activeAuthentication, MethodInvocation ignored, Collection ignoredAttributes, Object returnedObject ->
+ assert activeAuthentication.authorities*.authority.contains('ROLE_RUN_AS_SUPERUSER')
+ return returnedObject
+ }
+ }
+ )
+ def invocation = Stub(MethodInvocation) {
+ getMethod() >> method
+ getThis() >> ' ok '
+ proceed() >> {
+ assert SecurityContextHolder.context.authentication.authorities*.authority.contains('ROLE_RUN_AS_SUPERUSER')
+ 'ok'
+ }
+ }
+
+ when:
+ def result = interceptor.invoke(invocation)
+
+ then:
+ result == 'ok'
+ SecurityContextHolder.context.authentication.is(authentication)
+ !SecurityContextHolder.context.authentication.authorities*.authority.contains('ROLE_RUN_AS_SUPERUSER')
+ }
+}
+
+
+
+
+
diff --git a/plugin-core/plugin/src/test/groovy/org/springframework/security/access/method/AbstractFallbackMethodSecurityMetadataSourceSpec.groovy b/plugin-core/plugin/src/test/groovy/org/springframework/security/access/method/AbstractFallbackMethodSecurityMetadataSourceSpec.groovy
new file mode 100644
index 000000000..5b96cf886
--- /dev/null
+++ b/plugin-core/plugin/src/test/groovy/org/springframework/security/access/method/AbstractFallbackMethodSecurityMetadataSourceSpec.groovy
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.springframework.security.access.method
+
+import org.springframework.security.access.annotation.Secured
+import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource
+import spock.lang.Specification
+
+class AbstractFallbackMethodSecurityMetadataSourceSpec extends Specification {
+
+ private final SecuredAnnotationSecurityMetadataSource metadataSource = new SecuredAnnotationSecurityMetadataSource()
+
+ void 'getAttributes prefers method metadata from the concrete target class over class metadata'() {
+ given:
+ def method = SecuredService.getMethod('userAnnotated')
+
+ expect:
+ metadataSource.getAttributes(method, SecuredServiceImpl)*.attribute == ['ROLE_USER']
+ }
+
+ void 'getAttributes falls back to class metadata when the concrete target method has no annotation'() {
+ given:
+ def method = SecuredService.getMethod('notAnnotated')
+
+ expect:
+ metadataSource.getAttributes(method, SecuredServiceImpl)*.attribute == ['ROLE_ADMIN']
+ }
+
+ private static interface SecuredService {
+ void notAnnotated()
+ void userAnnotated()
+ }
+
+ @Secured('ROLE_ADMIN')
+ private static class SecuredServiceImpl implements SecuredService {
+ @Override
+ void notAnnotated() {}
+
+ @Override
+ @Secured('ROLE_USER')
+ void userAnnotated() {}
+ }
+}
+
diff --git a/plugin-core/plugin/src/test/groovy/org/springframework/security/access/prepost/MethodSecurityExpressionCompatibilitySpec.groovy b/plugin-core/plugin/src/test/groovy/org/springframework/security/access/prepost/MethodSecurityExpressionCompatibilitySpec.groovy
new file mode 100644
index 000000000..954d129d0
--- /dev/null
+++ b/plugin-core/plugin/src/test/groovy/org/springframework/security/access/prepost/MethodSecurityExpressionCompatibilitySpec.groovy
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.springframework.security.access.prepost
+
+import grails.plugin.springsecurity.SecurityTestUtils
+import org.aopalliance.intercept.MethodInvocation
+import org.springframework.security.access.PermissionEvaluator
+import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler
+import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory
+import org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice
+import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice
+import org.springframework.security.core.Authentication
+import org.springframework.security.core.parameters.P
+import spock.lang.Specification
+
+class MethodSecurityExpressionCompatibilitySpec extends Specification {
+
+ void cleanup() {
+ SecurityTestUtils.logout()
+ }
+
+ void 'metadata source extracts pre and post annotations from secured methods'() {
+ given:
+ def metadataSource = new PrePostAnnotationSecurityMetadataSource(
+ new ExpressionBasedAnnotationAttributeFactory(Stub(DefaultMethodSecurityExpressionHandler))
+ )
+ def method = SecuredService.getMethod('getReports')
+
+ when:
+ def attribute = metadataSource.getAttributes(method, SecuredService).first() as ExpressionBasedAnnotationConfigAttribute
+
+ then:
+ attribute.preAuthorizeExpression == 'hasRole("ROLE_USER")'
+ attribute.postFilterExpression == 'hasPermission(filterObject, read)'
+ }
+
+ void 'pre-invocation voter denies and grants based on the evaluated pre-authorize expression'() {
+ given:
+ PermissionEvaluator permissionEvaluator = Mock()
+ Authentication authentication = SecurityTestUtils.authenticate(['ROLE_USER'])
+ def advice = new ExpressionBasedPreInvocationAdvice(
+ expressionHandler: Stub(DefaultMethodSecurityExpressionHandler) {
+ getPermissionEvaluator() >> permissionEvaluator
+ }
+ )
+ def voter = new PreInvocationAuthorizationAdviceVoter(advice)
+ def reflectedMethod = SecuredService.getMethod('getReport', Long)
+ MethodInvocation invocation = Stub(MethodInvocation)
+ invocation.getMethod() >> reflectedMethod
+ invocation.getArguments() >> ([42L] as Object[])
+ def attribute = new ExpressionBasedAnnotationConfigAttribute(
+ 'hasPermission(#id, "com.testacl.Report", read)', null, null, null)
+
+ when:
+ def denied = voter.vote(authentication, invocation, [attribute])
+
+ then:
+ denied == PreInvocationAuthorizationAdviceVoter.ACCESS_DENIED
+ 1 * permissionEvaluator.hasPermission(authentication, 42L, 'com.testacl.Report', _) >> false
+
+ when:
+ def granted = voter.vote(authentication, invocation, [attribute])
+
+ then:
+ granted == PreInvocationAuthorizationAdviceVoter.ACCESS_GRANTED
+ 1 * permissionEvaluator.hasPermission(authentication, 42L, 'com.testacl.Report', _) >> true
+ }
+
+ void 'pre-invocation voter exposes the create ACL permission alias'() {
+ given:
+ PermissionEvaluator permissionEvaluator = Mock()
+ Authentication authentication = SecurityTestUtils.authenticate(['ROLE_USER'])
+ def advice = new ExpressionBasedPreInvocationAdvice(
+ expressionHandler: Stub(DefaultMethodSecurityExpressionHandler) {
+ getPermissionEvaluator() >> permissionEvaluator
+ }
+ )
+ def voter = new PreInvocationAuthorizationAdviceVoter(advice)
+ def reflectedMethod = SecuredService.getMethod('createReport', Long)
+ MethodInvocation invocation = Stub(MethodInvocation)
+ invocation.getMethod() >> reflectedMethod
+ invocation.getArguments() >> ([42L] as Object[])
+ def attribute = new ExpressionBasedAnnotationConfigAttribute(
+ 'hasPermission(#id, "com.testacl.Report", create)', null, null, null)
+
+ when:
+ def granted = voter.vote(authentication, invocation, [attribute])
+
+ then:
+ granted == PreInvocationAuthorizationAdviceVoter.ACCESS_GRANTED
+ 1 * permissionEvaluator.hasPermission(authentication, 42L, 'com.testacl.Report', 4) >> true
+ }
+
+ void 'post-invocation advice filters returned collections using post-filter expressions'() {
+ given:
+ PermissionEvaluator permissionEvaluator = Mock()
+ Authentication authentication = SecurityTestUtils.authenticate(['ROLE_USER'])
+ def advice = new ExpressionBasedPostInvocationAdvice(Stub(DefaultMethodSecurityExpressionHandler) {
+ getPermissionEvaluator() >> permissionEvaluator
+ })
+ def reflectedMethod = SecuredService.getMethod('getReports')
+ MethodInvocation invocation = Stub(MethodInvocation)
+ invocation.getMethod() >> reflectedMethod
+ invocation.getArguments() >> ([] as Object[])
+ def attribute = new ExpressionBasedAnnotationConfigAttribute(
+ null, null, null, 'hasPermission(filterObject, read)')
+ def report1 = new Object()
+ def report2 = new Object()
+
+ when:
+ def filtered = advice.after(authentication, invocation, attribute, [report1, report2])
+
+ then:
+ filtered == [report1]
+ 1 * permissionEvaluator.hasPermission(authentication, report1, _) >> true
+ 1 * permissionEvaluator.hasPermission(authentication, report2, _) >> false
+ }
+
+ private static class SecuredService {
+ @PreAuthorize('hasPermission(#id, "com.testacl.Report", read)')
+ Object getReport(@P('id') Long id) {
+ null
+ }
+
+ @PreAuthorize('hasPermission(#id, "com.testacl.Report", create)')
+ Object createReport(@P('id') Long id) {
+ null
+ }
+
+ @PreAuthorize('hasRole("ROLE_USER")')
+ @PostFilter('hasPermission(filterObject, read)')
+ List<Object> getReports() {
+ []
+ }
+ }
+}
+
+
+
diff --git a/plugin-rest/spring-security-rest/src/test/groovy/grails/plugin/springsecurity/rest/authentication/DefaultRestAuthenticationEventPublisherSpec.groovy b/plugin-rest/spring-security-rest/src/test/groovy/grails/plugin/springsecurity/rest/authentication/DefaultRestAuthenticationEventPublisherSpec.groovy
index 259c9fe51..68c52e9b9 100644
--- a/plugin-rest/spring-security-rest/src/test/groovy/grails/plugin/springsecurity/rest/authentication/DefaultRestAuthenticationEventPublisherSpec.groovy
+++ b/plugin-rest/spring-security-rest/src/test/groovy/grails/plugin/springsecurity/rest/authentication/DefaultRestAuthenticationEventPublisherSpec.groovy
@@ -82,4 +82,22 @@ class DefaultRestAuthenticationEventPublisherSpec extends Specification {
then:
0 * eventPublisher.publishEvent(_)
}
+
+ void "should allow null publisher in constructor and remain no-op for token events"() {
+ when:
+ new DefaultRestAuthenticationEventPublisher(null).publishTokenCreation(accessToken)
+
+ then:
+ noExceptionThrown()
+ 0 * eventPublisher.publishEvent(_)
+ }
+
+ void "should not execute parent publishEvent when publisher is not set"() {
+ when:
+ new DefaultRestAuthenticationEventPublisher().publishAuthenticationSuccess(accessToken)
+
+ then:
+ noExceptionThrown()
+ 0 * eventPublisher.publishEvent(_)
+ }
}
diff --git a/plugin-ui/examples/extended/src/integration-test/groovy/page/AbstractSecurityPage.groovy b/plugin-ui/examples/extended/src/integration-test/groovy/page/AbstractSecurityPage.groovy
index 66151831c..2cbe93709 100644
--- a/plugin-ui/examples/extended/src/integration-test/groovy/page/AbstractSecurityPage.groovy
+++ b/plugin-ui/examples/extended/src/integration-test/groovy/page/AbstractSecurityPage.groovy
@@ -23,6 +23,10 @@ import geb.Page
abstract class AbstractSecurityPage extends Page {
+ static content = {
+ submitBtn { $('input', type: 'submit') }
+ }
+
void submit() {
submitBtn.click()
}
diff --git a/plugin-ui/examples/extended/src/integration-test/groovy/spec/RegisterSpec.groovy b/plugin-ui/examples/extended/src/integration-test/groovy/spec/RegisterSpec.groovy
index 234a8637f..a73b88587 100644
--- a/plugin-ui/examples/extended/src/integration-test/groovy/spec/RegisterSpec.groovy
+++ b/plugin-ui/examples/extended/src/integration-test/groovy/spec/RegisterSpec.groovy
@@ -16,10 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package spec
-import grails.testing.mixin.integration.Integration
import page.profile.ProfileCreatePage
import page.profile.ProfileEditPage
import page.profile.ProfileListPage
@@ -30,6 +28,8 @@ import page.register.SecurityQuestionsPage
import page.user.UserEditPage
import page.user.UserSearchPage
+import grails.testing.mixin.integration.Integration
+
@Integration
class RegisterSpec extends AbstractSecuritySpec {
diff --git a/plugin-ui/examples/simple/src/integration-test/groovy/page/AbstractSecurityPage.groovy b/plugin-ui/examples/simple/src/integration-test/groovy/page/AbstractSecurityPage.groovy
index 66151831c..2cbe93709 100644
--- a/plugin-ui/examples/simple/src/integration-test/groovy/page/AbstractSecurityPage.groovy
+++ b/plugin-ui/examples/simple/src/integration-test/groovy/page/AbstractSecurityPage.groovy
@@ -23,6 +23,10 @@ import geb.Page
abstract class AbstractSecurityPage extends Page {
+ static content = {
+ submitBtn { $('input', type: 'submit') }
+ }
+
void submit() {
submitBtn.click()
}
diff --git a/plugin-ui/examples/simple/src/integration-test/groovy/page/register/RegisterPage.groovy b/plugin-ui/examples/simple/src/integration-test/groovy/page/register/RegisterPage.groovy
index cb9ff69af..30eb8f00a 100644
--- a/plugin-ui/examples/simple/src/integration-test/groovy/page/register/RegisterPage.groovy
+++ b/plugin-ui/examples/simple/src/integration-test/groovy/page/register/RegisterPage.groovy
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package page.register
import geb.module.PasswordInput
diff --git a/plugin-ui/examples/simple/src/integration-test/groovy/spec/RegisterSpec.groovy b/plugin-ui/examples/simple/src/integration-test/groovy/spec/RegisterSpec.groovy
index 6da4b24df..1e34c404e 100644
--- a/plugin-ui/examples/simple/src/integration-test/groovy/spec/RegisterSpec.groovy
+++ b/plugin-ui/examples/simple/src/integration-test/groovy/spec/RegisterSpec.groovy
@@ -16,32 +16,23 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package spec
-import grails.testing.mixin.integration.Integration
+import com.dumbster.smtp.SimpleSmtpServer
+import com.dumbster.smtp.SmtpMessage
import page.register.ForgotPasswordPage
import page.register.RegisterPage
import page.register.ResetPasswordPage
import page.user.UserEditPage
import page.user.UserSearchPage
-import com.dumbster.smtp.SimpleSmtpServer
-import com.dumbster.smtp.SmtpMessage
+import grails.testing.mixin.integration.Integration
@Integration
class RegisterSpec extends AbstractSecuritySpec {
private SimpleSmtpServer server
- void setup() {
- startMailServer()
- }
-
- void cleanup() {
- server.stop()
- }
-
void testRegisterValidation() {
when:
to(RegisterPage).with {
@@ -105,14 +96,14 @@ class RegisterSpec extends AbstractSecuritySpec {
void testRegisterAndForgotPassword() {
given:
- String un = "test_user_abcdef${System.currentTimeMillis()}"
-
- when:
- def registerPage = to(RegisterPage)
+ startMailServer()
and:
- registerPage.with {
- username = un
+ def un = "test_user_abcdef${System.currentTimeMillis()}"
+
+ when:
+ to(RegisterPage).with {
+ username = un
email = "$un@abcdef.com"
password = 'aaaaaa1#'
password2 = 'aaaaaa1#'
@@ -121,22 +112,17 @@ class RegisterSpec extends AbstractSecuritySpec {
then:
pageSource.contains('Your account registration email was sent - check your mail!')
- 1 == server.receivedEmailSize
-
- when:
- def email = currentEmail
-
- then:
- 'New Account' == email.getHeaderValue('Subject')
+ server.receivedEmailSize == 1
when:
- String body = email.body
+ def smtpMessage = currentEmail
then:
- body.contains("Hi $un")
+ 'New Account' == smtpMessage.getHeaderValue('Subject')
+ smtpMessage.body.contains("Hi $un")
when:
- String code = findCode(body, 'verifyRegistration')
+ String code = findCode(smtpMessage.body, 'verifyRegistration')
then:
code ==~ /^[a-f0-9]{32}$/
@@ -162,22 +148,18 @@ class RegisterSpec extends AbstractSecuritySpec {
then:
pageSource.contains('Your password reset email was sent - check your mail!')
- 2 == server.receivedEmailSize
+ server.receivedEmailSize == 2
when:
- email = currentEmail
+ smtpMessage = currentEmail
then:
- 'Password Reset' == email.getHeaderValue('Subject')
+ smtpMessage.getHeaderValue('Subject') == 'Password Reset'
- when:
- body = email.body
-
- then:
- body.contains("Hi $un")
+ smtpMessage.body.contains("Hi $un")
when:
- code = findCode(body, 'resetPassword')
+ code = findCode(smtpMessage.body, 'resetPassword')
go('register/resetPassword?t=123')
then:
@@ -247,6 +229,10 @@ class RegisterSpec extends AbstractSecuritySpec {
then:
pageSource.contains('User not found')
+
+ cleanup:
+ server?.stop()
+ server = null
}
private SmtpMessage getCurrentEmail() {
@@ -258,11 +244,11 @@ class RegisterSpec extends AbstractSecuritySpec {
return email as SmtpMessage
}
- private String findCode(String body, String action) {
+ private static String findCode(String body, String action) {
def matcher = body =~ /(?s).*$action\?t=(.+)".*/
- assert matcher.hasGroup()
- assert matcher.count == 1
- matcher[0][1]
+ assert matcher.find()
+ assert matcher.groupCount() == 1
+ matcher.group(1)
}
private void startMailServer() {
@@ -279,6 +265,6 @@ class RegisterSpec extends AbstractSecuritySpec {
}
server = SimpleSmtpServer.start(port)
go("testData/updateMailSenderPort?port=$port")
- pageSource.contains("OK: $port")
+ assert pageSource.contains("OK: $port")
}
}