Grails jms and atmosphere plugin null springSecurityService injection in services - grails

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?

Related

Grails: Grails3 : doWithWebDescriptor?

I am trying to get further into grails3 and I am unsure about plugin descriptor and doWithWebDescriptor:
src/main/groovy/grails/plugin/plugin/PluginGrailsPlugin.groovy
def doWithWebDescriptor = { xml ->
def listenerNode = xml.'listener'
listenerNode[listenerNode.size() - 1] + {
listener {
'listener-class'(someClass.name)
}
}
}
I tried grails install-templates under grails 3 and no web.xml was generated... I also had a look at the default generated plugin descriptor which did not appear to have doWithWebDescriptor...
Was wondering if this has changed - is it no longer producing a web.xml or if it is what should I be doing to register a listener under grails 3 .
I have managed to get default tomcat websocket listener to work via a spring boot grails app:
It is documented here:
https://github.com/vahidhedayati/testwebsocket-grails3
I have decided to update this post and include all my findings so far on this matter.
More specifically the application.groovy inside your application grails-app/init folder:
This bean initiates default tomcat websocket listener:
#Bean
public ServletListenerRegistrationBean<AnotherWebSocketHandler> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<AnotherWebSocketHandler>(new AnotherWebSocketHandler());
}
Whilst messing around to reuse in plugin, the findings are:
The above project is a basic grails application which does 2 things, a basic spring socket as well java 1.X Websocket:
Here is how to use Default websocket in a grails 3 plugin
In you plugin descriptor you have something like this:
Closure doWithSpring() {
{->
wsChatConfig DefaultWsChatConfig
}
}
In this plugin I have left both methods of initiating the listener:
#Bean
public ServletContextInitializer myInitializer() {
return new ServletContextInitializer() {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(WsCamEndpoint)
servletContext.addListener(WsChatFileEndpoint)
}
}
}
// Alternative way
#Bean
public ServletListenerRegistrationBean<WsChatEndpoint> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<WsChatEndpoint>(new WsChatEndpoint())
}
The top method came in very handy since you can only initialise 1 ServletListenerRegistrationBean and I had to resort to the top method to enable other listeners... I could have just used the top primary for all the calls. Left in for future reference..
With this in place, spring boot now emulates the same as web.xml would when registering a listener. The actual groovy classes that load the websockets from there are as they were i.e. using default websocket calls such as onOpen onMessage etc..
From Grails 3 there's a new way of adding runtime configuration using spring registration beans in the Plugin method doWithSpring. doWithWebDescriptor is not used anymore.
This should work for the Servlet Listeners:
Closure doWithSpring() {{ ->
MyListener(ServletListenerRegistrationBean) {
listener = bean(someClass)
order = Ordered.HIGHEST_PRECEDENCE
}
}}
Disclaimer: I didn't test this code.
Refer to the Gails documentation.

Not being able to inject services into AtmosphereHandler

I am running Grails 2.2.1.
I have a Grails service, which acts as an atmosphere handler..
Refer this link: https://bitbucket.org/bgoetzmann/grails-atmosphere-plugin/wiki/Home
I am trying to use spring security service inside this, in your simple way, ie, by injecting it via def springSecurityService.
But when a service hits the handler,
springSecurityService.getCurrentUser() returns null.
User is logged in, I am able to get him in my controllers. I think the service is not injecting somehow.
After some research I came across this question
Injecting service into Grails Atmosphere Handler class
but both answers are outdated...
Please help!
EDIT: my service goes like:
AtmosphereRequest req = r.getRequest();
if (req.getMethod().equalsIgnoreCase("GET")) {
log.info "got get, and suspending."
r.suspend();
} else if (req.getMethod().equalsIgnoreCase("POST")) {
def data = req.getReader().readLine().trim()
log.info "got some data:\n $data"
if (data == "GET_NEARBY"){
log.info "finding nearby..."
def user = springSecurityService.getCurrentUser()
log.info "user is $user" //USER IS NULL HERE
//...some logic
}
}
Try this:
def ctx = ServletContextHolder.servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
def springSecurityService = ctx. springSecurityService
def currentUser = springSecurityService.currentUser
now you should be able to use springSecurityService

Logging specific request header using spring security events

In my grails application, failed login attemps get logged using spring security events as shown here http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/single.html#7.3%20Registering%20Callback%20Closures
My issue has to do with client ip retrieval. Normally, calling getRemoteAddress from details object of the event should do the job, but my case is that my application is behind a reverse proxy therefore i should get the ip from request header X-Forwarded-For.
Neither event object nor application context parameters of the closuse provide access to the request object. The global request object isn't available either.
Any ideas how to get access to headers or any other way to implement this functionality?
You can get it from RequestContextHolder, if it exists:
GrailsWebRequest request = RequestContextHolder.currentRequestAttributes()
request.getHeader("X-Forwarded-For")
Generally, as you probably know, it isn't considered a very good idea to access the web session from within Services. First of all, you break the abstraction and separation of service logic, and requests might not always be available or associated with the current thread. One way to access the session from a service is to encapsulate the HTTP session in the following manner:
class WebUtilService {
void withSession (Closure closure) {
try {
GrailsWebRequest request = RequestContextHolder.currentRequestAttributes()
GrailsHttpSession session = request.session
closure.call(session)
}
catch (IllegalStateException ise) {
log.warn ("No WebRequest available!")
}
}
}
and you would use it like this:
class MyService {
WebUtilService webUtilService
void doSomething() {
webUtilService.withSession { HttpSession session ->
log.info(session.myValue)
session.newValue = 'Possible, but should be exceptional'
}
}
}
where you could have access to the getHeader() method.
Disclaimer: the code is from Marc-Oliver Scheele's blog.

Accessing flow scope in grails service

I have and WebFlow in my controller, and simple flow-scoped service. Somewhere close to the end of my web flow, I need to validate my command object field against value I received earlier in the web flow. For this I created a simple flow-scoped service:
class EventFlowService implements Serializable {
static transactional = false
static scope = "flow"
Date getEventStartDate(){
flow.basicData.eventDate
}
}
I don't need my service anywhere else than in a command object, so I inject it to my command object, like so:
class EventRestrictionsCommand implements Serializable{
def eventFlowService
boolean onlineRegistration
Date onlineRegistrationEnd
Date onlineRegistrationStart
static constraints = {
onlineRegistrationEnd validator: {val, obj ->
if(obj.onlineRegistration){
return val > obj.onlineRegistrationStart || val <= obj.eventFlowService.getEventStartDate()
}
return null
}
}
}
The problem is that I get exception saying, that there is no flow property in my service. Is there any way I can get access to flow storage in my flow-scoped service?
I met the SAME issue before and worked it out by installing webflow plugin in GRAILS:
grails install-plugin webflow
Say, the new version of grails surports webflow well by installing webflow plugin.

Grails: User evaluator for Commentable with Spring Security plugin

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)
}
}
}

Resources