Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions plugin-core/docs/src/docs/domainClasses/requestmapClass.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,37 @@ under the License.
////

[[requestmapClass]]
=== Requestmap Class
=== Requestmap Domain Class

Optionally, use this class to store request mapping entries in the database instead of defining them with annotations or in `application.groovy`. This option makes the class configurable at runtime; you can add, remove and edit rules without restarting your application.
Create and use a domain class to store request mapping entries in the database instead of defining them with annotations
or in `application.groovy`. This option makes the rules configurable at runtime; you can add, remove and edit rules
without restarting your application.

.Requestmap class configuration options
[cols="30,30,40"]
|====================
| *Property* | *Default Value* | *Meaning*

|requestMap.className
|_none_
|requestmap class name
|_none_ - set to your implementation
|requestmap domain class

|requestMap.urlField
|"`url`"
|"url"
|URL pattern property name

|requestMap.configAttributeField
|"`configAttribute`"
|"configAttribute"
|authority pattern property name

|requestMap.httpMethodField
|"`httpMethod`"
|"httpMethod"
|HTTP method property name (optional, does not have to exist in the class if you don't require URL/method security)
|====================

Assuming you choose `com.mycompany.myapp` as your package, and `Requestmap` as your class name, you'll generate this class:
Assuming you choose `com.mycompany.myapp` as your package, and `SecurityMapping` as your class name, you'll generate this class:

[source, groovy]
.`Requestmap.groovy`
----
package com.mycompany.myapp

Expand All @@ -60,7 +61,7 @@ import grails.compiler.GrailsCompileStatic
@GrailsCompileStatic
@EqualsAndHashCode(includes=['configAttribute', 'httpMethod', 'url'])
@ToString(includes=['configAttribute', 'httpMethod', 'url'], cache=true, includeNames=true, includePackage=false)
class RequestMap implements Serializable {
class SecurityMapping implements Serializable {

private static final long serialVersionUID = 1

Expand All @@ -69,15 +70,15 @@ class RequestMap implements Serializable {
String url

static constraints = {
configAttribute blank: false
httpMethod nullable: true
url blank: false, unique: 'httpMethod'
configAttribute(blank: false)
httpMethod(nullable: true)
url(blank: false, unique: 'httpMethod')
}

static mapping = {
cache true
cache(true)
}
}
----

To use Requestmap entries to guard URLs, see <<requestmapInstances>>.
To use database entries to guard URLs, see <<requestmapInstances>>.
59 changes: 35 additions & 24 deletions plugin-core/docs/src/docs/requestMappings/requestmapInstances.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,68 +18,79 @@ under the License.
////

[[requestmapInstances]]
=== Requestmap Instances Stored in the Database
=== Request Mappings Stored in the Database

With this approach you use the `Requestmap` domain class to store mapping entries in the database. `Requestmap` has a `url` property that contains the secured URL pattern and a `configAttribute` property containing a comma-delimited list of required roles, SpEL expressions, and/or tokens such as `IS_AUTHENTICATED_FULLY`, `IS_AUTHENTICATED_REMEMBERED`, and `IS_AUTHENTICATED_ANONYMOUSLY`.
With this approach you create and use a domain class to store security mapping entries in the database.

To use `Requestmap` entries, specify `securityConfigType="Requestmap"`:
The domain class must have the following properties:

* `url` - the secured URL pattern
* `httpMethod` - the http method for which the rule applies (or null for all methods)
* `configAttribute` - containing a comma-delimited list of required roles,
SpEL expressions, and/or tokens such as `IS_AUTHENTICATED_FULLY`, `IS_AUTHENTICATED_REMEMBERED`,
and `IS_AUTHENTICATED_ANONYMOUSLY`

To use database-backed url security mappings, use the following configuration:

[source,groovy]
.Listing {counter:listing}. Specifying `securityConfigType` as "`Requestmap`"
.Listing {counter:listing}. Configuring database-backed url security rules
----
grails.plugin.springsecurity.securityConfigType = "Requestmap"
grails.plugin.springsecurity.securityConfigType = 'Requestmap'
grails.plugin.springsecurity.requestMap.className = 'com.mycompany.myapp.SecurityMapping'
----

You create `Requestmap` entries as you create entries in any Grails domain class:
See <<requestmapClass>> for an example request map domain class.

You create request map entries as you create entries in any Grails domain class:

[source,groovy]
.Listing {counter:listing}. Creating `Requestmap` entries
.Listing {counter:listing}. Creating request map entries
----
for (String url in [
'/', '/error', '/index', '/index.gsp', '/**/favicon.ico', '/shutdown',
'/assets/**', '/**/js/**', '/**/css/**', '/**/images/**',
'/login', '/login.*', '/login/*',
'/logout', '/logout.*', '/logout/*']) {
new Requestmap(url: url, configAttribute: 'permitAll').save()
new SecurityMapping(url: url, configAttribute: 'permitAll').save()
}

new Requestmap(url: '/profile/**', configAttribute: 'ROLE_USER').save()
new Requestmap(url: '/admin/**', configAttribute: 'ROLE_ADMIN').save()
new Requestmap(url: '/admin/role/**', configAttribute: 'ROLE_SUPERVISOR').save()
new Requestmap(url: '/admin/user/**',
new SecurityMapping(url: '/profile/**', configAttribute: 'ROLE_USER').save()
new SecurityMapping(url: '/admin/**', configAttribute: 'ROLE_ADMIN').save()
new SecurityMapping(url: '/admin/role/**', configAttribute: 'ROLE_SUPERVISOR').save()
new SecurityMapping(url: '/admin/user/**',
configAttribute: 'ROLE_ADMIN,ROLE_SUPERVISOR').save()
new Requestmap(url: '/login/impersonate',
new SecurityMapping(url: '/login/impersonate',
configAttribute: 'ROLE_SWITCH_USER,IS_AUTHENTICATED_FULLY').save()
springSecurityService.clearCachedRequestmaps()
----

The `configAttribute` value can have a single value or have multiple comma-delimited values. In this example only users with `ROLE_ADMIN` or `ROLE_SUPERVISOR` can access `/admin/user/pass:[**]` urls, and only users with `ROLE_SWITCH_USER` can access the switch-user url (`/login/impersonate`) and in addition must be authenticated fully, i.e. not using a remember-me cookie. Note that when specifying multiple roles, the user must have at least one of them, but when combining `IS_AUTHENTICATED_FULLY`, `IS_AUTHENTICATED_REMEMBERED`, or `IS_AUTHENTICATED_ANONYMOUSLY` with one or more roles means the user must have one of the roles and satisty the `IS_AUTHENTICATED` rule.
The `configAttribute` value can have a single value or have multiple comma-delimited values. In this example only users with `ROLE_ADMIN` or `ROLE_SUPERVISOR` can access `/admin/user/pass:[**]` urls, and only users with `ROLE_SWITCH_USER` can access the switch-user url (`/login/impersonate`) and in addition must be authenticated fully, i.e. not using a remember-me cookie. Note that when specifying multiple roles, the user must have at least one of them, but when combining `IS_AUTHENTICATED_FULLY`, `IS_AUTHENTICATED_REMEMBERED`, or `IS_AUTHENTICATED_ANONYMOUSLY` with one or more roles means the user must have one of the roles and satisfy the `IS_AUTHENTICATED` rule.

Unlike the `application.groovy` Map approach (<<configGroovyMap>>), you do not need to revise the `Requestmap` entry order because the plugin calculates the most specific rule that applies to the current request.
Unlike the `application.groovy` map approach (<<configGroovyMap>>), you do not need to revise the request map entry order because the plugin calculates the most specific rule that applies to the current request.

==== Requestmap Cache
==== Request Map Cache

`Requestmap` entries are cached for performance, but caching affects runtime configurability. If you create, edit, or delete an instance, the cache must be flushed and repopulated to be consistent with the database. You can call `springSecurityService.clearCachedRequestmaps()` to do this. For example, if you create a `RequestmapController` the `save` action should look like this (and the update and delete actions should similarly call `clearCachedRequestmaps()`):
Request map entries are cached for performance, but caching affects runtime configurability. If you create, edit, or delete an instance, the cache must be flushed and repopulated to be consistent with the database. You can call `springSecurityService.clearCachedRequestmaps()` to do this. For example, if you create a `RequestSecurityRuleController` the `save` action should look like this (and the update and delete actions should similarly call `clearCachedRequestmaps()`):

[source,groovy]
.Listing {counter:listing}. Calling `clearCachedRequestmaps()`
----
class RequestmapController {
class SecurityMappingController {

def springSecurityService

...
//...

def save(Requestmap requestmap) {
if (!requestmap.save(flush: true)) {
render view: 'create', model: [requestmapInstance: requestmap]
def save(SecurityMapping mapping) {
if (!mapping.save(flush: true)) {
render(view: 'create', model: [instance: mapping])
return
}

springSecurityService.clearCachedRequestmaps()

flash.message = ...
redirect action: 'show', id: requestmap.id
flash.message = 'Mapping saved'
redirect(action: 'show', id: mapping.id)
}
}
----
Loading