The object of UserProifle cannot be saved for ShiroUser - grails

I am working on a Grails app and I am trying to tie a ShiroUser with a UserProfile.
I have two models called ShiroUser and UserProfile. In my ShiroUser:
class ShiroUser {
... ...
static hasOne = [profile: UserProfile]
static constraints = {
email(nullable: false, blank: false, unique: true)
profile(nullable: false)
}
}
And in my UserProfile.groovy, I have:
class UserProfile {
... ...
static belongsTo = [shiroUser:ShiroUser]
}
However, in my ShiroUserController.groovy, when I try to create a new ShiroUser instance, this doesn't work so well. Here's my code:
def create() {
[shiroUserInstance: new ShiroUser(params), userProfileInstance: new UserProfile()]
}
def save() {
//todo add validation for email and password here.
def shiroUserInstance = new ShiroUser(params)
// Create a user profile
def userProfileInstance = new UserProfile()
shiroUserInstance.profile.email = params.email
shiroUserInstance.profile.firstName = params.firstName
shiroUserInstance.profile.lastName = params.lastName
if (!userProfileInstance.save(flush: true)){
render(view: "create", model: [userProfileInstance: userProfileInstance])
return
}
shiroUserInstance.profile = userProfileInstance
if (!shiroUserInstance.save(flush: true)) {
render(view: "create", model: [shiroUserInstance: shiroUserInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'shiroUser.label', default: 'ShiroUser'), shiroUserInstance.id])
redirect(action: "show", id: shiroUserInstance.id)
}
When I go to my application and try to create a new ShiroUser, the object cannot be saved. I updated the schema before I run the app so it should not be a migration issue. Any thoughts?

It looks like in this block of code you are assigning email, firstName and lastName to the wrong object:
// Create a user profile
def userProfileInstance = new UserProfile()
shiroUserInstance.profile.email = params.email
shiroUserInstance.profile.firstName = params.firstName
shiroUserInstance.profile.lastName = params.lastName
Try this instead:
// Create a user profile
def userProfileInstance = new UserProfile()
userProfileInstance.email = params.email
userProfileInstance.firstName = params.firstName
userProfileInstance.lastName = params.lastName
shiroUserInstance.profile = userProfileInstance
You should then be able to get away with just saving shiroUserInstance and it should automatically save userProfileInstance as well if you have your mapping set up correctly.

Related

How to enter a hard-coded value to the database?

