CanCanCan not authorizing admin - ruby-on-rails

I'm using cancancan with rails_admin and devise gems. Even though the user has the role admin cancan shows the error You are not authorized to access this page. when trying to go to /admin route after logging in.
Here's my ability.rb
#models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
can :read, :all # allow everyone to read everything
if user && user.has_role?(:admin)
can :access, :rails_admin # only allow admin users to access Rails Admin
can :dashboard # allow access to dashboard
if user.role? :admin
can :manage, :all # allow superadmins to do anything
end
end
end
end
Here's my user model
#models/user.rb
class User < ActiveRecord::Base
ROLES = %i[admin moderator banned]
def roles=(roles)
roles = [*roles].map { |r| r.to_sym }
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
end
def roles
ROLES.reject do |r|
((roles_mask.to_i || 0) & 2**ROLES.index(r)).zero?
end
end
def has_role?(role)
roles.include?(role)
end
def role?(base_role)
ROLES.index(base_role.to_s) <= ROLES.index(role)
end
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
if conditions[:username].nil?
where(conditions).first
else
where(username: conditions[:username]).first
end
end
end
validate :validate_username
def validate_username
if User.where(email: username).exists?
errors.add(:username, :invalid)
end
end
validates_format_of :username, with: /^[a-zA-Z0-9_\.]*$/, :multiline => true
# Include default devise modules. Others available are:
# , :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :confirmable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login]
attr_accessor :login
def login=(login)
#login = login
end
def login
#login || self.username || self.email
end
end
Here's the rails_admin.rb
#config/initializers/rails_admin.rb
RailsAdmin.config do |config|
## == Devise ==
config.authenticate_with do
warden.authenticate! scope: :user
end
config.current_user_method(&:current_user)
## == Cancan ==
config.authorize_with :cancan
config.actions do
dashboard # mandatory
index # mandatory
new
export
bulk_delete
show
edit
delete
show_in_app
## With an audit adapter, you can add:
# history_index
# history_show
end
end
Here's the user in my schema.rb
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.string "username"
t.string "role"
t.integer "roles_mask"
end
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
add_index "users", ["username"], name: "index_users_on_username", unique: true
I created a regular user and changed it's role attribute to admin using
User.first.update_attribute :role, 'admin'
I don't exactly understand the use of roles_mask. Do I need to use both role and roles_mask in my database?

I don't fully understand what's going on in the User#roles but after arbitrarily assigning a role_mask of 0(the index of admin), I got an empty array. I doubt that this is the behaviour you anticipated.
I see that the check for roles(has_role?) uses the role_mask which you may not have assigned after the creation of your users.
To reduce some of these boilerplate code, I think it would be convenient to use ActiveRecord.enum.
#models/user.rb
class User < AR::Base
enum roles: %i[admin moderator banned]
end
note that to assign a default role, you could set that on your db or after_create.
#models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
can :read, :all # allow everyone to read everything
if user && user.admin?
can :access, :rails_admin # only allow admin users to access Rails Admin
can :dashboard # allow access to dashboard
if user.admin?
can :manage, :all # allow superadmins to do anything
end
end
end
end
You can also assign role to a particular user by user.admin!

Related

Rails rollback while saving model

I'm creating a license server, but I stuck with problem, that Rails can't save model.
I set after_create method in User model, but got no luck, also I tried create License model with Rails console, but it rollback transaction and didn't show any error.
models/user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
authentication_keys: [:login]
attr_writer :login
has_one :license, dependent: :destroy
validates :username, presence: true, uniqueness: { case_sensitive: false }
validates_format_of :username, with: /^[a-zA-Z0-9_\.]*$/, multiline: true
after_create :create_assocs
def login
#login || self.username
end
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(['lower(username) = :value OR lower(email) = :value', { value: login.downcase }]).first
else
if conditions[:username].nil?
where(conditions).first
else
where(username: conditions[:username]).first
end
end
end
def email_required?
false
end
private
def create_assocs
create_license(license_types_id: LicenseType.first.id)
# license = License.new(user_id: self.id, license_types_id: 1)
# license.save
# self.license.create(license_types_id: LicenseType.first.id)
end
end
models/license.rb
class License < ApplicationRecord
belongs_to :license_type
belongs_to :user
after_create :set_expired_at
private
def set_expired_at
# self.expired_at = DateTime.now + self.license_types.duration
end
end
in rails console,
2.5.1 :001 > license = License.new(license_types_id: LicenseType.first.id)
LicenseType Load (0.4ms) SELECT "license_types".* FROM "license_types" ORDER BY "license_types"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<License id: nil, expired_at: nil, created_at: nil, updated_at: nil, license_types_id: 1, user_id: nil>
2.5.1 :002 > license.save
(0.5ms) BEGIN
(0.2ms) ROLLBACK
=> false
schema.rb,
create_table "licenses", force: :cascade do |t|
t.datetime "expired_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "license_types_id"
t.bigint "user_id"
t.index ["license_types_id"], name: "index_licenses_on_license_types_id"
t.index ["user_id"], name: "index_licenses_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: ""
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "username"
t.string "key"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["username"], name: "index_users_on_username", unique: true
end
add_foreign_key "licenses", "users"
What should I do to set the license for new user after creating?
License model contains two foreign key user_id and license_type_id
=> Which means before create License there must be a user who own this License as Rails 5 convention says user_id must exist
=> Also there must exist LicenseType as Rails 5 convention says license_type_id must exist
The rollback reasons can be investigated by following
license = License.new(license_types_id: LicenseType.first.id)
license.save
#Begin
#ROLLBACK
errors_stack = license.errors
errors_stack contains model level errors which causes rollback
To fix these rollback issue
user = User.first #or current_user
license = user.license.new(license_type_id: LicenseType.first.id)
license.save
Or
user = User.first #or current_user
license = License.new(license_type_id: LicenseType.first.id, user_id: user.id)
license.save
Or To create a User and assign the user a License # Alternative of after_create :create_assocs
new_user = User.new(...)
new_user.license.build(license_type_id: LicenseType.first.id)
new_user.save
Are you sure about this "no errors"? In Rails 5, belongs_to association is required by default, so I guess that's because it fails (you don't set user association prior to save attempt). So either you should set license.user, or set:
belongs_to :user, optional: true
in License model if your business logic doesn't require it.

