Having difficulty enabling an authorized user to view their own resource - ruby-on-rails

I am trying to make a User able to edit their own Dashboard.
I am using CanCan and Devise to try to make this happen (Rolify is also installed to help with Admin features, but I don't feel it's applicable to this situation).
When I have a user logged in and they visit their :show dashboard page at root/users/id/dashboard.id they fail authentication although they have the same user.id as listed on the dashboard.user_id (as dictated in the ability.rb file.
How do I ensure that a user can view their own dashboard?
Here is the associated code:
dashboards_controller.rb:
class DashboardsController < ApplicationController
before_filter :authenticate_user!
def show
#dashboard = Dashboard.find(params[:format])
authorize! :read, current_user, :message => 'Not authorized to view this dashboard.'
end
end
ability.rb:
user ||= User.new
if user.has_role? :default # All of my users have this role
can :read, Dashboard, :user_id => user.id
end
from routes.rb
resources :users do
resource :dashboard, only: [:show, :edit, :update, :destroy]
end
User.rb:
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# created_at :datetime
# updated_at :datetime
#
class User < ActiveRecord::Base
rolify
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :dashboard
before_create :build_dashboard
end
Dashboard.rb:
# Table name: dashboards
#
# id :integer not null, primary key
# user_id :integer
# has_selected_account_type :boolean default(FALSE)
# created_at :datetime
# updated_at :datetime
#
class Dashboard < ActiveRecord::Base
belongs_to :user
end

Please take a look here
Basically, you want to use the load_resource helper method to fetch only current_user's dashboard. Behind the scene it is doing something like this in your controller:
current_user.dashboard.find(params[:dashboard_id])

Related

Trying to save different model before creation?

I am trying to add a user role according to Rolify gem in my devise User model.
What I basically want to achieve is that, if the user has selected that he is a student or a teacher in the registration page itself, after the creation of the user, it should add the required roles to the user.
Please note that I am not storing the 'role' in the User table. I am just using attr_accessor to send me an initial value to compare.
This is my User model code :
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0), not null
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# avatar :string(255)
# username :string(255)
#
class User < ActiveRecord::Base
# This is for the user roles.
rolify
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :role
# Adding the carrierwave uploader
mount_uploader :avatar, AvatarUploader
before_create :add_specified_role
def add_specified_role
if self.role == 0
# I am sure I am messing it up here and this is not the way.. :/
after_create :add_student_role
elsif self.role == 1
after_create :add_teacher_role
end
end
def add_student_role
self.add_role(:student) if self.roles.blank?
end
def add_teacher_role
self.add_role(:teacher) if self.roles.blank?
end
end
However, it does not seem to be working as when I check the roles, the role has not been added and I am sure I am doing something wrong.
What is the correct way to achieve the above task?
callback should be
after_create :add_specified_role , if: proc { |a| a.roles.blank? }
instead of
before_create :add_specified_role
And method should
def add_specified_role
if role == 0
add_role(:student)
elsif role == 1
add_role(:teacher)
end
end

Devise + omniauth twitter + authentications table

When I authenticate through my twitter sign in button, I'm getting this browser error
The action 'new' could not be found for RegistrationsController
I created a registrations_controller.rb to override the devise controller for some methods according to this guide http://www.orhancanceylan.com/rails-twitter-and-facebook-authentications-with-omniauth-and-devise/#comment-1375
Sorry to be verbose about the code. The the main part is in the authentications_controller.rb in the twitter method, in the else portion of the method.
That portion of the code governs when a user does not have other 3rd party signin authentications previously created AND not signed in via devise. Thus an authentication record and a user record must be created in that portion of the code. But I'm getting the error.
There is an authentications table so that I can add other omniauth strategies.
If unclear, or more information is needed, please let me know how I can improve the question.
routes.rb
devise_for :users, :path => '',
:path_names => { :sign_in => "login",
:sign_out => "logout",
:sign_up => "sign-up",
:account_update => "account-settings" },
:controllers => { omniauth_callbacks: "authentications", registrations: "registrations" }
get "/auth/:provider/callback", to: "authentications#:provider"
authentications_controller.rb
class AuthenticationsController < Devise::OmniauthCallbacksController
def twitter
omni = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omni['provider'], omni['uid'])
if authentication
# If already registered previously
flash[:notice] = "Logged in successfully."
user = User.find(authentication.user_id)
sign_in_and_redirect user
elsif current_user
# If signed in via any other strategy, including devise.
current_user.authentications.create!(:provider => omni['provider'],
:uid => omni['uid'],
:token => token = omni['credentials']['token'],
:token_secret => omni['credentials']['secret'])
flash[:notice] = "Authentication successful."
sign_in_and_redirect current_user
else # this is the conditional that gets executed.
# If brand new to the site.
user = User.new
user.apply_omniauth(omni)
if user.save
flash[:notice] = "Login successful."
sign_in_and_redirect User.find(user.id)
else
session[:omniauth] = omni.except('extra')
redirect_to new_user_registration_path
end
end
end
# Overrides
def create
super
session[:omniauth] = nil unless #user.new_record?
end
end
authentication.rb
# == Schema Information
#
# Table name: authentications
#
# id :integer not null, primary key
# user_id :integer
# provider :string(255)
# uid :string(255)
# token :string(255)
# token_secret :string(255)
# profile_page :string(255)
# created_at :datetime
# updated_at :datetime
#
class Authentication < ActiveRecord::Base
belongs_to :user
end
#
user.rb
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0), not null
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :inet
# last_sign_in_ip :inet
# username :string(255)
# admin :boolean default(FALSE)
# image :string(255)
# points :integer default(0), not null
# hacks_count :integer default(0), not null
# comment_threads_count :integer default(0), not null
#
# Indexes
#
# index_users_on_email (email) UNIQUE
# index_users_on_reset_password_token (reset_password_token) UNIQUE
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, omniauth_providers: [ :twitter ]
has_many :hacks
has_many :comments
acts_as_voter
has_many :authentications
validates :username,
presence: true,
:uniqueness => { case_sensitive: false },
length: { in: 4..20 }
#
SOCIALS = {
twitter: 'Twitter',
facebook: 'Facebook'
}
#
def apply_omniauth(omni)
authentications.build(:provider => omni['provider'],
:uid => omni['uid'],
:token => token = omni['credentials']['token'],
:token_secret => omni['credentials']['secret'])
end
# Overrides
def update_with_password(params, *options)
if encrypted_password.blank?
update_attributes(params, *options)
else
super
end
end
def password_required?
super && (authentications.empty? || !password.blank?)
end
end
I was inheriting registrations_controller from the wrong controller.
It should be
class RegistrationsController < Devise::RegistrationsController
not
class RegistrationsController < ApplicationController
watch out, guys =]