I have a few fields in my User.groovy model class
fName
lName
userName
pwd
yourTel
The user will be entering the first 4 fields shown above. The 5th field yourTel will be hardcoded.
def create() {
[userInstance: new User(params)]
}
How can I do it ?
This is what I already tried:
def create() {
userInstance.yourTel = "2323232"
params = userInstance.yourTe
[userInstance: new User(params)]
}
SAVE
def save() {
def userInstance = new User(params)
if (!userInstance.save(flush: true)) {
render(view: "create", model: [userInstance: userInstance])
return
}else {
def userRole = Role.findOrSaveWhere(authority:'ROLE_USER')
if(!userInstance.authorities.contains(userRole)){
UserRole.create(userInstance, userRole, true)
}
redirect(controller:"login", action : "auth")
}
}
MODEL
static constraints = {
...
yourTel blank:true , nullable: false
Your approach works too with a bit of tweak:
def create() {
def instance = new User(params)
instance.yourTel="2323232"
[userInstance: instance]
}
The [userInstance: instance] the left is the key that will be used by your models, the right hand side is what you are passing to it. Here you first create the new User(params) then bind it with params and then you can tweak it and pass it back to your model.
def save() {
def userInstance = new User(params)
// set the yourTel to some value
userInstance.yourTel="2323232"
if (!userInstance.save(flush: true)) {
render(view: "create", model: [userInstance: userInstance])
params is a map, so put your data like
params.yourTel="2323232"
or
params.put("yourTel","2323232")
Now your code becomes:
def create() {
params.yourTel="2323232"
[userInstance: new User(params)]
}

How to add a User in Grails Bootsrap with Audit Trails on?

Hi I'm having a hard time trying to add the first user in my grails bootstrap. I've tried many different ways but all have failed since the Audit Trail plugin wants a user to stamp the createdBy and EditedBy fields but the first one doesn't exit yet.
Here is my config for Audit Trails, essentially it's the same as default except I'm using User.username over the User.id:
grails {
plugin {
audittrail {
createdBy.field = "createdBy"
createdBy.type = "java.lang.String" //Long is the default
editedBy.field = "editedBy"
editedBy.type = "java.lang.String" //Long is the default
createdDate.field = "createdDate"
editedDate.field = "editedDate"
currentUserClosure = {ctx->
def authPrincipal = ctx.springSecurityService.principal
if(authPrincipal && authPrincipal != "anonymousUser"){
return authPrincipal.username
} else {
return null //fall back
}
}
}
}
}
Here is what my User class looks like, ie. I just add the annotation for the plugin to work:
#gorm.AuditStamp
class User {
//... basic Spring Security Generated User File with minor changes
}
Here is the contents of my bootstrap file:
def adminUser = new User(
username: 'admin',
enabled: true,
password: 'password',
firstName: 'ADMIN',
lastName: 'ADMIN'
).save(flush: true)
// The below will work if I take off the annotation on User class.
SpringSecurityUtils.doWithAuth('admin') {
def adminRole = new Role(authority: 'ROLE_ADMIN').save(flush: true)
UserRole.create adminUser, adminRole, true
}
So now how can I add that first user? I've tried:
- Using annonymousUser
- Failed, plugin prevents it
- Hardcoding the createdBy and editedby to "admin"
- Failed get's overridden
- Use executeUpdate to insert the user directly in the DB
- Failed: in bootstrap
- Delay the save of the first user until the doWithAuth
- Failed: couldn't find the user
Any help would be great! Thanks!
I had the same problem and was searching for a solution. I managed to get it working by doing the below:
grails {
plugin {
audittrail {
createdBy.field = "createdBy"
createdBy.type = "java.lang.String" //Long is the default
editedBy.field = "modifiedBy"
editedBy.type = "java.lang.String" //Long is the default
createdDate.field = "createdDate"
editedDate.field = "modifiedDate"
//custom closure to return the current user who is logged in
currentUserClosure = {ctx->
//ctx is the applicationContext
def userName = ctx.springSecurityService.principal?.username
return userName != null ? userName : "System"
}
}
}
}
In the Bootstrap.groovy file, just do a save like:
def adminUser = new User(username: 'admin', enabled: true, password: 'password',
firstName: 'ADMIN', lastName: 'ADMIN').save(flush: true)
def adminRole = new Role(authority: 'ROLE_ADMIN').save(flush: true)
UserRole.create adminUser, adminRole, true
The newly created first user will be stamped with user "System" (createdBy and modifiedBy columns).

not able to edit currently logged-in user profile in Grails

I am trying to edit currently logged-in user profile. I have used Spring Security Service plugin for user management. User ( which is subscriber in my application) contains field which come from different domain like:
1. User(subscriber in app): has username, password.
2 Profile:has emailaddress and phonenumber etc.
3. Person:has Firstname and lastname.
All above domains make a complete profile for user(subscriber).
Now I want to edit currently logged-in user profile like firstname, lastname or email.
I tried with following code.
def userSettings = {
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
if (loggedinSubscriber){
Profile profile = Profile?.get(params.id);
Party person = profile?.Person?.get(params.id);
if (!person){
flash.message = "could not find user with ${params.id}"
redirect action: list
}
else
[person: person, authorityList: sortedRoles()]
}
else {
redirect(controller: "login" , action:"login");
}
}
But it did not work. Here I got currently logged in user id but profile is null.
Profile domain:
package com.vproc.member
import java.util.Date;
import com.vproc.enquiry.Enquiry;
import com.vproc.enquiry.Membership;
import com.vproc.enquiry.Team;
class Profile {
String emailAddress // field governed by privacy policy
String phoneNumber // field governed by privacy policy
Date dateCreated
Date lastUpdated
boolean isDefaultProfile
static belongsTo = [ Person]
//ProfilePrivacyLevelEnum privacyLevel = ProfilePrivacyLevelEnum.Private
static constraints = {
}
}
Person domain:
package com.vproc.member
import com.vproc.enquiry.Enquiry;
import com.vproc.enquiry.Membership;
import com.vproc.enquiry.Notification;
import com.vproc.enquiry.Team;
class Person extends Party{
String firstName
String lastName
Profile profile
static belongsTo = [Organization]
static constraints = {
lastName nullable:true
firstName blank:false
}
}
Subscriber domain:
package com.vproc.member
import java.util.Date;
import com.vproc.common.StatusEnum;
import com.vproc.enquiry.Discussion;
import com.vproc.enquiry.Enquiry;
import com.vproc.enquiry.Membership;
import com.vproc.enquiry.Notification;
import com.vproc.enquiry.SharedEnquiry;
import com.vproc.enquiry.Team;
import com.vproc.order.Seat;
class Subscriber extends PartyRole{
transient springSecurityService
String username
String password
boolean enabled
boolean accountExpired
boolean accountLocked
boolean passwordExpired
StatusEnum status
Date dateCreated
Date lastUpdated
List<Contact> contacts ;
static belongsTo = [ customer: Customer]
static hasMany = [seats: Seat, ownedEnquiries: Enquiry,enquiresSharedWith: SharedEnquiry,]
static constraints = {
// username validator : { val , obj ->
// if (obj.status != StatusEnum.Pending)
// val!= null
// }
username unique: true
password validator : { val , obj ->
if (obj.status != StatusEnum.Pending)
val != null
}
contacts nullable: true
notifications nullable : true
username nullable: true
password nullable: true
}
}
UserController.groovy
package com.vproc.member
import com.vproc.common.StatusEnum
import com.vproc.exception.CustomValidationException;
class UserController extends AbstractS2UiController {
def saltSource
def userCache
def springSecurityService
def mailService
def messageSource
def create = {
//Subscriber user = lookupUserClass().newInstance(params)
UserCommand command = new UserCommand()
[command: command, authorityList: sortedRoles()]
}
def save = { UserCommand command ->
if (command.hasErrors()) {
render view: 'create', model: [command: command]
return
}
Subscriber user = lookupUserClass().newInstance(params)
Profile profile = new Profile(emailAddress : command.emailAddress, phoneNumber: "234555", isDefaultProfile: "true").save()
Party person = new Person(firstName: command.firstName, lastName: command.lastName, profile: profile).save()
user.party = person
if(! user.party.hasErrors()){
if (params.password) {
String salt = saltSource instanceof NullSaltSource ? null : params.username
user.password = springSecurityUiService.encodePassword(params.password, salt)
user.status = StatusEnum.Active
}else{
user.status = StatusEnum.Pending
}
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
user.customer = loggedinSubscriber.customer
if (!user.save(flush: true)) {
flash.message = "not able to save user"
}
}
else{
flash.message = "not able to save user"
}
//addRoles(user)
//flash.message = "User has been added"
flash.message = "${message(code: 'default.created.message', args: [message(code: 'user.label', default: 'User'), user.id])}"
redirect( action : "list" )
}
def edit = {
String username
def user = params.username ? lookupUserClass().findWhere((usernameFieldName): params.username) : null
if (!user) user = findById()
if (!user) return
return buildUserModel(user)
}
// def contacts = Contact.findAllBySubscriber( loggedinSubscriber)
def userSettings = {
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
if (loggedinSubscriber){
Profile profile = Profile?.get(params.id);
Party person = profile?.Person?.get(params.id);
if (!person){
flash.message = "could not find user with ${params.id}"
redirect action: list
}
else
[person: person, authorityList: sortedRoles()]
}
else {
redirect(controller: "login" , action:"login");
}
}
}
Now I want to edit profile of currently logged-in user using method userSettings in usercontroller. I got id of currently logged in user id but I am not able to use that id with profile and person.
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
if (loggedinSubscriber){
Profile profile = Profile?.get(params.id);
Party person = profile?.Person?.get(params.id);
Using above code, profile value is null.
Ok I dont't want to understand all of your domain modeling, so I'm answering on the thing that I noticed right after having a look at your code. I hope it helps, otherwise just use debug and logging a lot to tackle down your error. And read the documentation.
First of all, in Profile.groovy (and other Domains) use a map to define belongsTo:
static belongsTo = [person:Person]
that also does not look right:
Profile profile = Profile?.get(params.id);
When you have a static access to a grovvy class (upper case first letter) you don't need the questionmark. Is params.id supposed to be the ID of the profile? Then you need to use the findBy methods:
Profile profile = Profile.findById(params.id);
Then in the next line you have to use the key from your belongsTo map again, so person instead of Person:
// no get or findBy required
Party person = profile?.person
Hope it helps
I got solution with following code:
def userSettings = {
Subscriber loggedinSubscriber = Subscriber.get( springSecurityService.principal.id )
Party person = Person?.get(loggedinSubscriber.party.id)
Profile profile = person?.profile
[userInstance: profile, authorityList: sortedRoles()]
}
Thanks BurtBeckwith and moeTi.

Deserialize a JSON object with support for embedded associations

Is there an easy way to deserialize a JSON string to a domain class with support of embedded association; belongsTo and hasMany
{
name: "Customer",
contact: {
name: "Contact"
}
}
class Customer {
name
Contact contact
}
class Contact {
String name
static belongsTo = [customer:Customer]
}
in my controller I would like to do the following
def save() {
def customer = new Customer(request.JSON)
customer.save();
}
Now i'm forced to do
def save() {
def contact = new Contact(request.JSON.contact);
def customer = new Customer(request.JSON);
customer.contact = contact;
customer.save();
}
Have you tried using JsonSlurper?
Example usage:
def slurper = new JsonSlurper()
def result = slurper.parseText('{"person":{"name":"Guillaume","age":33,"pets":["dog","cat"]}}')
assert result.person.name == "Guillaume"
assert result.person.age == 33
assert result.person.pets.size() == 2
assert result.person.pets[0] == "dog"
assert result.person.pets[1] == "cat"
Ref: http://groovy.codehaus.org/gapi/groovy/json/JsonSlurper.html
you can try this
Test test
def result = new JsonSlurper().parseTest('yourString')
test = result
Try this will work.

Grails addTo in for loop

I'm doing a website for reading stories. My goal is to do save the content of the story into several pages to get a list and then paginate it easily; I did the following:
In the domain I created two domains, Story:
class Story {
String title
List pages
static hasMany=[users:User,pages:Page]
static belongsTo = [User]
static mapping={
users lazy:false
pages lazy:false
}
}
And Page:
class Page {
String Content
Story story
static belongsTo = Story
static constraints = {
content(blank:false,size:3..300000)
}
}
The controller save action is:
def save = {
def storyInstance = new Story(params)
def pages = new Page(params)
String content = pages.content
String[] contentArr = content.split("\r\n")
int i=0
StringBuilder page = new StringBuilder()
for(StringBuilder line:contentArr){
i++
page.append(line+"\r\n")
if(i%10==0){
pages.content = page
storyInstance.addToPages(pages)
page =new StringBuilder()
}
}
if (storyInstance.save(flush:true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'story.label', default: 'Story'), storyInstance.id])}"
redirect(action: "viewstory", id: storyInstance.id)
}else {
render(view: "create", model: [storyInstance: storyInstance])
}
}
(I know it looks messy but it's a prototype)
The problem is that I'm waiting for storyInstance.addToPages(pages) to add to the set of pages an instance of the pages every time the condition is true. But what actually happens that it gives me the last instance only with the last page_idx. I thought it would save the pages one by one so I could get a list of pages to every story.
Why does this happen and is there a simpler way to do it than what i did?
Any help is appreciated.
You are working with only one page... Correct solution:
def save = {
def storyInstance = new Story(params)
def i = 0
StringBuilder page = new StringBuilder()
for(StringBuilder line in params?.content?.split("\r\n")){
i++
page.append(line+"\r\n")
if(i%10 == 0){
storyInstance.addToPages(new Page(content: page.toString()))
page = new StringBuilder()
}
}
if (storyInstance.save(flush:true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'story.label', default: 'Story'), storyInstance.id])}"
redirect(action: "viewstory", id: storyInstance.id)
}else {
render(view: "create", model: [storyInstance: storyInstance])
}
}

Resources