Rails Koala - Getting a undefined method `oauth_token=' for #<User:

Apologies for the beginner question. I'm trying to use the Koala gem on my app. Currently, I have devise and omniauth (for facebook login). And they work great - I'm able to register users easily. What I want to do now though is utilize Koala to get access to Facebook data from my logged in Users. However, I'm getting an error "undefined method `oauth_token=' for #
User.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, omniauth_providers: [:facebook]
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.username = auth.info.name
user.avatar = URI.parse(auth.info.image)
user.oauth_token = auth.credentials.token #Added this after learning about Koala
end
end
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
end
end
Application_Controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
before_filter :set_body_class
before_filter :set_current_user
def set_current_user
User.current_user = current_user
end
def set_body_class
#body_class = "#{controller_name} #{action_name}"
end
protected
def after_sign_in_path_for(resource)
# request.env['omniauth.origin'] || stored_location_for(resource) || root_path
items_path
end
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username, :name, :avatar])
devise_parameter_sanitizer.permit(:account_update, keys: [:username,:name, :avatar])
end
end
Schema.rb
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
t.string "username"
t.string "provider"
t.string "uid"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
Migration AddOmniauthToUsers
class AddOmniauthToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :provider, :string
add_column :users, :uid, :string
end
end
Try and call self.outh_token. outh_token belongs to a user and unless you pass a user as a parameter or call it on itself using "self", Rails doesn't know where outh_token belongs to.
def facebook
#facebook ||= Koala::Facebook::API.new(self.oauth_token)
end
or
def facebook(user)
#facebook ||= Koala::Facebook::API.new(user.oauth_token)
end

1 error prohibited this user from being saved: Id can't be blank

I used devide in gem and now I made Sign up&Log in pages.
When I wrote my id and email address in my app,blowser told me 1 error prohibited this user from being saved: Id can't be blank.Of course,I surely wrote id,so I don't know why.
Is this blowser error?
I wrote in home_controller,
class HomeController < ApplicationController
before_filter :find_user, only: [:index]
def index
# #id = params[:id]
# #email = params[:email]
if id == #user.id && email == #user.email
render :text => "sucsess"
else
render :text => "fail"
end
end
def create
id = params[:id]
email = params[:email]
#user = UserData.new(user_params)
#user.save
unless userData.save
#error_message = errors.full_messages.compact
end
end
private
def find_user
user = User.find(params[:id])
if current_user.id != user.id
redirect_to root_path
end
end
end
in users.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
validates :id, presence: true
validates :email, presence: true, uniqueness: true
end
in migration file
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
in routes.rb
Rails.application.routes.draw do
get 'notes/new'
devise_for :users
root to: "home#index"
get 'home/index'
get 'home/create'
namespace :home, default: {format: :json} do
resources :index, only: :create
end
end
Validation runs before creation. So the id will have no chance of being created before you save.
Remove this
validates :id, presence: true
and don't set the id manually on the create action.
Remove below line from model, as ID is auto generated field.
validates :id, presence: true
Update create action in controller file as below,
def create
#user = User.new(user_params)
#user.save
unless #user.save
#error_message = errors.full_messages.compact
end
end
Replace "UserData" with "User" which is the model name as per your code.
Comment or remove below line
validates :id, presence: true
as id is create automatically by rails and no need to validate it.
And
In your controller, change private method code:
def find_user
#user = User.find(params[:id]) #replace user to #user
if current_user.id != user.id
redirect_to root_path
end
end

