inserting audit logging entry for user login - grails

I am using the grails 1.3.7 with audit logging plugin. I want to capture user login event like "user:PSam logged in at .." in the logs and since plugin does not have onLoad event defined, I am adding it to the domain classes and populate my own audit table entry in this event.
So in grails user domain class I do as follows
def onLoad = {
try {
Graaudit aInstance = new Graaudit();
aInstance.eventType= "User Log in"
aInstance.eventDescription = "User logged in"
aInstance.user_id = username
aInstance.save(flush:true);
println "username="+username
println aInstance;
}
catch (e){
println(e.getMessage())
}
}
I am having two issues here...
1) the username property defined in this domain class as an attribute is coming up as null
2) it throws an exception saying: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
Is there another approach to be taken here?
and also can use the plugins audi_log table to populated my onLoad() events entries?
Thanks in advance

Wrap yoyr logic in transaction:
def onLoad = {
Graaudit.withTransaction {
try {
Graaudit aInstance = new Graaudit();
aInstance.eventType= "User Log in"
aInstance.eventDescription = "User logged in"
aInstance.user_id = username
aInstance.save();
println "username="+username
println aInstance;
}
catch (e){
println(e.getMessage())
}
}
}

Related

springSecurityService.isLoggedIn() returns false inside #MessageMapping function

I'm attempting a simple chat that requires the user to be logged in to use. I've gotten the login working, and can detect it in the controller with springSecurityService.isLoggedIn(). However, when calling that function inside the chat handler, it will always return false. The function in question:
#MessageMapping("/sendchat")
#SendTo("/topic/chats")
protected String sendchat(String getrequest) {
log.info "\n\npre login"
if(springSecurityService.isLoggedIn())
{
log.info "\n\n"+getrequest
//do other things if successful
}
else
{
log.info "\n\nnot logged in"
}
}
It will always execute the else statement, even when the index() function of the controller has correctly detected the user as logged in.
That is because "springSecurityService" is work only for Controller action methods, the "spring-security-core plugin" use filters to setup security-context, then the springSecurityService can retrieve the user login status.
While in your code, the method is "protected" and it is a "websocket plugin" convention, so you should do security according to the plugin document, like this:
class ExampleController {
#ControllerMethod
#MessageMapping("/hello")
#PreAuthorize("hasRole('ROLE_USER')")
#SendTo("/topic/hello")
String hello(String world) {
return "hello from secured controller, ${world}!"
}
#ControllerMethod
#MessageExceptionHandler
#SendToUser(value = "/queue/errors", broadcast = false)
String handleException(Exception e) {
return "caught ${e.message}"
}
}
More details can reference "https://github.com/zyro23/grails-spring-websocket#securing-message-handler-methods".

Grails3 spring security core 3 cannot use domain objects

I am trying to create a grails 3.1.2 website with spring security core 3.0.4.
I have used this page:
Grails Spring Security Core
I used the following scripts (replacing "website" for my project name):
grails s2-quickstart com.website User Role
grails s2-create-persistent-token com.website.PersistentLogin
grails s2-create-role-hierarchy-entry com.website.RoleHierarchyEntry
Then I added the following to my BootStrap.groovy:
if(User.count()==0) {
Date testDate = new Date()
Role userRole = new Role('ROLE_USER').save()
Role adminRole = new Role('ROLE_ADMIN').save()
User user = new User("John","password").save()
User admin = new User("Richard","password").save()
UserRole.create(admin, adminRole)
UserRole.create(user, userRole)
UserRole.withSession {
it.flush()
it.clear()
}
assert User.count() == 2
assert Role.count() == 2
assert UserRole.count() == 2
}
I received an error about not being able to find authorities of the User bean class, so I replaced the getAuthorities method with this (as adding it to the transient list didn't work):
Set<Role> getAuthorities() {
if(this.id){
return UserRole.findAllBySecUser(this)*.role
}
else{
return [] as Set
}
}
Then I try and build again, and receive an exception about the UserRole class.
ERROR org.springframework.boot.SpringApplication - Application startup failed
org.codehaus.groovy.runtime.metaclass.MethodSelectionException: Could not find which method <init>() to invoke from this list:
public com.website.UserRole#<init>(org.springsource.loaded.C)
public com.website.UserRole#<init>(com.website.User, com.website.Role)
at groovy.lang.MetaClassImpl.chooseMethodInternal(MetaClassImpl.java:3197) ~[groovy-2.4.6.jar:2.4.6]
at groovy.lang.MetaClassImpl.chooseMethod(MetaClassImpl.java:3134) ~[groovy-2.4.6.jar:2.4.6]
at groovy.lang.MetaClassImpl.createConstructorSite(MetaClassImpl.java:3434) ~[groovy-2.4.6.jar:2.4.6]
at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:91) [groovy-2.4.6.jar:2.4.6]
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60) [groovy-2.4.6.jar:2.4.6]
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:235) [groovy-2.4.6.jar:2.4.6]
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:247) [groovy-2.4.6.jar:2.4.6]
at com.website.UserRole.create(UserRole.groovy:55) ~[main/:na]
How do I get past his error? I have no idea what this particular error means :/
It looks like a User had a validation error and didn't save. You're using the 2-arg constructor, but if there are more required properties than just username and password, validation will fail and save() will return null.
If you change the code to create a user to
User user = new User(..., ...)
user.save()
if (user.hasErrors()) {
println user.errors
}
then you'll see what's wrong. Add missing properties after the constructor call:
User user = new User(..., ...)
user.fullName = '...'
or switch to the traditional Map constructor to initialize it in one line:
User user = new User(username: ..., password: ..., fullName: ...)

