How to access devise current_user from model - authentication

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)

Related

rails3 Pundit policy base on join table value

User has_many constructusers, the latter being a join table for a has_many :through relationship to Construct. For the application purposes, the boolean roles are defined in the join table (constructusers.manager, constructusers.operator, etc.), while admin is a user attribute.
So when it comes time to define the policies on the actions the following throws a no method error for 'manager', while a relationship is recognised ActiveRecord::Relation:0x000001035c4370
def show?
user.admin? or user.constructusers.manager?
end
if the relationship (I assume the proper one) is correct, why is there no recognition of the boolean attribute?
As per comment below, for the simple reason that is plural. Thus filtering requires:
Constructuser.where(['construct_id = ? and user_id = ?', params[:id], current_user]).first
...which is running in the controller and impacts the view. Nonetheless, for proper Pundit handling, this needs to be factored out... still de application_controller in a before filter to set that attribute. However a before_filter :set_constructuser_manager with that find condition, with nil case handling, still has no impact when stating the policy
def show?
set_constructuser_manager?
end
Update: as per comment below. Pundit class private method
def contractorconstruct
#contructs = Construct.where(['constructusers.user_id = ?', user]).joins(:users).all
#contractorconstruct ||= Contractor.where(['construct_id IN (?)', #contructs]).first
end
and action rule
|| contractorconstruct?
returns no method error.
manager? will be a method on an instance of Constructuser, not on the relation. Think about what you are asking, "Is this constructusers a manager?" - it makes no sense. How would the computer know what constructuser you are talking about?
If a user has_many constructusers, in order to use manager? you need to find the instance you are concerned about. If this is in the ConstructPolicy, then you need to find the specific constructuser that links user to the construct that you are authorizing, then check if that single constructuser is manager?.
If you are in the Construct controller, you'll have something like
class ConstructsController
before_action :set_construct
def show
authorize #construct
# ...
end
# ...
end
In your policy then, user will be the current user and record will be #construct.
class ConstructPolicy
def show?
user.admin? || constructuser.manage?
end
private
def constructuser
#constructuser ||= Constructuser.find_by(user_id: user, construct_id: record)
end
end

What is the most rails like way to structure the following

I am using devise for two different types of user in my app. They are called user and professional.
I currently have a simple resource based controller called MessagesController which pulls out messages for the current professional like this
class MessagesController < ApplicationController
def index
#messages = Message.find_all_by_profession_id(current_professional.id)
end
end
I want to find the best way of keeping this controller but changing the query based on the type of user that is logged in. I want the same to happen for all actions in the resource (index, new, create, update etc)
I know I can do this
if current_user
#messages = Message.find_all_by_user_id(current_user.id)
else
#messages = Message.find_all_by_profession_id(current_professional.id)
end
but this would be bulky and messy across all actions. I'm sure there must be a better way. What is the most rails like way of doing this? Should I be creating a completely new controller do handle user based messages?
I can think of two ways:
You can put your code inside the initialize method of your controller:
def initialize
if current_user
#messages = Message.find_all_by_user_id(current_user.id)
else
#messages = Message.find_all_by_profession_id(current_professional.id)
end
super
end
Or you can create a before_filter :
class MessagesController < ApplicationController
before_filter :get_messages
private
def get_messages
if current_user
#messages = Message.find_all_by_user_id(current_user.id)
else
#messages = Message.find_all_by_profession_id(current_professional.id)
end
end
end
IMHO, i think you can move this chunk of code to the model, so the controller only make a call passing the user parameter and gets all messages from the model.
# messsages_controller.rb
#messages = Message.find_all_messages(current_user, current_professional)
# message.rb
def self.find_all_messages(user, professional)
if user
self.find_all_by_user_id(user.id)
else
self.find_all_by_profession_id(professional.id)
end
end
I think it is better for this kind of code to be on your model. Of course you can improve the if/else code, but i am out of ideas now.

