springSecurityService returns a null user on load - grails

Occasionally this code returns a null user object (line 1), even though the springSecurityService.principal.id is a valid id. Line 2 throws a null pointer exception because the user object is null. Most of the time the code works, but I can't figure out what causes the null load.
User user = User.load(springSecurityService.principal.id)
def contactInstance = BasicProfile.findByUser(user)
Attached is a screenshot of the user object after the call in line1.
Could it be due to a timeout in the session? And if so why wouldn't the controller get redirected to the login page if that were the case?

I ran into a similar issue with Tomcat caching some data incorrectly.
Here is what I did:
Stopped Tomcat
Deleted all the files in Tomcat's Temp directory
Restarted Tomcat
After that, it worked fine.
Let me know if this helps.

Related

Thymeleaf: Occasional error using #session - Exception evaluating SpringEL expression

We are using Spring webflow + ThymeLeaf and trying to access session.getAttribute() in html page.
Bit new to Thymeleaf and I understand Thymeleaf has 2 ways to address session viz. ${session.something} and ${#session.getAttribute('something')}.
Code we are using is something like below which fails occasionally.
<div th:if="${(#session.getAttribute('booleanAttribute'))}">
...
</div>
In local environment I never see the failures, and it works as expected. In production this fails appox. 200 times in 30 minutes with following error -
org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "(#session.getAttribute('booleanAttribute'))" (template: "base" - line 80, col 10)
I am little reluctant to place null checks to see if (#session) is null or not without understanding why working locally fine. So I have this question -
What might be wrong above and how can I reproduce locally so that I can confirm fix I am placing will work across all environments?
According to the docs:
#session : direct access to the javax.servlet.http.HttpSession object associated with the current request.
In my testing, #session is null when the session expires. In cases where a user has their session expired, using #session would throw a null point exception (Method call: Attempted to call method getAttribute(java.lang.String) on null context object). You should be able to test this by deleting your JSESSIONID cookie.
${session} on the other hand is a SessionAttributesMap, which appears to never be null -- even when there isn't a valid session. In that case, the expression ${session.booleanAttribute} would still work, just evaluate to false.

Generate unique login string based on username + integer fails, why?

We are using this code in a Ruby on Rails 4.2 project to generate a unique username. The method below is an instance method of a User class and receives a name string and -if required to avoid collisions- appends an integer. It then returns a unique login to be used e.g. like
self.login = self.generate_unique_sane_login(self.name)
in a before_save hook or in a controller. In particular, this is called when a new user instance is created, after registration.
However, once in a while this function fails. This method returns a login that already exists, the database driver then throws a uniqueness violation exception (Postgres in this case) and the application throws a HTTP/1.1 500. Or it doesn't even do that, we have several duplicates in the database that were definitely created using this code but do not validate once loaded again. Those users are then unable to edit their profile.
We are not seeing multiple simultaneous (or almost simultaneous) user registration attempts in our logs when this happens so I don't think this is a race condition issue.
So: why does this happen? Am I missing something? What would be a better solution to generate a unique user login string?
def self.generate_unique_sane_login(l)
l = l.gsub(/\#.*$/, '')
l = l.gsub(/[^A-Za-z0-9äöüÄÖÜß]/, '_')
c = 1
login = l
conflicting_logins = self.where("LOWER(login) = LOWER(?)", login)
until conflicting_logins.empty?
login = "#{l}#{c}"
conflicting_logins = self.where("LOWER(login) = LOWER(?)", login)
c = c+1
end
login
end
Answers to comments
Thank you for your comments. Here are my answers.
We already use uniqueness validator and it doesn't catch these duplicates for some reason. Other duplicates are caught (also all are caught in tests), and these exceptions are not repetable (meaning if we register the same login that threw an exception ten minutes later it works suddenly).
I would prefer understanding existing code to refactoring the whole authentication process, so other gems or replacement code is not an option right now.
Using random values as the integer part is an idea but makes the logins very long, which I would like to avoid since they are user visible. Using a counter should work, shouldn't it?

Rails 4 session.id occasionally nil

I'm running a simple website on Heroku and I'm noticing something strange occurring when I'm running the app. It appears that approximately 50-60% of my users are reporting a nil session_id when it gets logged in my database.
I'm using active_record_store for my session handler and Postgres as my db server. I've gotten similar results using the cookie_store, so i'm not sure what i'm doing wrong. The only guess I have is that the first request a user makes, the id might not be populated yet. The sessions table has the correct number of entries, but my tracking table does not.
Example Code
class CaptionController < ApplicationController
def index
#image = Image.order("RANDOM()").first
Tracking.log(session.id, Tracking::VIEW_CAPTION_ON_IMAGE, #image.id)
end
The code above results in 50% of the time, the session being nil in the table it logs to.
I found the answer, it looks like Rails is trying to be efficient by only creating a session if there is something to store. So accessing the session.id without storing something doesn't return consistent results.
You need to force the session to be created by storing something in it.
TLDR: Add this somewhere before you access the session ID.
session[:foo] = "bar"
Source: http://www.gani.com.au/2013/08/force-session-creation-in-rails/

Tire Elastic search: Deleting a record from model and redirecting immediately throws error

I am having a model named User. When I destroy a user and redirect to the User's index page, it throws an exception
ActiveRecord::RecordNotFound: Couldn't find all Users with IDs (1, 200) (found 1 results, but was looking for 2)
But when I refresh the page, the page loads properly and displays all the users. The problem I guess is Tire::Model::Callbacks after_destroy is taking some time to execute and if I call User.search_all within that time period the result is searched including the destroyed id.
I am looking for a clean approach to overcome this problem. Or does Tire have any option to handle this problem?
Thanks in advance.
send a refresh call to Elasticsearch before you invoke User.search_all. Elasticsearch refresh_interval defaults to 1 second, so if you delete from it and do a search under a second, it might not have a chance to remove the deleted record before return results to you.

Setting Session Variables with AJAX and Rails when appcache is present

Have an ajax call to "updateUser" which does this:
puts session[:user_id]
user = User.find(params[:user_id])
if user
session[:user_id] = user.id
session[:user_name] = user.first_name + " " + user.last_name
puts session[:user_id]
render text => "Success.
end
The first puts shows the original user_id and the second shows the new user_id, so it would appear to be working properly. However, when I navigate to another page, all the session information is still that of the original user_id. What have I done wrong?
I have a feeling it has something to do with the local session cookie not being updated.
UPDATE
Definitely has something to do with caching. I can go to the page, clear the browser cache (am using Chrome as my browser), then run the ajax call and it works properly once. After that I am locked in to the (new) old user again.
UPDATE 2
Looks like it is something specifically to do with html5 application-cache. If I kill the appcache or run the script from a page that does not include manifest it works just fine. Still can't get it working properly on the cached page.
The same session id is being sent to the server from the cached page as the non-cached page, and the response headers are identical. But each request from the locally cached page causes the server to start with old session information.
http://diveintohtml5.ep.io/offline.html
I can tell that you've got a manifest caching problem, and altering the session itself is not going to clear the manifest. The cache is persistent until such time as the cached item is de-cached or the manifest is invalidated.
Another user ran into this same issue in a different way: they passed their session data in the URI and ended up caching a new application each time the user visited. Their solution may be useful:
How to clear Application cache (HTML5 feature) using JavaScript?
You might also take a look at this, on the various storage caches:
http://sharonminsuk.com/blog/2011/03/21/clearing-cache-has-no-effect-on-html5-localstorage-or-sessionstorage/
And finally, a resource on updating a cache file with JS:
http://www.html5rocks.com/en/tutorials/appcache/beginner/
This last one I would use after checking if the session ID has changed: update the session ID, then confirm the change, then clear and re-download the cached files.
Good luck. I hope that helps some.
The problem is that the session information is stuffed inside the application cache somewhere, All requests sent to the server are sent using that session info (which was cached on page load). So, we need to update the application cache with window.applicationCache.update() after the successful ajax call. This will cause the next request sent to the server to have the updated session information and all is well.
$.ajax({url: "/contoller/update_logged_user",
data: {id: user_id},
success:function(){
window.applicationCache.update();
}})
I encountered a very similar problem... had some code to store a user's zip code in session[:zip] when provided with an ajaxSubmit'ed form. Modified the implementation only slightly and suddenly session[:zip] had amnesia. Storing the info in cookies[:zip] worked properly. Path of least resistance.
Would you try setting the session[:user_id] = nil before assigning it with another value user.id and see what happens?

Resources