Role based authorization with cancan doesn't works Rails 4 - Ruby 2.1 - ruby

I use cancan (1.6.10) and devise (3.2.2), I have been implementing authorization using this guide as recommended by cancan's author, I need to assign multiple roles to a user then I decided store it into a single integer column using a bitmask (I added a column named "roles_mask" to user model).
I have this files involved:
user.rb
edit.html.erb
I'm aware that I folowed word by word of this guide except the lines that I wrote for indicate that roles is a accessible attribute:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:email, :password, :password_confirmation, :current_password, :roles) }
end
end
When update a user (logged), all fields are updated except :roles_mask =/, I don't understand why roles field is not captured. I think there's something that I don't achieve to see. anyone can help me?
*Solution:
Using cancancan (support for Rails 4) and change application_controller.rb file it works (as roles is a non-scalar attribute).
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:email, :password, :password_confirmation, :current_password, roles: []) }
end
end

CanCan does not support Rails 4. You can however use CanCanCan, which is Rails4 friendly: https://github.com/CanCanCommunity/cancancan

Related

CanCanCan alias method for a controller

In my application I have permissions set up like so for an admin:
can :read, Journey
can :destroy, Journey
can :update, Journey
And I have controllers like so:
class JourneyController < ApplicationController
authorize_resource class: :journey
def index; end
def show; end
end
module Journeys
class VoidJourneyController < ApplicationController
authorize_resource class: :journey
def show; end
def destroy; end
end
end
This is based on how DHH does his controllers: http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/
Now the issue I have is that because I have a show method inside the VoidJourney controller (this is to show the user some additional information as we talk to an API) it means a user who doesn't have permission to destroy a journey can access it because show is aliased to read and only the destroy is protected in that controller.
CanCanCan has the alias_action method, but that only allows aliasing a method to another for all controllers, not just one.
The only way I could think to handle this was to do:
def show
authorize! :destroy, :journey
end
So that it checks that method against a different permission. But I'd like to avoid having to do that if possible.
Is it possible to alias a method in only one controller to another? And not alias for all controllers. Looking at the docs I can't see this.

Attempting to create a database item using the has_one relationship, no exceptions, but still no item

Models:
A User has_one Ucellar
A Ucellar belongs_to User
I have confirmed from multiple sources that these are set up correctly. For posterity, here is the top portion of those two models.
class User < ActiveRecord::Base
has_many :authorizations
has_one :ucellar
validates :name, :email, :presence => true
This is actually the entire Ucellar model.
class Ucellar < ActiveRecord::Base
belongs_to :user
end
Ucellar has a column called user_id, which I know is necessary. The part of my application that creates a user uses the method create_with_oath. Below is the entire User class. Note the second line of the create method.
class User < ActiveRecord::Base
has_many :authorizations
has_one :ucellar
validates :name, :email, :presence => true
def create
#user = User.new(user_params)
#ucellar = #user.create_ucellar
end
def add_provider(auth_hash)
# Check if the provider already exists, so we don't add it twice unless authorizations.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
Authorization.create :user => self, :provider => auth_hash["provider"], :uid => auth_hash["uid"]
end
end
def self.create_with_omniauth(auth)
user = User.create({:name => auth["info"]["name"], :email => auth["info"]["email"]})
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end
EDIT:
Forgot to summarize the symptoms. On create, the user is in the db, with no exceptions thrown, and nothing to signify that anything went wrong. However, the related ucellar is never created. Per the documentation Here, the create method should create AND save the related ucellar.
It should create ucellar too.
Try to get the error messages after the creation by calling:
raise #user.errors.full_messages.to_sentence.inspect
I'm not sure why this wasn't working, but I ended up just moving this code out of the create action of the user controller, and putting it directly after an action that was creating a user. It solved my issue though. Thanks everyone for your help!

Use CanCan Authorization along with Custom Authentication in Rails 3