Devise + Declarative_authorization + role_model + different users model name : undefined method `current_user'

I do have this famous error : "undefined method `current_user'" with declarative authorization, though I set up this variable in the application_controller.rb :
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_current_user
protected
def set_current_user
Authorization.current_user = current_admin_utilisateur
end
end
I'm using a table called "admin_utilisateurs" instead of "users". Which was activated in Devise with : "rails generate devise admin_utilisateur"
Devise is working great.
For info, I customized my users table (admin_utilisateurs) with "roles_model" gem, So that I do have an attribut roles_mask that allows me to manage different roles while providing a role_symbols method for declarative authorization.
The problem is now that I got this strange error though the Authorization.current_user is set by the application_controller.rb.
This is the begning of one my resource controllers that procude the error :
class PubResponsablesController < ApplicationController
before_filter :authenticate_admin_utilisateur!
filter_resource_access
...
end
I search by google for this error, but none of the results provide a working solution.
Could anybody help me on this ?
Many Thanks
Ok this is the final answer.
I modified my app/controller/application_controller.rb because I don't use the #current_user instance variable in the views :
class ApplicationController < ActionController::Base
protect_from_forgery
# This is mandatory if you want to secure as well your app/models
before_filter :set_current_user
# This method is required by declarative_authorization on every controller
# that is using filter_resource_access (or any other declarative_auth.. mechanism)
def current_user
current_admin_utilisateur
end
protected
def set_current_user
Authorization.current_user = current_admin_utilisateur
end
end
As I said I'm using the following gem in collaboration :
gem devise for the authentication
The user-model-name is "admin_utilisateur" instead of "user", but it could have been : account, member, group or what you need.
gem role_model to provide a brillant role method "role_symbols" to my user model
*The method role_symbols was returning a "Set" subclass instead of an "Array" but after quick post on Github, the developer (martinrehfeld) fixed this compatibility issue in a lightning matter of minutes. Great !*
gem declarative_authorization to provide access management based on roles.
My will to use a different model name than "user" is confirmed to work by the following post.
The only thing that declarative_authorization needs is the current_user method on each controller. As I'm using a different model name with Devise (such as admin_utilisateur, account, member, ...) the helper created by devise have a different name. Instead of current_user, it is current_admin_utilisateur (or current_account, current_member). So I have to create my own current_user method.
The role_symbols method required by declarative_authorization is provided by role_model gem.
I hope this will help other developer cause I spent two days to sort out how all this fabric works together. Devise took me even more with routing issues.
My few cents to RoRrrr ;-)
Ok I managed to solve this error by modifying my app/controller/application_controller.rb :
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_current_user
def current_user
#current_user = current_admin_utilisateur
end
protected
def set_current_user
Authorization.current_user = current_admin_utilisateur
end
end
I just created my own current_user method that create an instance variable #current_user. This one is initialized with the value of current_admin_utilisateur which is based on Devise helpers and my customized user model called admin_utilisateur. (my model could as well be called acount, member, or whatever...)
I placed my current_user method in application_controller in order that it to be available in every controller of my application.
Now, I'm getting another error :
User.role_symbols doesn't return an Array of Symbols (#<RoleModel::Roles: {:developer, :admin, :coordinator, :manager, :assistant, :distributor, :exporter, :historian}>)
I don't understand because the roles_model gem provide an alias method 'role_symbols' to the admin_utilisateur model.

How to call the ApplicationController helper method in model? RoR

Guys I've a helper method in ApplicationController as follows
class ApplicationController < ActionController::Base
helper_method :current_user
end
And I want to call it in my model (say Project) as follows:
class Project < ActiveRecord::Base
after_save :update_other_tables
private
def update_other_tables
# if project saves, Create a new insteance into User Projects
#userproject=UserProject.new(
:user_id=>User.current_user.id,
:project_id=>self.id
)
#userproject.save
end
Here, I'm getting error like undefined method `current_user'
So, How to call the this method in model? please help me
I would do it in the following way:
class ApplicationController < ActionController::Base
# Assignment method
def current_user=(user)
#current_user = user
end
# Returns the current user, nil otherwise
def current_user
#current_user
end
end
Now when a user logs in you can set:
current_user = the_user
And from the point onwards you can do:
current_user.id
Hope this helps.
not a ruby guy but I am guessing the User here isnt referring to the User that is present in your controller namespace and that is why there is no method current_user on the object.. You probably need to pass the User object to your helper method or just pass the id..
The above theory is from my experience dealing with something similar in .Net MVC so please correct me if I am wrong and I will delete the answer..
P.S: was too long of a comment

Rails 3 subsequent forms submission (second dependent on first)

I am trying to achieve a subsequent form submission. To clarify things -
I submit a form for #post
then once that #post is created I would immediately (under the hood) like to submit the form for #associations.
The catch is, this second form submission would require the post_id field from the newly created #post.
What would be the best way to achieve this? Would nested forms help me pull the newly created #post.id? Kindly help me with this.
If this is something that should happen whenever you create a Post, then you should use active callbacks to achieve that :
class Post < ActiveRecord::Base
after_create do |post|
# create your association using post.id
end
end
or, you can write it like that also :
class Post < ActiveRecord::Base
after_create :after_create_post
def after_create_post
# create your association using self.id
end
end
Otherwise, if this is something specific to a controller action, you should simple do something like this :
class PostsController < ApplicationController
def create
#post = current_user.posts.build(params[:post])
# then use the #post.id to build your association. something like
#post.associations.build(:prop1 => 'value1', :prop2 => 'value2')
end
end
Hope this helps!