populating own error-messages to the grails domain errors

I'd like to know, if (and how) I could append some own error-messages to the domain-object after (or before) a validation.
My intention is, I have to check the uploaded file in a form for some attributes (image size etc.) and if something is wrong, I would like to add an error-message which is displayed in the usual grails ".hasErrors" loop.
(And I think I need to have the possibility to express errors in some cross-domain check failure...)
Thanks in advance,
Susanne.
You can add custom validation errors as described in the errors docs as follows:
class SampleController {
def save() {
def sampleObject = new SampleObject(params)
sampleObject.validate()
if(imageSizeIsTooBig(sampleObject)) {
sampleObject.errors.rejectValue(
'uploadedFile',
'sampleObject.uploadedFile.sizeTooBig'
)
}
private def imageSizeIsTooBig(SampleObject sampleObject) {
// calculation on sampleObject, if size is too big
}
Perhaps, you could even handle your case with a custom validator, so you can call validate() one time and be sure, that all constraints are fulfilled.
Here is a real example with a custom domain error:
def signup(User user) {
try {
//Check for some condition
if (!params.password.equals(params.passwordRepeat)) {
//Reject the value if condition is not fulfilled
user.errors.rejectValue(
'password',
'user.password.notEquals',
'Default message'
)
//Throw an exception to break action and rollback if you are in a service
throw new ValidationException('Default message', user.errors)
}
//Continue with your logic and save if everything is ok
userService.signup(user)
} catch (ValidationException e) {
//Render erros in the view
respond user.errors, view:'/signup'
return
}
}

Writing an Integration Test in Grails that will test DB persistence

My Integration-Test for my grails application is returning a null object when I try to get a domain object using grails dynamic get method.
This is a simplified example of my problem. Lets say I have a controller TrackerLogController that uses a service TrackerLogService to save an updated Log domain for another Tracker domain.
Domain Tracker:
class Tracker {
int id
String name
static hasMany = [logs: Log]
}
Domain Log:
class Log {
int id
String comment
static belongsTo = [tracker: Tracker]
}
Controller TrackerLogController save:
def TrackerLogService
def saveTrackerLog() {
def trackerId = params.trackerId
def trackerInstance = Tracker.get(trackerId)
Log log = TrackerLogService.saveTrackerLogs(trackerInstance, params.comment)
if( log.hasErrors() ){
//render error page
}
//render good page
}
Service TrackerLogService save:
Log saveTrackerLogs( Tracker tracker, String comment) {
Log log = new Log(tracker: tracker, comment: comment)
log.save()
return log
}
So now I want to write an Integration-Test for this service but I'm not sure if I should be writing one just for the simple logic in the controller (if error, error page else good page) I would think I would write a Unit test for that, and an Integration-Test to check the persistence in the Database.
This is what I have for my Integration-Test:
class TrackerLogServiceTests {
def trackerLogService
#Before
void setUp(){
def tracker = new Tracker(id: 123, name: "First")
tracker.save()
//Now even if I call Tracker.get(123) it will return a null value...
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(123) //I have tried findById as well
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
So for this example my trackerInstance would be a null object. I know the Grails magic doesn't seem to work for Unit tests without Mocking, I thought for Intigration-Tests for persistence in the DB you would be able to use that grails magic.
You can't specify the id value unless you declare that it's "assigned". As it is now it's using an auto-increment, so your 123 value isn't used. It's actually ignored by the map constructor for security reasons, so you'd need to do this:
def tracker = new Tracker(name: "First")
tracker.id = 123
but then it would get overwritten by the auto-increment lookup. Use this approach instead:
class TrackerLogServiceTests {
def trackerLogService
private trackerId
#Before
void setUp(){
def tracker = new Tracker(name: "First")
tracker.save()
trackerId = tracker.id
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(trackerId)
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
Also, unrelated - don't declare the id field unless it's a nonstandard type, e.g. a String. Grails adds that for you, along with the version field. All you need is
class Tracker {
String name
static hasMany = [logs: Log]
}
and
class Log {
String comment
static belongsTo = [tracker: Tracker]
}

Save On My Domain Class Object Is Not Working.

I have a User class which has a List field namely pt. This field is not initialized when User register his account. But when user goes this controller action :
def updatePt() {
//performs some action
def user = User.get(springSecurityService.principal.id) //find the user
user.pt = []
//on certain conditions i put values into user.pt like this
user.pt << "E"
//at last I save it
user.save()
}
But using user/show action via scaffolding I found that pt field is not saved on users object. Where I'm making a mistake?
You have to provide a static mapping in the Users domain class so that Grails knows the field must be persisted:
class User {
static hasMany = [pt: String]
}
It's possible because of validation error. Try with
if (!user.save()) {
log.error('User not saved')
user.errors.each {
log.error('User error: $it')
}
}
PS or you can use println instead of log.error

Resources