How to render json using :includes in it?

I've models GuestOrder, OrderBatch, OrderItem
# == Schema Information
#
# Table name: guest_orders
#
# id :integer not null, primary key
# notes :string(255)
# adults :integer
# children :integer
# created :datetime
# placed :datetime
# billed :datetime
# created_at :datetime
# updated_at :datetime
#
class GuestOrder < ActiveRecord::Base
has_many :order_batches, :dependent => :destroy
end
# == Schema Information
#
# Table name: order_batches
#
# id :integer not null, primary key
# placed :datetime
# guest_order_id :integer
# created_at :datetime
# updated_at :datetime
#
class OrderBatch < ActiveRecord::Base
belongs_to :guest_order
has_many :order_items, :dependent => :destroy
end
# == Schema Information
#
# Table name: order_items
#
# id :integer not null, primary key
# quantity :integer
# accepted :datetime
# cooking :datetime
# ready :datetime
# delivered :datetime
# cancelled :datetime
# order_batch_id :integer
# dish_id :integer
# created_at :datetime
# updated_at :datetime
#
class OrderItem < ActiveRecord::Base
belongs_to :order_batch
belongs_to :dish
end
I'm trying to render json in the following method to get a guest_order and its belonging order_batches and order_items by passing guest_order id as parameter.
def getOrderDetails
#To get the details of a particular guest_order and its batches and items
#guest_order = GuestOrder.find_by_id(params[:id])
render :json => #guest_order.to_json(:except => [:created_at, :updated_at],
:includes => {:order_batches => {:except => [:guest_order_id, :created_at, :updated_at],
:includes => {:order_items => {:except => [:order_batch_id, :created_at, :updated_at] } } } } )
end
But I didn't get the expected result, only the details from the guest_orders table is rendered. How to solve this?
I use :include rather than :includes, not sure if that's significant.
Try using the :include without the :except first, and when you get that working, add in the :except.

newbie: tips on how to cleanup , improve, arrange, shorten model?

