I created the app with the help of rails composer. Using devise for authentication and cancan for managing roles. So I have 3 roles by default: Admin, User and VIP. I deleted VIP, because I don't need it. Run rake db:seed to create a default admin. Then I'm coming to localhost and seeing the "First User" as admin. I logout and register(signup) a new user. Then, signing in again as admin. I see, that by deafault, this new user doesn't have any role. And also I see, that I can change it("Change role" - button). I push it and as admin can choose whether new user will be the second admin or just User. I choose, for example, User, push "change role" and have an "ArgumentError in UsersController#update wrong number of arguments (2 for 1)".
Sooo, I have two questions:
1. How to give my admin the ability to change roles without errors.
2. How to make new signing up users to have default role "User".
Thanks!
Ok, I managed to set the default role this way:
after_create :assign_reader_role
private
def assign_reader_role
self.add_role "user"
end
Here is my UserControlle:
class UsersController < ApplicationController
before_filter :authenticate_user!
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#users = User.all
end
def show
#user = User.find(params[:id])
end
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
if user.update_attributes(user_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
unless user == current_user
user.destroy
redirect_to users_path, :notice => "User deleted."
else
redirect_to users_path, :notice => "Can't delete yourself."
end
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end
Here is models.
User:
class User < ActiveRecord::Base
after_create :assign_reader_role
rolify
devise :database_authenticatable, :registerable,#:confirmable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :name
private
def assign_reader_role
self.add_role "user"
end
end
Role:
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
scopify
end
UserController I've already put! And where can I take params from the form?
I think you missed role_ids in permit
def user_params
params.require(:user).permit(:name, :email, :role_ids)
end
Related
I am a newB in rails and I am working on a project and wanted to remove trailing and leading white spaces from username and email. So I created a method in user model
class User < ActiveRecord::Base
include CarrierWave::MiniMagick
#removes the white spaces before validating the data
before_validation :strip_whitespace, :only => [:name,:email]
#data validations
validates :email, :presence =>true, :uniqueness => {case_sensitive: false}, :format => { :with=> /([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)/, :message => "please enter a valid e-mail" }
validates :name, :presence=>true
validates :password ,:presence =>true, :confirmation=> true #, :length =>{ :minimum=>6, :maximum=>30}, :format=>{:with=>/(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,30}/}
#for the image
mount_uploader :image, ImageUploader
#for the password
has_secure_password
#associations
has_many :issues
has_many :comments
end
def strip_whitespace
self.email = self.email.squish
self.name = self.name.squish
end
when I enter user information in Users.create action, the leading and trailing spaces are removed, But when I login from my sessions controller, the leading and trailing spaces are not removed and hence it shows me an error.
Please help
Code for the users controller
class UsersController < ApplicationController
before_action :not_logged_in?, :only => [:new]
def new
#user=User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
RegistrationsMailer.welcome_email(#user).deliver
flash[:success] = 'you are successfully registered'
redirect_to :controller => 'users' , :action => 'show', :id => #user.id
else
render 'new'
end
end
def show
#user=User.find(params[:id])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to #user
else
render 'edit'
end
end
protected
def user_params
params.require(:user).permit(:name,:email,:password,:image)
end
end
code for sessions controller
class SessionsController < ApplicationController
def new
end
def create
#user = User.find_by_email(params[:sessions][:email])
if #user && #user.authenticate(params[:sessions][:password])
sign_in #user
redirect_to #user
else
flash.now[:error] = 'Invalid email or password'
render 'new'
end
end
def destroy
end
end
Please help
According to When does Validation happen?
The following methods trigger validations, and will save the object to the database only if the object is valid:
create
create!
save
save!
update
update!
When you are creating or destroying a new session, you don't actually call any of those methods on the User model so validations won't be called.
But since your database has the squished values, you need to modify your create action in the SessionsController to squish the passed in params.
def create
#user = User.find_by_email(params[:sessions][:email].squish)
if #user && #user.authenticate(params[:sessions][:password].squish)
sign_in #user
redirect_to #user
else
flash.now[:error] = 'Invalid email or password'
render 'new'
end
end
I am having challenges assigning a current user a role in a team the user is creating. I want to assign the user that creates the team the role of the captain which could be changed later.
I'm currently using the create_asociation method that comes with has_one relationship, as this instantiates the values of the associated model, which i want to be instantiated with the current user but get the error Can't mass assign protected attribute: captain. Captain is a self join model with user as i will like to use captain.teammates and team.captain.
Below are the models involved.
User and Captain Model
class User < ActiveRecord::Base
has_one :profile
has_many :teammates, :class_name => "User", :foreign_key => "captain_id"
belongs_to :captain, :class_name => "User"
belongs_to :team
# before_create :build_profile
after_create :build_default_profile
accepts_nested_attributes_for :profile
attr_accessible :email, :password, :password_confirmation, :profile_attributes, :captain_id
def build_default_profile
Profile.create(user_id: self.id)
end
has_secure_password
before_save { email.downcase! }
before_save :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Team Model
class Team < ActiveRecord::Base
has_many :profiles, through: :users
has_one :captain, :class_name => "User", foreign_key: :captain_id
has_one :result, as: :result_table
attr_accessible :teamname, :color, :result_attributes, :captain_attributes
after_create :build_result_table
after_create :build_default_captain
accepts_nested_attributes_for :profiles
accepts_nested_attributes_for :captain
accepts_nested_attributes_for :result
def build_result_table
Result.create(result_table_id: self.id, result_table_type: self.class.name)
end
def build_default_captain
# Team.captain = User
# Captain.create(team_id: self.id, captain_id: user.id)
end
end
User Controller
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save!
sign_in #user
flash[:success] = "Welcome to the JHDC Mini Olympics Web Application; Thanks for singing Up"
redirect_to user_profile_path(#user, #profile)
else
flash[:error_messages]
render 'new'
end
end
def show
#user = User.find(params[:id])
end
def index
#users = User.paginate(page: params[:page])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile Updated"
redirect_to user_profile_path(#user, #profile)
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted."
redirect_to users_url
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
end
Team Controller
class TeamsController < ApplicationController
def new
#team = Team.new
end
def create
#team = Team.new(params[:team])
#captain = #team.create_captain(captain: current_user)
if current_user.admin?
if #team.save!
flash[:success] = "Team created."
redirect_to #team
else
flash[:error_messages]
render 'new'
end
else
flash[:error] = "Sorry, you don't have the authority to create a Team"
redirect_to current_user
end
end
def index
#teams = Team.paginate(page: params[:page])
end
def show
#team = Team.find(params[:id])
end
def edit
if current_user.admin?
#team = Team.find(params[:id])
else
flash[:error] = "Sorry you dont have the authourity to edit a Team"
redirect_to current_user
end
end
def update
#team = Team.find(params[:id])
if #team.update_attributes(params[:team])
flash[:success] = "Team Updated"
redirect_to #team
else
render 'edit'
end
end
def destroy
Team.find(params[:id]).destroy
flash[:success] = "Team is deleted."
redirect_to teams_url
end
private
def team_params
params.require(:team).permit(:teamname, :color)
end
end
The admin is currently a way i'm using to restrict the user that can create a team but i plan to use gems like declarative authorization to create role based authorization. Thanks
The error you are getting is because the attribute :captain is not declared as attr_accessible
Either set the attribute :captain in your list of attr_accessible for the User model, or change the code form
Captain.create(team_id: self.id, captain_id: user.id)
to
captain = Captain.new
captain.team_id = self.id
captain.captain_id = user.id
captain.create
in this way, the attribute won't be set by mass-assignment and won't raise the error
Edited
After checking your code twice, just realized that you don't have a Captain model, actually :captain is a relation for the user and a relation from the Team to the User.
So on Team model, take off the build_default_captain stuff and the after_create :build_default_captain, I would say to replace with something like
after_save :set_default_captain
def set_default_captain
if captain_id_changed?
profiles.each do |user|
user.captain = captain
user.save
end
end
end
so every time the captain_id change for the model, you change the captain_id of all its profiles (users)
Then on the Team controller, on the action create, instead of
#team = Team.new(params[:team])
#captain = #team.create_captain(captain: current_user)
do something like
#team = Team.new(params[:team])
#team.captain = current_user
if current_user.admin?
if #team.save!
current_user.update_attribute(:team_id, #team.id)
flash[:success] = "Team created."
redirect_to #team
else
flash[:error_messages]
render 'new'
end
else
flash[:error] = "Sorry, you don't have the authority to create a Team"
redirect_to current_user
end
so on the last part of the code, you set the captain of the team to the current user and set the user team to the current team once its saved, you can also improve the code with current_user.build_team to avoid saving current_user.update_attribute
I'm having troubles getting my friending model in Rails 4 working. I set up a friendship controller and modified my User model to use a self-join as follows.
Friendship Model:
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, class_name: "User", foreign_key: "friend_id"
validates_presence_of :user_id, :friend_id
end
User Model:
class User < ActiveRecord::Base
rolify
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
has_many :items
has_many :friendships
end
My UserController:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!
load_and_authorize_resource
def index
authorize! :index, #user, message: 'Not authorized as an administrator.'
#users = User.all
end
def update
authorize! :update, #user, message: 'Not authorized as an administrator.'
if #user.update_attributes(params[:user])
redirect_to users_path, notice: "User updated."
else
redirect_to users_path, alert: "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, message: 'Not authorized as an administrator.'
user = User.find(params[:id])
unless user == current_user
user.destroy
redirect_to users_path, notice: "User deleted."
else
redirect_to users_path, notice: "Can't delete yourself."
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit( :name, :email, :role_ids, :id, :user_id )
end
end
The relevant portion of my log:
NoMethodError - undefined method friends' for nil:NilClass:
app/views/users/index.html.erb:32:in_app_views_users_index_html_erb__2019584293611114470_70229529361660'
Unless I've missed this completely, I should be able to access a friend as #users.friends but that gives me a NoMethod Error. I've also tried accessing it as #users.friendships but I get the same error.
I have run the migration and restarted my rails server. I don't know what I'm missing but I would sure appreciate a fresh set of eyes. I think it must be something with how I've set up the model.
The models look fine, however in the index method on UsersController you have the following line: #users = User.all. This creates an array of users which means you can't call .friends on it. You need to access each user individually through iteration
#users.each do |user|
user.friends
# ... more code
end
or some other means.
I have devise installed and configured on my rails 3 project and I want to make it so that only admins can create/edit users. How can I edit the devise controllers to accomplish this?
I suggest using CanCan for that.
First, you'll define abilities like :read, :create, :update and :destroy and assign them to user roles by using something like:
if user.admin?
can :manage, :all
end
Then, you'll check those abilities by checking whether the user has permissions to create/edit users by using something like if can? :create, User.
I've sorted this out previously. I remember it being a pain but it does work. It requires CanCan.
Given that an administrator is defined with an admin boolean on the User model:
user.rb:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
attr_accessor :current_password
attr_accessible :name, :password, :password_confirmation, :current_password, :email, :remember_me, :admin
end
class Ability
include CanCan::Ability
def initialize(user)
can :manage, :all if user.admin
end
end
users_controller.rb
def update
#user = User.find(params[:id])
params[:user].delete(:password) if params[:user][:password].blank?
params[:user].delete(:password_confirmation) if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
if #user.update_attributes(params[:user])
flash[:notice] = "Successfully updated "+#user.name
redirect_to users_path
else
render :action => 'edit'
end
end
routes.rb
devise_for :users, :path => "d"
devise_scope :user do
get '/sign_in' => 'devise/sessions#new'
get '/sign_out' => 'devise/sessions#destroy'
end
resources :users, :controller => "users"
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
after_filter :user_activity
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_path
end
def admin?
self.admin == true
end
def authenticate_admin
redirect_to :new_user_session_path unless current_user && current_user.admin?
end
private
def user_activity
current_user.try :touch
end
end
application_helper.rb
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
That should do it.
If you need only to allow admins to create users you could write something like
class uUsersController < ApplicationController
def create
#validate if the current user is an admin
end
end
But the more standard, flexible way is to use a gem like cancan, which personally I would prefer :)
I want to skip validation when I am trying to edit user as admin.
Model
class User
...
attr_accessible :company_id, :first_name, :disabled, as: :admin
Controller
class Admin::UsersController
...
def update
#user = User.find(params[:id])
#user.update_attributes(params[:user], as: :admin)
redirect_to edit_admin_user_path(#user), :notice => "User Account Updated"
end
So I tried to change update action to
def update
#user = User.find(params[:id])
#user.attributes = params[:user]
#user.save(validate: false)
redirect_to edit_admin_user_path(#user), :notice => "User Account Updated"
end
But then I dont have access to set :disabled and :company_id attributes because i dont know where to set as: :admin
Try this:
#user = User.find(params[:id])
#user.assign_attributes(params[:user], as: :admin)
#user.save(validate: false)
Strong Parameters
This has been an issue with rails for a long time, in Rails 4 they are introducing "Strong Parameters"
https://github.com/rails/strong_parameters
http://railscasts.com/episodes/371-strong-parameters
You can use strong parameters gem in rails 3 applications as well
Alternative: Context Attribute
Another way to do it, introduce a context variable on the user model - *Note I am not familiar with the 'as' option for attr_accessible*
class User < ActiveRecord::Base
attr_accessor :is_admin_applying_update
validate :company_id, :presence => :true, :unless => is_admin_applying_update
validate :disabled, :presence => :true, :unless => is_admin_applying_update
validate :first_name, :presence => :true, :unless => is_admin_applying_update
# etc...
In your admin controller set the is_admin_applying_update attribute to true
class Admin::UsersController
# ...
def update
#user = User.find(params[:id])
#user.is_admin_applying_update = true
#user.update_attributes(params[:user])
NOTE: you can also group the validations and use a single conditional
http://guides.rubyonrails.org/active_record_validations.html#grouping-conditional-validations
Hack method:
def update
#user = User.find(params[:id])
#user.define_singleton_method :run_validations! do true; end
#user.update_attributes(params[:user], :as => :admin)
redirect_to edit_admin_user_path(#user), :notice => "User Account Updated"
end