I have the following grails controller
class UserController {
def userService
def roleService
def index() {
def roles = roleService.listRoles()
[roles: roles]
}
def userDetails() {
[user: userService.getUser(params.id), role:params.role]
}
def updateUser() {
def user = userService.getUser(params.id)
if (!(params.username)) {
flash.message = "You have to enter a username!"
redirect(action: "userDetails")
}
else {
user.username = params.username
user.person.title = params.title
user.person.given = params.given
user.person.middle = params.middle
user.person.family = params.family
userService.updateUser(user)
redirect(action: "index")
}
}
}
Starting with index() the user gets a list of all roles and users currently available. The user may then select one particular user being linked to the userDetails()-action. There I retrieve the information about the id of the user with params.id and the user's role name with params.role.
In userDetails.gsp the user is able to update some of the user's properties. However, if he doesn't enter a username, he should be redirected back to the userDetails.gsp. (I know that I could check this with the required-attribute within the gsp - it's just to understand the functionality)
And here is where I get stuck - when using the userDetails()-action, two parameters are passed to the gsp. But now when committing the redirect I don't know how to access this information. As a result, rendering the userDetails.gsp results in an error as the required information concerning the user and the role are not available.
Any help would be highly appreciated!
You should change the form (presumably) that submits to the updateUser action, so that it also sends in the role. Then if the data submitted is invalid, you simply include these parameters when redirecting back to the userDetails action.
def updateUser() {
def user = userService.getUser(params.id)
// I'm not sure if this the right way to get a Role from the role parameter
// but presumably you can figure that out yourself
def role = roleService.getRole(params.role)
if (!(params.username)) {
flash.message = "You have to enter a username!"
redirect action: "userDetails", params: [id: params.id, role: params.role]
}
}
As an aside, the way you're manually binding each parameter to the user object is unnecessarily verbose. Grails' databinding can do this automatically.
Related
I made the following Controller.
I can login and logout, but how can I force the the user to log in first?
I need to start a session on login and terminate it on logout.
class UserController {
def scaffold = User
def login = {}
def authenticate = {
def user = User.findByLoginAndPassword(params.login, params.password)
if(user){
session.user = user
flash.message = "Hello ${user.name}!"
redirect(controller:"entry", action:"list")
} else {
flash.message = "Sorry, ${params.login}. Please try again."
redirect(action:"login")
}
}
def logout = {
flash.message = "Goodbye ${session.user.name}"
session.user = null
redirect(controller:"entry", action:"list")
}
}
Choice 1:
There are number of security plugins available for securing your Grail's app.
The most popular one is the "Spring Security Core Plugin" which will force user to login before accessing your secured resource.
Reference Link : http://grails.org/plugin/spring-security-core
Choice 2 :
But if you don't want to use any external plugin for you application(I would suggest to use one) you can take advantage of "Filters" in Grail's
You can create a filter for checking the session before user hits any action of your controller and if session is already expired/not created then you can redirect them to login page.
Example :
class SecurityFilterFilters {
def filters = {
loginCheck(controller: 'Your_controller_name(if many separate them with pipe(|) )', action: "*") {
before = {
//check if user is logged in(if yes then there will be session.user) and action is not login action
if (!session.user && !actionName.equals('login')) {
//user is not logged in so redirect him to login page
redirect(controller: 'user', action: 'login')
return false
}
}
}
}
}
Reference Link : http://docs.grails.org/2.2.1/ref/Plug-ins/filters.html
See the 'Secured annotation' spring-security-core plugin documentation for Grails
https://grails-plugins.github.io/grails-spring-security-core/v3/
When I used #RequiresPermissions I got cannot resolve symbol 'RequiresPermissions' error. I already have imported org.apache.shiro.authz.annotation.RequiresPermissions.
Annotations are used as #RequiresPermissions("module:books:list")
My authorization class
class AuthController {
def shiroSecurityManager
def index = { redirect(action: "login", params: params) }
def login = {
return [ username: params.username, rememberMe: (params.rememberMe != null), targetUri: params.targetUri ]
}
def signIn = {
def authToken = new UsernamePasswordToken(params.username, params.password as String)
// Support for "remember me"
if (params.rememberMe) {
authToken.rememberMe = true
}
// If a controller redirected to this page, redirect back
// to it. Otherwise redirect to the root URI.
def targetUri = params.targetUri ?: "/"
// Handle requests saved by Shiro filters.
SavedRequest savedRequest = WebUtils.getSavedRequest(request)
if (savedRequest) {
targetUri = savedRequest.requestURI - request.contextPath
if (savedRequest.queryString) targetUri = targetUri + '?' + savedRequest.queryString
}
try{
// Perform the actual login. An AuthenticationException
// will be thrown if the username is unrecognised or the
// password is incorrect.
SecurityUtils.subject.login(authToken)
log.info "Redirecting to '${targetUri}'."
redirect(uri: targetUri)
}
catch (AuthenticationException ex){
// Authentication failed, so display the appropriate message
// on the login page.
log.info "Authentication failure for user '${params.username}'."
flash.message = message(code: "login.failed")
// Keep the username and "remember me" setting so that the
// user doesn't have to enter them again.
def m = [ username: params.username ]
if (params.rememberMe) {
m["rememberMe"] = true
}
// Remember the target URI too.
if (params.targetUri) {
m["targetUri"] = params.targetUri
}
// Now redirect back to the login page.
redirect(action: "login", params: m)
}
}
def signOut = {
// Log the user out of the application.
SecurityUtils.subject?.logout()
webRequest.getCurrentRequest().session = null
// For now, redirect back to the home page.
redirect(uri: "/")
}
def unauthorized = {
render "You do not have permission to access this page."
}
}
My application runs but when I login by using user that I have set, it directly sends me to unauthorized page. I have allowed some permissions to the user.
If you look at the Javadoc for that annotation (or the source) you'll see that it's allowed to be assigned to types (class-level) and methods (#Target(value={TYPE,METHOD})). You are defining your controller actions as closures, which is still supported in Grails 2.0+ but methods are preferred now. You cannot use that annotation on closures, since they are not methods even though Grails and Groovy let you use them like methods. If the Target annotation included FIELD in addition to the other types it would work, but it doesn't because the Shiro library doesn't have direct support for closures in Grails controllers.
So change all of your actions from closures to methods, e.g.
def index() { redirect(action: "login", params: params) }
def login() {
...
}
....
and then you can annotate them.
My Grails app uses the Spring Security plugin. Whenever a user successfully logs in I want to:
store something in the session
redirect them to a custom page (depending on their role)
I need to handle logout events similarly, which was pretty straightforward because the plugin provides a bean named logoutSuccessHandler that can be overriden. I was hoping to similarly find a bean named loginSuccessHandler, but no such luck.
I read the page in the plugin's docs about event handling, but neither of the event handling mechanisms appears to give me access to the current request or session.
If you want to do some stuff upon successful login. You can listen to InteractiveAuthenticationSuccessEvent
class AuthenticationSuccessEventListener implements
ApplicationListener<InteractiveAuthenticationSuccessEvent> {
#Override
public void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
.......do some stuff here
}
}
And then register AuthenticationSuccessEventListener as a spring bean in resources.groovy
You can do whatever you want here, however you wont be able to do redirect from listener.
Here's another similar question
Add a config parameter:
grails.plugins.springsecurity.successHandler.defaultTargetUrl = '/myLogin/handleSuccessLogin'
Then add your custom login-handling in the action that handles this URL
class MyLoginController {
def springSecurityService
def handleSuccessLogin() {
session.foo = 'bar'
if (springSecurityService.currentUser.username == 'bob') {
redirect action: 'bobLogin'
} else {
redirect action: 'defaultLogin'
}
}
def bobLogin() {
// bob's login handler
}
def defaultLogin() {
// default login handler
}
}
I recently used this in a project for logging in. Its kind of a hack but works for me. I'm using version 1.2.7.3 of the plugin.
def auth() {
def config = SpringSecurityUtils.securityConfig
if (springSecurityService.isLoggedIn()) {
def user = User.get(principal.id)
def roles = user.getAuthorities()
def admin_role = Role.findByAuthority("ROLE_ADMIN")
//this user is not admin
if(!roles.contains(admin_role)){
//perform redirect to appropriate page
}
redirect uri: config.successHandler.defaultTargetUrl
//log.info(getPrincipal().username + "logged in at :"+new Date())
return
}
String view = 'auth'
String postUrl = "${request.contextPath}${config.apf.filterProcessesUrl}"
render view: view, model: [postUrl: postUrl,
rememberMeParameter: config.rememberMe.parameter]
}
For logging out I used a Logout controller, performed some action before redirecting to the logout handler:
class LogoutController {
/**
* Index action. Redirects to the Spring security logout uri.
*/
def index = {
// perform some action here
redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl
}
}
I need to execute a function before each def in the controller is invoked (before each new request is sent to the server). In this function I will
1) do a session check if valid forward the request to the appropriate action & controller with the params
2) do a sanitization check for user submitted params
I am aware of per controller interceptor, how can I write a global common interceptor.
how and where can I invoke this function in grails?
Thanks in advance..
You can use beforeInterceptor in the Controller for exactly the same purpose. Put this into a new controller say BaseController and put in all the stuff which you want to do and then extend all your existing Controllers with BaseController and you are ready to go. For more information on beforeInterceptor Click here
Note: If you already have some common controller which is extended by all other controllers, no need to implement a new controller. Just implement the beforeInterceptor in that controller itself.
Alternatively, you can have a filter implemented and do all your stuff in that filter. For more information on filters in Grails Click Here
You can add filter in following way to intercept each action
class AbcFilters {
def filters = {
abc1(controller: '*', action: '*') {
}
abc2(controller: '*Test', action: '*') {
}
abc2(controllerExclude: 'AppStartup*', action: '*') {
}
}
}
Here is a filter for every action in every controller:
all(controller: '*', action: '*') {
before = {
}
after = {
}
afterView = {
}
}
here is a filter for session check that uses spring security:
auditEntry(controller:'*', action:'*') {
before = {
if (session.isNew()){
def userName
def principal = SCH?.context?.authentication?.principal
if (principal instanceof String){
userName = principal
log.info "Anonymous user has logged into the application."
}else{
userName = principal?.username
log.info "User $userName has logged into the application."
log.debug (principal)
}
}
}
}
Upon creating new users in my system, I am sending them a temporary password via email and setting an property of changePasswordNextLogin=true. When they come to log in for the first time, I would like to intercept the flow upon a successful login, check for this this value, and if it is true, redirect them to a change password action. Once the password change has been completed, ideally I would like to send them to their intended destination.
I have been pouring through the default settings and am not seeing - or more likely not interpreting properly - any way to make that happen. It seems that almost every time that I try to cobble some solution together in Grails, I find that someone has already made a much more elegant approach to do the same thing. Is there any functionality built in that would allow this?
If not, I would really appreciate any suggestions on the best approach to make it so.
There is some support for this directly with Spring Security and the grails plugin, but you also have to do some work yourself :)
The domain class that was created when you installed grails-spring-security plugin (and ran the S2Quickstart script) has a property on it named 'passwordExpired'. Set this to true when you create your new user domain instance.
Once that user logs in for the first time, the Spring Security core libs will throw an exception which you can catch in your login controller's authfail closure, re-directing them to the change password form (that you need to supply yourself).
Here's an example from one of my apps, a skeleton version of this closure should already be included in your login controller:
/**
* Callback after a failed login.
*/
def authfail = {
def msg = ''
def username =
session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY]
def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
if (exception) {
if (exception instanceof CredentialsExpiredException) {
msg = g.message(code: "springSecurity.errors.login.passwordExpired")
if (!springSecurityService.isAjax(request))
redirect (action:'changePassword') // <-- see below
}
// other failure checks omitted
}
if (springSecurityService.isAjax(request)) {
render([error: msg] as JSON)
}
else {
flash.message = msg
redirect controller: 'login', action:'auth', params: params
}
}
/**
* render the change pwd form
*/
def changePassword = {
[username: session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY] ?: springSecurityService.authentication.name]
}
From your 'changePasssword' view, submit the form back to another controller closure (I call mine 'updatePassword' that checks whatever constraints you want for passwords and either saves the updated password on the domain object or not..
def updatePassword = {
String username = session[UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY] ?: springSecurityService.authentication.name
if (!username) {
flash.message = 'Sorry, an error has occurred'
redirect controller: 'login', action:'auth'
return
}
String password = params.password
String newPassword = params.password_new
String newPassword2 = params.password_new_2
if (!password || !newPassword || !newPassword2 || newPassword != newPassword2) {
flash.message = 'Please enter your current password and a new password'
render view: 'changePassword', model: [username: username]
return
}
SecUser user = SecUser.findByUsername(username)
if (!passwordEncoder.isPasswordValid(user.password, password, null /*salt*/)) {
flash.message = 'Current password is incorrect'
render view: 'changePassword', model: [username: username]
return
}
if (passwordEncoder.isPasswordValid(user.password, newPassword, null /*salt*/)) {
flash.message = 'Please choose a different password from your current one'
render view: 'changePassword', model: [username: username]
return
}
if (!newPassword.matches(PASSWORD_REGEX)) {
flash.message = 'Password does not meet minimum requirements'
render view: 'changePassword', model: [username: username]
return
}
// success if we reach here!
user.password = springSecurityService.encodePassword(newPassword)
user.passwordExpired = false
user.save()
flash.message = 'Password changed successfully' + (springSecurityService.loggedIn ? '' : ', you can now login')
redirect uri: '/'
}
If you are using Spring Secuirty 3.0 and later, You can refer to the spring security plugin documentation 11.3 Account Locking and Forcing Password Change.
Remember that you should set
grails.plugin.springsecurity.apf.storeLastUsername=true in Config.groovy.