I find my models getting more and more clumped and messy. below is my actual user model, any suggestions on cleaning things up and arranging things for better readability?
Im getting frustrated by the unreadability of this all, so any general thoughts on this code are welcome. I really like code that does the same to be positioned together but like I have now is just one big bulk unreadable mess.
# == Schema Information
#
# Table name: users
#
# id :integer(4) not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer(4) default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# password_salt :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
# failed_attempts :integer(4) default(0)
# unlock_token :string(255)
# locked_at :datetime
# authentication_token :string(255)
# username :string(255)
# is_blocked :boolean(1)
# is_deleted :boolean(1)
# role :string(255)
# slug :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# last_seen :datetime
# credits :integer(4)
#
class User < ActiveRecord::Base
devise :database_authenticatable,
:registerable,
:recoverable,
:rememberable,
:trackable,
:validatable,
:token_authenticatable,
:encryptable,
:confirmable,
:lockable,
:timeoutable,
:lastseenable
#:omniauthable
attr_accessible :username,
:login,
:email,
:password,
:password_confirmation,
:remember_me,
:profile_attributes,
:is_moderated,
:is_blocked,
:is_deleted,
:credits,
:role,
:confirmed_at,
:last_seen,
:invite_code
attr_accessor :login
#attr_accessor :invite_code
has_one :profile
has_one :account
accepts_nested_attributes_for :profile
accepts_nested_attributes_for :account
extend FriendlyId
friendly_id :username, use: :slugged
before_create :default_values
# AFTER CREATE -------------------------------------------------------------------------------------------------------
after_create :add_account
def add_account
self.create_account
end
def default_values
self.credits = -1
self.invite_code = invite_code
#self.reset_authentication_token!
beta = Beta.where(:code => invite_code).first
beta.used = 1
beta.save
end
# ROLES --------------------------------------------------------------------------------------------------------------
before_create :setup_default_role_for_new_users
ROLES = %w[admin default vip]
# VALIDATIONS --------------------------------------------------------------------------------------------------------
before_validation { |u| u.username.downcase! }
before_validation { |u| u.email.downcase! }
validates_uniqueness_of :username,
:email,
:case_sensitive => false
validates_presence_of :email,
:username,
:invite_code
validates :username,
:exclusion => {:in => ["admin", "root", "administrator", "superuser", "myhost", "support", "contact", "chat", "boo"],
:message => "is reserved"}
validate :check_email, :on => :create
validate :check_invite_code, :on => :create
def check_invite_code
errors.add(:invite_code, "Invalid code") unless Beta.where(:code => invite_code, :used => 0).first
end
# Devise
def active_for_authentication?
super && !is_deleted
end
# Devise
def confirm!
#welcome_message
#super
end
# Devise
def soft_delete
update_attribute(:deleted_at, Time.current)
end
def is_moderated?
return self.is_moderated
end
def is_online?
if self.last_seen < 10.minutes.ago
return true
else
return false
end
end
private
def setup_default_role_for_new_users
if self.role.blank?
self.role = "default"
end
end
def welcome_message
#::Devise.mailer.welcome_instructions(self).deliver
::Devise.mailer.welcome_instructions(self).deliver
end
def check_email
host = email.split("#").last
username = email.split("#").first
reserved_word_filters = %w(admin hostmaster root support )
if /.*(#{reserved_word_filters.join("|")}).*\#/.match(email)
errors.add(:email, "Invalid username in email")
end
if email.include? 'myhost'
errors.add(:email, "Invalid email")
end
end
# DEVISE: --------------------------------------------------------------------------------------------------
protected
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", {:value => login.strip.downcase}]).first
end
end
The only thing I would do to clean up models is to modularize them a bit. DHH himself posted a great example gist showing how to clean up a model that had gotten too large. I don't think yours is particularly too big, but if you wanted to move all the devise stuff into its own module, it certainly would make your model a bit more tidy.
You could also get in the habit of treating booleans as first class expressions. For example,
def is_online?
if self.last_seen < 10.minutes.ago
return true
else
return false
end
end
is more clearly written as:
def is_online?
last_seen < 10.minutes.ago
end
1.
You have return statements all over your code. Unless you are returning something prematurely in a method, the return of the last statement in a method is what Ruby returns automatically. Like so:
def square(x)
val = x**2
return val
end
can be shortened to:
def square(x)
x**2
end
It's a contrived example, but there it is.
2.
Many of the selfs are redundant. In an model instance's scope, when setting an attribute to a value or calling a method, you do not need to prepend self, since that method/variable is already being called from that same scope.

How do I automatically assign a role to a user when my assignment is related to user model on a has_many through ? Rails 3

What I would like to do is when a user is created, it automatically gives them a role.
The thing is that the role is related to the user via an assignment table and a has_many through association.
The code is as follows:
User model
# == Schema Information
# Schema version: 20110214082231
#
# Table name: users
#
# id :integer not null, primary key
# email :string(255)
# encrypted_password :string(128)
# password_salt :string(255)
# reset_password_token :string(255)
# remember_token :string(255)
# remember_created_at :datetime
# sign_in_count :integer
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# username :string(255)
# f_name :string(255)
# l_name :string(255)
# created_at :datetime
# updated_at :datetime
# invitation_token :string(60)
# invitation_sent_at :datetime
# plan_id :integer
# current_state :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
#
class User < ActiveRecord::Base
before_create :assign
has_many :assignments
has_many :roles, :through => :assignments
def role_symbols
roles.map do |role|
role.name.underscore.to_sym
end
end
def assign
#assignment.build_role(:user_id => self.id, :role_id => '3')
end
end
Assignments model
# Table name: assignments
#
# id :integer not null, primary key
# user_id :integer
# role_id :integer
# created_at :datetime
# updated_at :datetime
#
class Assignment < ActiveRecord::Base
belongs_to :role
belongs_to :user
end
Role model
# == Schema Information
# Schema version: 20101117094659
#
# Table name: roles
#
# id :integer not null, primary key
# name :string(255)
# created_at :datetime
# updated_at :datetime
#
class Role < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments
end
Firstly, don't use hard-coded ids to find your roles as they may change.
Secondly, use association builders, or just access the roles association yourself.
Thirdly, follow the rule of "Don't Make Me Think" (DMMT) when naming your methods.
With these three things in mind:
def assign_default_role
self.roles << Role.find_by_name("User")
end

Resources