Ruby on rails, cancan and default role assignment - ruby-on-rails-3

I have built a small ruby webservice, in this I have implemented cancan authorization.
I followed this tutorial. The problem is that, I can't find out the way to assign at the user, when they do the registration to my site, the base role level.
I find out to do this with a checkbox, but it's not what I want. My idea was to put this assignment directly into the registrations_controller, but I failed to save the role.
I hope that somebody can help me.
Thank you.

This is what worked for me
user.rb:
after_create :default_role
private
def default_role
self.roles << Role.where(:name => 'User').first
end

I had the same problem, but I am using embedded association from rbates:
http://railscasts.com/episodes/189-embedded-association
user.rb:
before_create :default_role
private
def default_role
self.roles = ['client']
end
Works like a charm, but pay attention that the hook is before_create, not after_create, because the before_create runs just before the insert operation.
The after_create is after the insert operation, which in my case is late.

I have rebuild the migration, I have unified the user and role tables, so now I can assign all without problem.
Thank you.

Related

Rails: I need different AdminUsers to have different permissions

So I'll go straight to the point.
I'm using the ActiveAdmin gem, so I have the AdminUser model in my app... now I got a requirement from my client where a "super admin" must be able to control the permissions of other administrators.
So, for example, if I have the resources: Message, Client and Country, the "super admin" should be able to assign to an AdminUser the task of managing messages, to another one the task of managing clients and to another one the task to managing countries.
For this I was thinking about adding several boolean attributes to the admin_users table. For example, a boolean attribute called "super_admin" would be used to determine if this AdminUser can change the permissions of other AdminUsers, another attribute called message would be used to determine if this AdminUser has control (can edit, read, delete, etc.) over the messages, another attribute called country would be used to determine if this AdminUser has control (can edit, read, delete, etc.) over the countries and so on...
What's the problem? I can't access to current_admin_user in models, so I can't do something like this:
ActiveAdmin.register Message do
if (current_admin_user.message)
permit_params Commune.attribute_names.map(&:to_sym)
end
end
So what can I do? I must build this functionality!
edit
I found this gem that adds roles to active admin https://github.com/yhirano55/active_admin_role
why it needs to be in model ? Code looks like it should be placed in controller, permit_params...
I would use pundit. I can see can can was updated 5 years ago. They are similiar.
pundit repo: https://github.com/varvet/pundit
It uses policies, so you create policy for every model.
class PostPolicy < ApplicationPolicy
def update?
user.admin? or not record.published?
end
end
where you can check your flags on update or create or show or anything...
In action you use something like this authorize #post, :update?
Quote from their doc
In this case, you can imagine that authorize would have done something like this:
unless PostPolicy.new(current_user, #post).update?
raise Pundit::NotAuthorizedError, "not allowed to update? this #{#post.inspect}"
end
Hope it will help
P.S It you need more complex solution. I would create Role model, where I could specify model, read, write permissions. I would link it with my user to has_many roles, and in my policy do something like this:
class PostPolicy < ApplicationPolicy
def get_role
user.roles.where(model: "Post").first
end
def update?
user.get_role.write? or not record.published?
end
end
Or maybe there is better way to use it somehow in policy model...

Assigning a random password while using Devise

Here's a breakdown of the situation -
Desired behavior: user can sign_up through Devise gem by providing their email address only. Web app generates and db stores a temporary password, unknown to user.
Logic: This is meant to be a 'gradual sign-up' process to a web application still under construction (we want to begin capturing potential users without providing access to the web app as it's still in partial development). The email is to be used for communication purposes until final release.
Problem: Devise gem requires user to input email && password during sign_up process. We've found no obvious way to circumvent the dual requirement. User failing to provide password generates error.
Potential solution: After searches and many tries, this seems to be the closest alternative (found here).
generated_password = Devise.friendly_token.first(8)
user = User.create!(:email => email, :password => generated_password)
Question: While this potential solution makes sense, we're REALLY new to this and don't understand in which file to place this code within the Devise configuration, and how to call it.
All help appreciated.
LM
OK, I kept digging until I found what I was looking for (here) - maybe it can help you too.
In your model:
before_validation :generate_password, :on => :create
def generate_password
o = [('a'..'z'), ('A'..'Z'), (0..9)].map{|i| i.to_a}.flatten
self.password = self.password_confirmation = (0..16).map{ o[rand(o.length)] }.join if self.password.blank?
end

How to use 'Has_secure_password', when trying to refactor?

I am trying to refactor the railstutorial authorization mechanism.
My version of rails is 3.2.0 and I am using ruby 1.9.3-p0 and postrgresql 9.1.
So far, my tests are passing when it comes to a failed attempt to sigin but the successfull sign in fails.(The reason is that I have to refactor the old signin mechanism)
Here is my session helpers sign_in function:
def sign_in(employee)
cookies.permanent.signed[:remember_token] = [employee.id, employee.salt]
self.current_employee = employee
end.
One problem I see immediately with the sign_in function is that has_secure_password already takes care of the encryption and salt etc ... my thinking was that maybe I should use password_digest instead of employee.salt, but that failed as well.
I would like to have my cookies expire after two hours. I found this option at api.rubyonrails.org under cookies.
cookies[:key] = {
value => "employee.id, employee.salt",
expires => 2.hours.from.now
}
Another question I have has to do with the fact that has_secure_password already has an authenticate method so that means that I do not have to use the authenticate definition defined in the employee model,(user model) in the rails tutorial, but when I comment it out I get a flag reading:
NoMethodError: undefined method 'authenticate'
Here is my session controllers create action:
def create
employee = Employee.authenticate(params[:session][:email],
params[:session][:password])
if employee.nil?
flash.now[:error] = "Invalid email/password combination."
#title = "Sign in"
render 'new'
else
sign_in employee
redirect_back_or employee
end
end
It seems the Employee.authenticate is a problem.
So I essentially have three question and they are as follows:
In the rails tutorial we go through a fairly lengthly process of encrypting and applying salt etc to the employees password. Since has_secure_password has this already taken care of, what variable would I pass to my functions or arguments that would capture the encrypted password?
The next question has to do with the expiration of the cookie, and how I would use that in the sign_in function?
Lastly, how do I use the authenticate method so that rails recognizes it as a genuine method?
Just for the record, I have searched through railsguide, api.rubyonrails.org and other questions asked on SO that are similar to this one. Of course this merely points up my lack of understanding of the principles, but I am learning and do take direction well.
Thanks for any thoughts, suggestions and or resources you might share with me.
Update
I re-read the api on has_secure_password and authenticate takes only one argument, namely an unencrypted password ... so I have something to work with.
I still need any help or thoughts or suggestions that you might offer ... thanks.
update
I found this article that deals with session timeouts:
http://madkingsmusings.blogspot.com/2011/05/session-timeouts-on-rails.html
I am still working to see if I can get it to work for me, but it is tailored for the railstutorial.
As for the other questions, Michael Hartl is busy pushing out the second edition of Ruby on Rails tutorial and in that edition he will be dealing with has_secure_password.
The new version of the railstutorial is available.
For the first and last question question... You'll find the authentication extremely simple.
In the User model:
has_secure_password
In the SessionController:
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_back_or user
else
flash.now[:error] = 'Invalid email/password combination'
render 'new'
end
end
In the SessionsHelper:
def sign_in(user)
cookies[:remember_token] = user.remember_token
current_user = user
end
It should have been obvious but I didn't even think about looking for the code on github. Maybe someone else will appreciate the link.
Here is Hartl's 2nd edition Sample_App source on github

Rails 3 + Devise: How do I set a "Universal" password?

I'm using Rails 3 with Devise. Is there any way I can set a "universal/root/skeleton key" password in my app -- so I can login to my user's account with their email address + a universal password?
p.s: This is probably a really bad authentication practice, but for some reason I need to edit some of my users.
What you want is highly NOT recommended.
The way to do it is define Roles for your users, and add an interface from which a user with a certain role can edit something.
If you still want to do it your way, probably the best way to do it would be to extend DatabaseAuthenticatable like this
module Devise
module Models
module DatabaseAuthenticatable
def valid_password?(incoming_password)
password_digest(incoming_password) == self.encrypted_password or incoming_password == "your_universal_password_here"
end
end
end
end
you can put this in your initializers folder (create for example an add_universal_password.rb file, and write that down)
But I say again, this idea is not ok
Extending DatabaseAuthenticable as in the answer by Andrei S is a bit brittle, because it makes your code assume some implementation details of Devise's valid_password? method. A less brittle way would be to override the valid_password? method in the model that mixes in DatabaseAuthenticatable (e.g. User) and call super(), like this:
class User < ActiveRecord::Base
devise :database_authenticable
...
def valid_password?(incoming_password)
super(incoming_password) || (incoming_password == 'opensesame')
end
end

How to access devise current_user from model

Just start to develop with devise for my app authentication, however running into trouble with accessing current_user from a model. I have no problem accessing this from a controller.
For my scenario I need to get the current user id to keep track who has edit the blog post, which I save the user id with before_save filter.
Appreciate your help!
Thanks
You can only pass it down in a parameter.
class SomeModel
def my_nifty_method(user)
end
end
in controller:
class SomeModelsController < ApplicationController
def some_method
sm = SomeModel.new
sm.my_nifty_method(current_user)
end
end
You should be able to do this in your model. 'c' here being the model object and setting it's user_id attribute before create, or save in your case.
before_create {|c| c.user_id = Authorization.current_user.id}
I'm somewhat new to RoR so be warned.
Why are you trying to access to that method in the model?
You can access to the ID attributes via self.id (within the model)