I'm looking at implementing some form of anonymous user system in Rails. I need to let people do things (creating records, looking at what they've created, etc), without actually creating an account. Once they create an account, everything persists without risk of losing it by clearing cookies or something.
Right now, I'm thinking it's pretty straightforward. Have an is_anonymous field in the User model, and use something like this to access the currently logged in user:
def find_user
session[:user_id] ||= create_new_anonymous_user.id
end
Assuming the session persists for some reasonable period of time, and the session cookie doesn't expire, that should keep everything running smoothly.
However, there is this piece of me that is convinced that I'm missing something security-related. Has anyone done something like this before? Am I missing something super-obvious?
Thanks!
The only real security issue is going to be if these anonymous users can perform critical operations.
Your system means that anyone with the specific cookie will gain access to the site. Not necessarily a big deal, but it really depends on the type of information your users are providing.
I have done something similar in the past (in my case I was tracking progress through a site and when the user logged in or registered, I attached the "guest" data to their account. When you do the switch, make sure you delete the anonymous record to prevent further access and it should be fine.
I just found a pretty cool example of "trial users" using Authlogic: http://github.com/gisikw/authlogic_trial
Assuming the session persists for some
reasonable period of time, and the
session cookie doesn't expire, that
should keep everything running
smoothly.
Perhaps you should set a separate long lived cookie for the new user, so they can have multiple sessions (at least from that browser).
Are you sure that you want to let people create objects that are tied to accounts that may not exist? Unfortunately I don't know much about what your application is actually doing but I would think that going down this path might leave you with a bunch of orphaned objects not really "owned" by any real users.
If you really do want to do this I think what you have is decent. You could be creating a real user, flagged as "guest" (or whatever) and once the user wants to really register they are prompted for other information and unflagged. You should add access control for guest vs non-guest, etc.
Related
I have ror app where i can poll. How to remember user without registration? That he could not vote many times for the same answer.
What should I use? I need to create a new model for users? Maybe use some gems?
Every visitor to your Rails website will be given a session, you can just store some state in there like: session[:seen] = true and in subsequent requests you can check for the existence of that session variable.
See here for more and especially note that a user can always thwart this effort by clearing their cookies (which is why it would be better to use registration).
I might be approaching this problem the wrong way ... so if you have a more elegant solution I'm all ears.
Imagine I'm making a system like Kickstarter. I want my users to be able to specify how much they want to pledge before I ask them to sign up.
Then, if they're not registered I need them to sign up before putting them back in the flow that they would have been on had they just signed in. Devise makes this easy by redirecting a user back to the after_sign_up_path_for which ends up being after_sign_in_path_for by default.
So this will always issue a GET request. But if I have data that I received from the POST with the amount they wanted to pledge, but that's lost.
Is the only way to do this to store that posted data in the session? Or is there a clever way to start creating the pledge record without the user (without needing to run jobs to destroy orphaned pledge records)?
I found the approach described in this blog post over at highgroove.com quite interesting in this regard:
http://highgroove.com/articles/2012/10/09/lazy-user-registration-for-rails-apps.html
The basic idea is to always have an anonymous user at hand, even if the current vistor is not registered. Like this you can create e.g. associations as usual and — once the visitor actually does sign–up — you edit the user rather than all associated objects.
If the user does not ever register, you can simply look for abandoned user accounts and delete them including their associations, rather than look for all kind of abandoned models.
Is it possible to show "Already Logged In" message if that user is logged on one of the machine.
This thing is possible using database.
But i had implemented sessions for login and logout,i think it isn't possible with sessions to show "Already Logged In".
Please suggest best way for this.
If they go to another computer? If you are using session[:something] - it is stored in the browser. For anything to be stored across a different browser, you have to use a database for state.
I think it is possible, but that being said, I don't like it. I take it you are concerned because you can't easily identify a user from the session data?
Make sure the User model is updated every time a user accesses the application by using a before_filter in the application controller.
During the login process, you can query "logged in" users by specifying a time limit that is the same as the session clean up time limit.
You can do something like this :
User.where("email = ? and updated_at >= ?",login_email,20.minutes.ago)
If you want to avoid concurrent logins for the same username. The best implementation can be achieved by adding a column to the users table that stores the ip address of the logged in user.
Otherwise you can change the session store mechanism to use either of:
ActiveRecordStore
FileStore
DRbStore
MemoryStore
in addition to the default CookieStore.
I'm using devise/cancan for my app and everything is pretty sound -- provided a user creates an account and signs in.
What I'd like to do is allow a user to get started without creating an account. And then sign up if they want to actually save their work.
Has anybody come across this before? Should I be figuring out how to create dummy accounts with devise? Or allowing unauthorized users access to creating models in my app via CanCan?
I could go into detail about how I've been thinking about approaching this, but it feels like a pretty obvious use case that somebody has come up with a nice solution for.
Thanks in advance,
Mike
If you go with creating dummy accounts, you would have to track the user somehow via a cookie and cache the values in that cookie in your db. Cancan does allow for guest accounts via the ability model. For example:
user ||= User.new # Guest user, for users who are not registered or don't have an account yet
Which is enough you to you started with applying permissions for non registered users. Note though, tracking by cookie alone is not very reliable and can lead to some type of security hazard (i.e. by means of cookie hijacking). User, one day, can also decide to clear out his cookies.
If need be, I would suggest letting the user do minimal interaction with a guest account and motivating the user to sign up / register with Devise as much as possible.
Hope that helps!
I actually am considering the same problem, I have a scheduling app that makes a calendar. To get over the problem I'm thinking that you use
user ||= User.new
Like was suggested above and using cookies to get the data to the database once the user creates an account.
This would mean that you would not have to worry about clearing out cookies because they would create an account if they want to save data.
so I have designed this voting thing which does not let somebody vote for the same article twice in 24 hours. However, suppose a person votes and after seeing the person was able to cast vote or that he is falling in that 24 hour window, I disable the vote-casting button (and this is all Ajax btw).
But what to do when a person closes his/her browser and comes back up or even refreshes the page? Obviously, he would not be able to cast vote, because of my algorithm, but the person would still end up succeeding in making call to the server. So if he really wanted, he would keep refreshing the page and clicking on the vote and put unnecessary load on the server. How to avoid that by doing some sort of client-side thing or something?
I am using ASP.NET MVC, so session variables are out of question.
Am I being over-concerned by this?
If voting happens only from logged in (known) members then you shouldn't have any problem.
If, on the other hand, everyone can vote then you need to store all user vote events:
timestamp
poll
poll_vote
ip
user agent
user uniqueness cookie
So you'll need a random hash sent out as cookie. This will ensure that you don't accept another vote for the same poll from the same person.
If the user deletes his cookies you fallback to plan B, where you don't allow more than (say) 10 votes from the same IP and user agent combination for 24 hours.
The system is not perfect since users can change IPs and (more easily) user agents. You'd need advanced pattern detection algorithms to detect suspicious votes. The good thing about storing all user vote events is that you can process these later on using a scheduler, or outsource the votes to someone else who can process them for you.
Good luck
Refreshing is not a problem
If you're doing all this voting using Ajax, refreshing a page won't do anything except load the page using GET.
If you're not using Ajax you should make sure you call RedirectToAction/RedirectToRoute action result, that would as well help you avoid refresh problems.
How do you recognise users
If you use some sort of user authentication this re-voting is not a problem. But if your users are plain anonymous, you should store IP address with your votes. This is how things are usually done. This makes it possible to avoid session variables as well. But you have to be aware of this technique because it's not 100% perfect.
Cookies?
You could of course also use absolute expiration cookies. They'd expire in an day. Advanced users would of course be able to avoid your voting restrictions, but they would be able to avoid other ways as well. Sessions BTW are also based on cookies anyway.
Combination
But when you'd like to make you system as great as possible, you'll probably use a combination of the above.
The best way would be to track who voted for what and when on the server (probably storing it in a database). In order to do this you must use an authentication system on your site (probably forms authentication) to identify users. So every time someone tries to vote you check first in your data storage if he already voted and when and decide whether to validate the vote or not. This is the most reliable way.
If your site is anonymous (no authentication required to vote) then you could store a persistent cookie on the client computer that will last for 24 hours and indicate that a vote has already been cast from this computer. Remember though that cookies might be disabled, removed and are not a reliable way to identify a given user.
I am using ASP.NET MVC, so session
variables are out of question.
Any reason for that? Sessions are perfectly fine in ASP.NET MVC applications. It is in your case that they won't work because if the user closes the browser he will lose the session.
Obviously, he would not be able to
cast vote, because of my algorithm,
but the person would still end up
succeeding in making call to the
server. So if he really wanted, he
would keep refreshing the page and
clicking on the vote and put
unnecessary load on the server
Automated bots could also put unnecessary load to your server which is much more important than a single user clicking on F5.
If you just want to ensure the user can only vote once on an article then you just need to store a Set (i.e. HashSet) of all article id's that they've already voted on, then just check before allowing the vote.
If you still wanted a 24hr limit then you need to store a Dictionary<articleId,DateTime> then you can check if he has already voted for that article and if he has when it was.