I am new to Rails and have been developing an app in rails 3 after following a Lynda.com tutorial where Kevin Skoglund showed us a way to authenticate a user using SHA1 Digest. I used that in my app and there is a need now to put in some Authorization. When I searched around, I found CanCan to be one of the better ones for authorization in rails. However, CanCan seems to be mostly implemented using Devise or Authlogic authentication and not custom authentication.
I wanted to know if it is at all possible to use CanCan if we use custom authentication, like I did. Is so, how to go about getting CanCan to work ?
It looks like CanCan needs some 'create_user' to be present but I am not sure how/where to create it.
Another alternative that I thought would be to put in my custom check on every page to check the user role and redirect them to an error page if they are unauthorized but that seems like a bad way to approach this problem...Your views on this please.
Please let me know if you need any additional information. I am using Ruby 1.9.3 and Rails 3.2.1.
Below is the way I have my current authentication set up. Any help would be greatly appreciated.
access_controller.rb
class AccessController < ApplicationController
before_filter :confirm_logged_in, :except => [:login, :attempt_login, :logout]
def attempt_login
authorized_user = User.authenticate(params[:username], params[:password])
if authorized_user
session[:user_id] = authorized_user.id
flash[:notice] = "You are logged in"
redirect_to(:controller => 'orders', :action => 'list')
else
flash[:notice] = "Invalid Username/password combination"
redirect_to(:action => 'login')
end
end
def logout
session[:user_id] = nil
flash[:notice] = "You have been logged out"
redirect_to(:action => 'login')
end
end
user.rb (User Model)
require 'digest/sha1'
class User < ActiveRecord::Base
has_one :profile
has_many :user_roles
has_many :roles, :through => :user_roles
attr_accessor :password
attr_protected :hashed_password, :salt
def self.authenticate(username="", password="")
user = User.find_by_username(username)
if user && user.password_match(password)
return user
else
return false
end
end
def password_match(password="")
hashed_password == User.hash_with_salt(password, salt)
end
validates_length_of :password, :within => 4..25, :on => :create
before_save :create_hashed_password
after_save :clear_password
def self.make_salt(username="")
Digest::SHA1.hexdigest("Use #{username} with #{Time.now} to make salt")
end
def self.hash_with_salt(password="", salt="")
Digest::SHA1.hexdigest("Put #{salt} on the #{password}" )
end
private
def create_hashed_password
unless password.blank?
self.salt = User.make_salt(username) if salt.blank?
self.hashed_password = User.hash_with_salt(password, salt)
end
end
def clear_password
self.password = nil
end
end
ApplicationController.rb
class ApplicationController < ActionController::Base
protect_from_forgery
private
def confirm_logged_in
unless session[:user_id]
flash[:notice] = "Please Log In"
redirect_to(:controller => 'access', :action => 'login')
return false
else
return true
end
end
end
I recommend first reading or watching the Railscast about CanCan. It is produced by the author of this gem and therefore very informative:
http://railscasts.com/episodes/192-authorization-with-cancan
You can also get help on the Github page:
https://github.com/ryanb/cancan
Somehow, you need to fetch the currently logged in user. This is what the current_user method does, and it needs to be defined on the users controller. Try something like this:
class UsersController < ApplicationController
# your other actions here
def current_user
User.find(session[:user_id])
end
end
Then, you should be able to use CanCan as described in the resources above.

cant use has_secure_password, password_digest error

Good evening. I have a problem. i am using has_secure_password
and cause of this i have an error undefined methodpassword_digest=' for #`,
but i dont have this method!! Please help, dont know what to do. I read how to fix this problem but it didnt help me(
Here is my User model. Please help if you can.
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
has_secure_password
validates_presence_of :password, :on => :create
before_create { generate_token(:auth_token) }
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
You may have forgotten to make sure your migration backing the user model has a column for password_digest. Make sure the column exists and that it's a string. If it doesn't, create a migration to add the column.
Models having has_secure_password store password in password_digest column instead of password column.
In fact password column is not needed.
> u=User.create!(email: 'user#gmail.com', password: '12345678')
> u
#<User:0x007fc794be9278> {
:id => 1,
:email => "user#gmail.com",
:password_digest => "$2a$10$S82GVFR..yO9jihgIoeMj.7dNMWtbCUZpWDKvH0tyMs1SYlfdefmW"
}
I had the same problem, I was following http://www.railstutorial.org/book/modeling_users
and my app/Controllers/users_controllers.rb didn't have a method to create the attribute, I also used git to share the working code between portable laptop for the train and larger home, this created the migration file but didnt apply it, my working user controller is below.
class UsersController < ApplicationController
def new
attr_accessor :name, :email, :password
def initialize(attributes = {})
#name = attributes[:name]
#email = attributes[:email]
#password = attributes[:password]
end
def formatted_email
"#{#name} <#{#email}>"
end
end
Hey I'm following RoR too and come into the same problem. The trick here is your bundle exec rake db:migrate fails and therefore the password_digest column hasn't been added into the database. My console complains that database for User already exists. I delete the db/development.sqlite3 manully with "SQLite Browser". After running bundle exec rake db:migrate, every test passes

Any examples of using Sinatra with acts_as_audited?

background: I am building a web app using Sinatra and ActiveRecord and I am keen to take advantage of acts_as_audited (as per https://github.com/collectiveidea/acts_as_audited). The docs for acts_as_audited assume I'll be using Rails and so assume I'll use Rails to generate the necessary migrations. I've not found any examples of using acts_as_audited with Sinatra.
So my question: Can someone point me at an example of using Sinatra and ActiveRecord with acts_as_audited?
I have been able to get this to work using the Audit.as_user method. Using this method lets you audit records as if the change were made by the user object you pass in.
Here is a simple example.
# This is my User model, I want to audit email address changes to it.
class User < ActiveRecord::Base
acts_as_audited
# user has :email attribute
...
end
# This is what I would call in my Sinatra code.
# user is an instance of my User class
...
Audit.as_user(user) do
user.audit_comment = "updating email from sinatra"
user.update_attribute(:email, 'foo#bar.com')
end
...
A more complex example...
# Now I have a User model and a Comments model and I
# want to audit when I create a comment from Sinatra
class User < ActiveRecord::Base
has_many :comments
acts_as_audited
...
end
class Comment < ActiveRecord::Base
belongs_to :user
acts_as_audited
# has a :body attribute
...
end
# This is what I would call in my Sinatra code.
# Again, user is an instance of my User class
...
Audit.as_user(user) do
user.comments.create(
:body => "Body of Comment",
:audit_comment => "Creating Comment from Sinatra"
)
end

Resources