As I can create with username profile page Rails

I am unable to find accurate information to what I want, so my second choice is to ask.
So, I want to know how to create a user profile from scratch and with your username replacing the ID.
Example: http://example.com/profile/{username}
In this case I have no problems with usernames, as are those working on a game server and they can not contain spaces or unusual characters.
I have done something, but I think it is wrong, even though I do not have any error on my website.
Notes:
My Devise Model: Player/s
Schema of Players
create_table "players", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "admin", default: false
t.integer "role_id"
t.string "nick"
t.string "username"
end
Variables "admin and role_id" are for charges will have on the web. I think this is not relevant to the subject.
Looked at other tutorials, but I have no more special things to add to my code.
My Controllers:
- registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
new_path(resource)
end
end
profile_controller.rb
class ProfileController < ApplicationController
def members
end
def myProfile
#attributes = Profile.attribute_names - %w(id player_id created_at updated_at)
end
def new
#profile = current_player.build_profile
end
def create
#profile = current_player.build_profile(params[:profile].permit( :nick))
end
end
My Models:
- player.rb
class Player < ActiveRecord::Base
validates :email, :presence => true, :email => true
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :role
before_create :set_default_role
has_one :profile
private
def set_default_role
self.role ||= Role.find_by_name('registered')
end
end
profile.rb
class Profile < ActiveRecord::Base
belongs_to :players
end
And in my view, have the controller profiles with:
myProfile => Only the profile of the user
profiles that is the global view of the profiles
route.rb
get 'profile/:id' => 'profile#perfiles'
resources :profile, only: [:edit]
map.resources :players, :has_one => :profile
map.resources :profiles
I hope I'm not asking for much, but hey, it's a question that I have and worth a try.
Thanks in advance, if anything is missing tell me.
I believe what you're looking to do is override the named route parameter. See this rails guide for more details: http://edgeguides.rubyonrails.org/routing.html (section 4.10).
Basically, you'll want to just add the param: :username to your routes file:
resources :profile, only: [:edit], param: :username
Which will make your route look something like:
profile GET /profile/:username(.:format) profile#show
And in your controller:
#profile = Profile.find_by(username: params[:username])
Hope that helps!

devise: undefined method `user=' for nil:NilClass

I'm trying to implement the devise gem into my web app - everything with user login and registration is working, but when a user tries to actually leave a prediction (the only thing you need to be logged in for), I get this error message: undefined method `user=' for nil:NilClass. I can't seem to figure out what's causing this problem. Might anyone know?
_login_items.html.erb:
<ul>
<% if user_signed_in? %>
<li>
<%= link_to('Logout', destroy_user_session_path, :method => :delete) %>
</li>
<% else %>
<li>
<%= link_to('Login', new_user_session_path) %>
</li>
<% end %>
<% if user_signed_in? %>
<li>
<%= link_to('Edit registration', edit_user_registration_path) %>
</li>
<% else %>
<li>
<%= link_to('Register', new_user_registration_path) %>
</li>
<% end %>
</ul>
prediction model:
class Prediction < ActiveRecord::Base
belongs_to :student
belongs_to :user
end
user model:
class User < ActiveRecord::Base
has_many :predictions
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
predictions migration:
class CreatePredictions < ActiveRecord::Migration
def change
create_table :predictions do |t|
t.string :prediction
t.belongs_to :student, index: true
t.belongs_to :user
t.timestamps
end
end
end
user migration:
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.inet :current_sign_in_ip
t.inet :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
predictions controller:
class PredictionsController <ApplicationController
def create
#prediction.user = current_user
Prediction.create(prediction_params)
redirect_to :back
end
def new
#prediction = Prediction.new(student_id: params[:student_id])
end
def destroy
Prediction.find(params[:id]).destroy
redirect_to :back
end
private
def prediction_params
params.require(:prediction).permit(:prediction) #, :student_id
end
end
The problem is in your PredictionsController. You have to create the prediction and assign it to a variable first. Change your create action to this:
def create
#prediction = Prediction.create(prediction_params)
#prediction.user = current_user
redirect_to :back
end
You were trying to assign a user to a non existing prediction. This correction creates the prediction first then assigns the user to it.
Edit: I noticed one other problem: Your prediction_params method is incorrect. The arguments you pass to the permit method must be the attributes of your prediction model which you want to mass assign. The :prediction key in the params is the one you want to require but within that nested hash you want to permit attributes of the prediction model. So, hypothetically, if your prediction model has :name, :value and :student_id attributes, your prediction_params method should look like this:
def prediction_params
params.require(:prediction).permit(:name, :value, :student_id)
end
This will work with a params hash that looks like:
{
prediction: {
name: 'something',
value: 'stuff',
student_id: 1
}
}

Resources