A survey/layout of the security plugin space
Colin Harrington
G3 Summit US 2016
Grails is a powerful web framework, for the Java platform aimed at multiplying developers’ productivity thanks to a Convention-over-Configuration, sensible defaults and opinionated APIs. It integrates smoothly with the JVM, allowing you to be immediately productive whilst providing powerful features, including integrated ORM, Domain-Specific Languages, runtime and compile-time meta-programming and Asynchronous programming.
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
The Spring Security plugin simplifies the integration of Spring Security into Grails applications. The plugin provides sensible defaults with many configuration options for customization. Nearly everything is configurable or replaceable in the plugin and in Spring Security itself, which makes extensive use of interfaces.
@Secured(value=["hasRole('ROLE_ADMIN')"], httpMethod='POST')
def someMethod() { ... }
WebSecurityExpressionRoot delegate...
@Secured(closure = {
assert request
assert ctx
authentication.name == 'admin1'
})
def someMethod() { ... }
grails.plugin.springsecurity.filterChain.filterNames = [
'securityContextPersistenceFilter', 'logoutFilter',
'authenticationProcessingFilter', 'myCustomProcessingFilter',
'rememberMeAuthenticationFilter', 'anonymousAuthenticationFilter',
'exceptionTranslationFilter', 'filterInvocationInterceptor'
]
grails.plugin.springsecurity.filterChain.chainMap = [
[pattern: '/urlpattern1/**', filters: 'filter1,filter2,filter3,filter4'],
[pattern: '/urlpattern2/**', filters: 'filter1,filter3,filter5'],
[pattern: '/**', filters: 'JOINED_FILTERS']
]
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
public interface UserDetailsService {
UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException;
}
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated)
throws IllegalArgumentException;
}
public abstract class AbstractAuthenticationProcessingFilter
extends GenericFilterBean
implements ApplicationEventPublisherAware,
MessageSourceAware {
...
}
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
Token Based
Stateful/Stateless
JWT
RFC6750
JWT Signatures
Encryption
Grails plugin for Facebook Authentication, extension to Grails Spring Security Core plugin
The ACL plugin adds Domain Object Security support to a Grails application that uses Spring Security. It depends on the Spring Security Core plugin.
The core plugin and other extension plugins support restricting access to URLs via rules that include checking a user’s authentication status, roles, etc. and the ACL plugin extends this by adding support for restricting access to individual domain class instances. The access can be very fine-grained and can define which actions can be taken on an object - these typically include Read, Create, Write, Delete, and Administer but you’re free to define whatever actions you like.
@PreAuthorize
@PreFilter
@PostAuthorize
@PostFilter
class ReportService {
@PreAuthorize("hasPermission(#id, 'com.yourapp.Report', read) or " +
"hasPermission(#id, 'com.yourapp.Report', admin)")
Report getReport(long id) {
Report.get(id)
}
@PreAuthorize("hasRole('ROLE_USER')")
@PostFilter("hasPermission(filterObject, read) or " +
"hasPermission(filterObject, admin)")
List getAllReports(params = [:]) {
Report.list(params)
}
@PreAuthorize("hasPermission(#report, write) or " +
"hasPermission(#report, admin)")
Report updateReport(Report report, params) {
report.properties = params
report.save()
report
}
@PreAuthorize("hasPermission(#report, delete) or " +
"hasPermission(#report, admin)")
void deleteReport(Report report) {
report.delete()
}
}
class AclUtilService {
void addPermission(Class<?> domainClass, Serializable id,
recipient, Permission permission) { ... }
void addPermission(domainObject, recipient, Permission permission) { ... }
void addPermission(ObjectIdentity oid, recipient,
Permission permission) { ... }
void changeOwner(domainObject, String newUsername) { ... }
void deletePermission(domainObject, recipient, Permission permission) { ... }
void deletePermission(Class<?> domainClass, long id,
recipient, Permission permission) { ... }
boolean hasPermission(Authentication authentication,
domainObject, Permission... permissions) { ... }
boolean hasPermission(Authentication authentication,
domainObject, List<Permission> permissions) { ... }
Acl readAcl(domainObject) { ... }
Acl readAcl(Class<?> domainClass, id) { ... }
void deleteAcl(domainObject) { ... }
protected Sid createSid(recipient) { ... }
}
The Enforcer Plugin, gives you the tools to enforce business rules, and or permissions. Enforcer is light weight, easy to maintain, extend and, use. The plugin works off of a EnforcerService in conjunction with the Enforce, Reinforce, and ReinforceFilter Annotations(AST transforms).
def enforcerService
enforcerService.enforce({ hasRole('ROLE_USER', testUser) })
enforcerService.enforce({ hasDomainRole('owner', sprocket, testUser) })
@Enforce({hasRole('ROLE_USER', testUser)
&& hasDomainRole('owner', sprocket, testUser)})
def someMethod(){
//some logic
}
@Reinforce({hasRole('ROLE_USER', testUser)
&& hasDomainRole('owner', sprocket, testUser)})
def someMethod(){
//some logic
}
@ReinforceFilter({ Object o ->
(o as List).findResults { it % 2 == 0 ? it : null } })
List<Integer> reinforceFilter() {
[1, 2, 3, 4, 5, 6, 7, 8, 9]
}
The Spring Security Shiro plugin adds some support for using a hybrid approach combining Spring Security and Shiro. It currently only supports Shiro ACLs, since Spring Security ACLs are very powerful but can be very cumbersome to use, and the Shiro approach is straightforward and simple.
The majority of the authentication and authorization work is still done by Spring Security. This plugin listens for Spring Security authentication events and uses the Spring Security Authentication instance to build and register a ShiroSubject instance. It also removes the Shiro credentials when you explicitly logout.
grails.plugin.springsecurity.shiro.permissionDomainClassName =
'com.mycompany.myapp.Permission'
class Permission {
User user
String permission
static constraints = {
permission unique: 'user'
}
}
import com.mycompany.myapp.MyShiroPermissionResolver
beans = {
shiroPermissionResolver(MyShiroPermissionResolver)
}
import org.apache.shiro.SecurityUtils
import org.apache.shiro.subject.Subject
...
Subject subject = SecurityUtils.getSubject()
subject.checkPermission('printer:print:lp7200')
subject.isPermitted('printer:print:lp7200')
subject.checkRole('ROLE_ADMIN')
subject.hasRole('ROLE_ADMIN')
subject.isAuthenticated()
... etc
The Grails Security Bridge plugin is used for providing a decoupled, cross-plugin security interface. This allows you to keep the majority of authentication logic in one plugin, while other plugins can reference a public API interface to retrieve the information needed.
@Secure Annotation
...
interface SecurityBridge {
def getCurrentUser()
def getUserIdentity()
def getCurrentAccount()
def getAccountIdentity()
def getCurrentUserDisplayName()
boolean isLoggedIn()
boolean isAuthorized(object, action)
boolean hasRole(role)
boolean hasPermission(permission, opts)
boolean hasPermission(permission)
def storeLocation(request)
def withUser(identity, Closure code)
Map createLink(String action)
}
application.yml
grails:
plugin:
springsecurity:
oauth2:
active: true
registration:
askToLinkOrCreateAccountUri: '/oauth2/ask'
roleNames: ['ROLE_USER']
class MyGrailsUser {
...
static hasMany = [oAuthIDs: OAuthID]
}
Add the oAuthIDs to your user object
The OAuth2 plugin adds OAuth 2.0 support to a Grails application that uses Spring Security.
It depends on Spring Security Core plugin.
grails.org
User Guide
(RTFM: Read The Fantastic Manual)
Stack Overflow
Slack
Github!
Please visit our table for giveaways
and to meet the team!
ociweb.com/grails
info@ociweb.com