I am trying to use the commentable plugin with Spring Security.
I can't manage to write the right
grails.commentable.poster.evaluator
I tried {User.get(springSecurityService.principal.id)},
but from the CommentController, both User and springSecurity seems unaccessible.
What should I do?
For springSecurityService.principal.id since there's no dependency injection field for springSecurityService it can't work, so you need to call what springSecurityService.principal.id calls - org.springframework.security.core.context.SecurityContextHolder.context.authentication.principal.id
To fix the problem with User, you'll need the full class name with package. So combined, this should be
{com.yourcompany.yourapp.User.get(org.springframework.security.core.context.SecurityContextHolder.context.authentication.principal.id)}
I adapted Burt's answer such that it doesn't throw an exception if nobody is logged in
grails.commentable.poster.evaluator = {
def principal = org.springframework.security.core.context.SecurityContextHolder.context.authentication?.principal
if (principal?.hasProperty('id')) {
def currentUserId = principal.id
if (currentUserId) {
com.yourcompany.yourapp.User.get(currentUserId)
}
}
}
Related
I'm trying to configure a Grails app in a pre-authenticated scenario, using Spring Security Core.
So I wrote a custom authentication filter:
class MyAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
protected getPreAuthenticatedPrincipal(request) { "my_username" }
protected getPreAuthenticatedCredentials(request) { "N/A" }
}
added it to Spring:
beans = {
myAuthenticationFilter(MyAuthenticationFilter) {
authenticationManager = ref('authenticationManager')
checkForPrincipalChanges = true
}
}
and registered it in BootStrap, with position PRE_AUTH_FILTER:
class BootStrap {
def init = { servletContext ->
SpringSecurityUtils.clientRegisterFilter('myAuthenticationFilter',
SecurityFilterPosition.PRE_AUTH_FILTER.order)
}
}
Config.groovy only has the standard User, Role, and UserRole class names, plus some staticRules.
Inside the filter, I tried returning all sorts of things from its getPreAuthenticatedPrincipal() method: the user id, the user id as a string, its username, the User object itself… I can see that my filter is being called for each request (which is what I want, having set checkForPrincipalChanges = true) no matter what I return from it, the current user remains anonymous: springSecurityService.principal is still __grails.anonymous.user__
What do I need to change in my setup, to be able to authenticate my users, with their existing groups and roles? I don't want to write an additional authentication provider, I'm fine with Grails's standard daoAuthenticationProvider. Do I need to return something specific from my filter? Do I need to setup some other Spring classes?
I solved it. I'm documenting it here in case somebody else needs to do the same.
Basically the missing piece was to configure a new PreAuthenticatedAuthenticationProvider as the only provider. No additional code needed, just define it in resources:
myAuthenticationProvider(PreAuthenticatedAuthenticationProvider) {
preAuthenticatedUserDetailsService = ref('authenticationUserDetailsService')
}
and set it as the only provider in Config:
grails.plugin.pringsecurity.providerNames = ['myAuthenticationProvider']
Then, from the authentication filter, just return the username of the pre-authenticated user, or null if none (the anonymous user.)
I'm getting ready to implement the Spring Security UI plugin (we've already implemented the Spring Security Core plugin). I know the core plugin has support for users, roles, and groups, however, I don't see any mention of groups in the Spring Security UI plugin's documentation. Does Spring Security UI plugin not support creating, edition, etc. groups? Anyone tried adding this functionality?
A late response, but I had the same question so I thought I would just try it.
I have just attempted this myself and I believe the the answer is No. (out of the box)
The spring security ui plugin doesn't take the groups into consideration. If you try to edit a user
myapp/user/edit/1
you will recieve some sort of error like:
Class groovy.lang.MissingPropertyException
Message
No such property: authority for class: com.myapp.security.SecGroup Possible solutions: authorities
I'm curious if you found a way around this? Or we will have to customize the the plugin.
As Julian noted the UI doesn't provide support for groups out of the box. To avoid the error you can do the following (customize the plugin):
Copy the User controller into your project to override the plugin's controller:
grails s2ui-override user <your-package-for-controller>
Copy the "buildUserModel" from the plugin code in UserController and edit the userRoleNames field:
import grails.plugin.springsecurity.SpringSecurityUtils
class UserController extends grails.plugin.springsecurity.ui.UserController {
protected Map buildUserModel(user) {
...
// Added so that when using groups doesn't cause an error
Set userRoleNames
if (SpringSecurityUtils.securityConfig.useRoleGroups) {
String groupAuthorityFieldName = SpringSecurityUtils.securityConfig.authority.groupAuthorityNameField
userRoleNames = user[authoritiesPropertyName].collect { it[groupAuthorityFieldName].collect { it[authorityFieldName] } }
} else {
userRoleNames = user[authoritiesPropertyName].collect { it[authorityFieldName] }
}
...
}
Hello!
I am using grails jms and atmosphere plugin...
When trying to inject springSecurityService into
Jms-Atmosphere Service class, the principal/currentUser is null while there is a reference to this object (springSecurityService is not null)
//Grails Service class
class UserMessageService {
static transactional = true
static exposes = ['jms']
static destination = "queue.usermessage"
def jmsService
def springSecurityService
public def onMessage(msg) {
sleep(2000) // slow it down
log.info "sending jms mssage"
//User is null!!
User user = springSecurityService.currentUser
jmsService.send(topic:'msgevent', msg)
return null
}
}
So I am wondering...
Is it a plugin problem, or is it a problem with listening the events (As an event Listener) publicated from the jms plugin?
I have the same problem when using Atmosphere Hander as a Service using the Atmosphere plugin! In this case the atmosphere service have to be exposed to jms events also!
Please give me some ideas to deal with this... workarounds maybe... thanks
I guess there's no cookie set in your particular call. Are you using websocket? Make sure, you've got Atmosphere 1.0.0 and have the session support enabled! You will then still need to fetch the authentication from the originating request:
if(resource.writer instanceof WebsocketResponseWriter) {
AtmosphereResource resource = resource.atmosphereResource
SecurityContext sessionContext = resource.session
.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY)
}
You would then need to provide the current session context with the authentication from the session context you've found. Make sure you reset the authentication once the request has been handled.
Does this help?
I want to implement permission-based access control as discussed in this post.
I am not familiar with the implementation - is there any detailed example on how to implement this from start to finish?
Have you seen the Spring security plugin? Main docs are here
As in this post you could also consider the Shiro plugin as well. I find the Spring security plugin a simpler but the Shiro one does have some advantages as discussed in the post.
I have just solved this (mostly) so I post it here for reference. This solution works on Grails 2.2.4 and Spring Security 2.0 RC
1) Security Domain Model
You model your security domain classes as described in the article, so you will have these domains in the end:
- Permission
- Role
- RolePermission
- User
- UserRole
2) Querying authorities for users
You make sure that your User class returns Permissions instead of Roles as authorities in the getAuthorities() method:
/**
* Gets authorities for the user.
*
* It will return all of the Permissions of the User assigned by the Roles
* which the User has
*/
Set<Permission> getAuthorities() {
Set allPermissions = []
// Collect all Roles of the User
UserRole.findAllByUser(this).each { userRole ->
// Collect all Permissions from the Role
Role role = userRole.role
// Returning the collected permissions
RolePermission.findAllByRole(role).each { rp ->
allPermissions.add(rp.permission)
}
}
return allPermissions
}
3) Spring Security Config
I have this in my Config.groovy for Spring Security configuration (non-relevant parts omitted):
grails {
plugin {
springsecurity {
...
userLookup {
userDomainClassName = 'limes.security.User'
}
authority {
nameField = 'name'
className = 'limes.security.Permission'
}
...
}
}
}
One important highlight is the authority.nameField which MUST conform with your Permission class. The name attribute is called 'name' in my model (and the article).
Naturally, you set your Permission class as the authority.className in order to make it fit with the return values of User.getAuthorities().
4) Using in security expressions
The solution above does not solve the limitation of the Grails Spring Security plugin that you can use only authority name starting with "ROLE_".
So, if you want to call your permissions like "PERM_PERMISSION1", than you have to write EL expressions for checks everywhere (notybly on controller #Secured annotations and static url rules).
So instead of
#Secured(["PERM_PERMISSION1"])
You write
#Secured(["hasRole('PERM_PERMISSION1')"])
Is it possible to inject a Spring bean into a Grails webflow? I tried the following
class CheckoutController {
ShoppingService shoppingService
def checkoutFlow = {
start {
action {
// This attempt to access the service doesn't work
flow.addresses = shoppingService.getOrder()
}
}
}
}
I can access shoppingService from a regular controller action, but I can't access it from an action of the webflow (see above).
add the following to your controller:
def transient shoppingService
There are issues with dependency injection with webflows in controllers that contain traditional actions plus webflows. It worked for me if the traditional action executed first.
see:
GRAILS-7095
GRAILS-4141
Webflows also break notions of defaultAction in mixed controllers. I have found the first webflow wins and becomes the default action.
separately using transient keeps your service from be serialized between flow states. (e.g. don't have to implement serializable)
At first I thought what you listed was pseudocode but I made a sample app using your example and got the NPE as well. I think it may be your flow structure that is the problem. action blocks should go within a flow state. Your flow definition should look something like:
class CheckoutController {
ShoppingService shoppingService
def checkoutFlow = {
start {
action {
flow.addresses = shoppingService.getOrder()
if(flow.addresses) {
showForm()
}
else {
showError()
}
}
on("showForm").to "showForm"
on("showError").to "showError"
}
showError {
...
}
//etc.
}
}
You can definitely use injected services in your web flows. I am guessing that the problem lies in your flow structure.