Devise login and current_user

527 views
Skip to first unread message

Peter Hamilton

unread,
May 8, 2012, 11:15:08 PM5/8/12
to declarative_...@googlegroups.com
I am having trouble getting Devise login to work.  When the form is posted, my before_filter in application_controller.rb assigns Authentication.current_user = current_user.  Since current_user is not set at this point (as the login has not happened), it uses guest permission throughout the request, even though current_user is soon set.

So I get:

Authorization::NotAuthorized in Devise::SessionsController#create

No matching rules found for update for #<Authorization::AnonymousUser:0x007fa09af957d0 @role_symbols=[:guest]> (roles [:guest], privileges [:update, :all], context :users).

Does anyone have a good solution to deal with this?

jearlu

unread,
May 8, 2012, 11:36:52 PM5/8/12
to declarative_authorization
Peter,

You can grant access to the devise login pages by adding the following
rules (you'll have to customize accordingly based on the devise
modules that you are utilizing):

role :guest do
has_permission_on :devise_sessions, :to =>
[:new, :create, :delete]
has_permission_on :devise_passwords, :to =>
[:new, :create, :edit, :update]
has_permission_on :devise_unlocks, :to => [:show, :new, :create]
has_permission_on :devise_invitations, :to => [:edit, :update]
end

Regards,
jearlu

Edward Rudd

unread,
May 9, 2012, 12:36:11 AM5/9/12
to declarative_...@googlegroups.com
If you look through the archives there is a post I made with a monkey patch that solves this without having to give gust the ability to write to the user model.

Sent from my iPad
--
You received this message because you are subscribed to the Google Groups "declarative_authorization" group.
To post to this group, send email to declarative_...@googlegroups.com.
To unsubscribe from this group, send email to declarative_author...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/declarative_authorization?hl=en.

Peter Hamilton

unread,
May 9, 2012, 12:54:42 AM5/9/12
to declarative_...@googlegroups.com
Since I'm using declarative at the model level, I'm not sure jearlu's solution will work.

Here's my gross hack... (we've got a demo tomorrow and a slow internal rollout of the tool, so I'm not super worried about security for now.  I'll come up will a better solution over the next few days):

  def set_current_user
    Authorization.ignore_access_control true
    Authorization.current_user = current_user
    Authorization.ignore_access_control false
  end  

I can hear everyone cringing...
Message has been deleted

jearlu

unread,
May 9, 2012, 1:21:34 AM5/9/12
to declarative_authorization
The whole point of the login page is to authenticate users, which by
definition requires a guest role (or no authorization). My solution
doesn't allow write access to the user model in any way, shape or
form. I don't see anything to worry about security-wise.

Also, you can't do any model level checking when you don't yet have a
user to authorize against, so I don't see why it wouldn't work for
you. Any model level authorization would happen after login/
authentication.

On May 8, 11:54 pm, Peter Hamilton <peterghamil...@gmail.com> wrote:
> Since I'm using declarative at the model level, I'm not sure jearlu's
> solution will work.
>
> Here's my gross hack... (we've got a demo tomorrow and a slow internal
> rollout of the tool, so I'm not super worried about security for now.  I'll
> come up will a better solution over the next few days):
>
>   def set_current_user
>     Authorization.ignore_access_control true
>     Authorization.current_user = current_user
>     Authorization.ignore_access_control false
>   end
>
> I can hear everyone cringing...
>
>
>
>
>
>
>
> On Tue, May 8, 2012 at 10:36 PM, Edward Rudd <ur...@outoforder.cc> wrote:
> > If you look through the archives there is a post I made with a monkey
> > patch that solves this without having to give gust the ability to write to
> > the user model.
>
> > Sent from my iPad
>

Peter Hamilton

unread,
May 9, 2012, 1:43:14 AM5/9/12
to declarative_...@googlegroups.com
jearlu: It didn't work.

The problem is with current_user (from devise).  It calls warden.authenticate which throws the error I show above.  Interestingly enough, it only fails once.  If you call it twice (in the debugger) it works the second time.  I started climbing down the rabbit hole to find where it fails, but it got pretty deep pretty fast.  My guess is somewhere it does @user.current_sign_in_at = Date.now, as well as updating all that other stuff in there about the current session.  I don't know why it would fail once and then work the second time.  Maybe it does something like this:

if @user.nil?
  @user = get_user
  @user.update_login_data
  @user.save
end

So the first time it fails when @user.save is called.  The second time, @user is no longer nil?.  Inspection in the debugger shows that the login stats are updated, but never get persisted to the database.

In any case, the only solution I've found so far is to disable and reenable access_control.

Peter Hamilton

unread,
May 9, 2012, 1:55:15 AM5/9/12
to declarative_...@googlegroups.com
Also, the reason I want model level permissions is query rewriting.  We have a lot of Users.with_permissions_to(:read) and Users.with_permissions_to(:update) in our code, along with a lot of less experienced guys doing most of the business logic.  config/authorization.rb is much easier to walk them through than other parts of the code.

jearlu

unread,
May 9, 2012, 2:20:34 AM5/9/12
to declarative_authorization
Something's not quite adding up... Do you have "devise_for :users" in
your routes config file and are you using the path "/users/sign_in"
for the login page?

I tried to reproduce your error in one of my apps and was only able to
do so when I went to "/users" (sans "sign_in"). If you are hitting the
right path, but not passing authorization you should be seeing the
following error (note the context is devise_sessions, not users as
appears in your initial post):

Processing by Devise::SessionsController#new as HTML
Permission denied: No matching rules found for new for
#<Authorization::AnonymousUser:0x00000103ebc3d8
@role_symbols=[:guest]> (roles [:guest], privileges
[:new, :create, :manage], context :devise_sessions).


On May 9, 12:55 am, Peter Hamilton <peterghamil...@gmail.com> wrote:
> Also, the reason I want model level permissions is query rewriting.  We
> have a lot of Users.with_permissions_to(:read) and
> Users.with_permissions_to(:update) in our code, along with a lot of less
> experienced guys doing most of the business logic.  config/authorization.rb
> is much easier to walk them through than other parts of the code.
>

Peter Hamilton

unread,
May 9, 2012, 2:29:46 AM5/9/12
to declarative_...@googlegroups.com
I don't have any controller level permissions set up.  It's all in the models.  So I wouldn't get such an error.  My context is :users, as in the User model.  Here's the output in the console:

Started POST "/users/sign_in" for 127.0.0.1 at 2012-05-09 00:28:34 -0600
Processing by Devise::SessionsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"+GTvJflMxVlwx0cP2ZblsV3JGefEHnDWj9JBw4+qcJI=", "user"=>{"email"=>"petergh...@gmail.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Sign in"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."email" = 'petergh...@gmail.com' LIMIT 1
   (0.1ms)  begin transaction
   (0.0ms)  rollback transaction
Completed 500 Internal Server Error in 75ms

Authorization::NotAuthorized (No matching rules found for update for #<Authorization::AnonymousUser:0x007fcecdd4fe60 @role_symbols=[:guest]> (roles [:guest], privileges [:update, :all], context :users).):
  app/controllers/application_controller.rb:9:in `set_current_user'

jearlu

unread,
May 9, 2012, 2:48:04 AM5/9/12
to declarative_authorization
But you are using controller authorization... that is what the error
is telling you. Look at the error, the part that says "No matching
rules found for update" is telling you that you are requiring
authorization for the update action.

On May 9, 1:29 am, Peter Hamilton <peterghamil...@gmail.com> wrote:
> I don't have any controller level permissions set up.  It's all in the
> models.  So I wouldn't get such an error.  My context is :users, as in the
> User model.  Here's the output in the console:
>
> Started POST "/users/sign_in" for 127.0.0.1 at 2012-05-09 00:28:34 -0600
> Processing by Devise::SessionsController#create as HTML
>   Parameters: {"utf8"=>"✓",
> "authenticity_token"=>"+GTvJflMxVlwx0cP2ZblsV3JGefEHnDWj9JBw4+qcJI=",
> "user"=>{"email"=>"peterghamil...@gmail.com", "password"=>"[FILTERED]",
> "remember_me"=>"0"}, "commit"=>"Sign in"}
>   User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."email" = '
> peterghamil...@gmail.com' LIMIT 1

Peter Hamilton

unread,
May 9, 2012, 3:09:12 AM5/9/12
to declarative_...@googlegroups.com
I guess you're right.  I got confused between :users meaning the User model and :users meaning the controller.

I still can't get the fix to work without disabling DA for that one line of code.

Peter Hamilton

unread,
May 9, 2012, 3:33:41 AM5/9/12
to declarative_...@googlegroups.com
I'm still lost here.  Here's the trace leading up to the Authorization::NotAuthorized  

declarative_authorization (0.5.5) lib/declarative_authorization/authorization.rb:192:in `permit!'

declarative_authorization (0.5.5) lib/declarative_authorization/in_model.rb:159:in `block (3 levels) in using_access_control'

activesupport (3.2.2) lib/active_support/callbacks.rb:407:in `_run__4541110893238119253__update__4112429618023661094__callbacks'

activesupport (3.2.2) lib/active_support/callbacks.rb:405:in `__run_callback'

activesupport (3.2.2) lib/active_support/callbacks.rb:385:in `_run_update_callbacks'

activesupport (3.2.2) lib/active_support/callbacks.rb:81:in `run_callbacks'

activerecord (3.2.2) lib/active_record/callbacks.rb:272:in `update'

.....


It fails right after calling update on the model.  So it's definitely the model.

Eduard is right on.  Here's the message where he has his patch to devise: http://groups.google.com/group/declarative_authorization/browse_thread/thread/e7fcd2eceec6e554

Sorry for the multiple replies and thanks for the help.

jearlu

unread,
May 9, 2012, 4:26:11 AM5/9/12
to declarative_authorization
Edward's reason for the monkey patch and comment about an anonymous
user setting anyone's password is a non-issue. Devise is really quite
sophisticated. Password resets require a token that expires after a
configurable amount of time (I think the default is something like 2
hours), so it's really nothing to worry about.

It's hard to say what's wrong without full access to your codebase.
I'm posting the relevant bits from a number of key files. You won't be
able to copy and paste, but it might help you verify that you have all
the bases covered. One quick note, in application_controller, I
include the line,
"filter_access_to :index, :new, :create, :show, :update, :destroy",
this is simply an easy way to specify authorization in one single
place for all of my controllers. There is also a line that I have
commented out, "check_authorization :unless => :devise_controller?",
which might be something you want to play around with.

One final bit of advice... won't pertain to logging in, but If you are
doing attribute on your models, remember to include :attribute_check
=> true when calling filter_access_to, such as in the following line:

filter_access_to :create, :update, :destroy, :attribute_check => true

This is easily overlooked and trips up many people who are new to
devise. Google "declaritive_authorization attribute_check" for more
information.

Here are the relevant code snippets that I mentioned above:

[routes.rb]
Myapp::Application.routes.draw do
devise_for :users, :controllers => { :invitations => 'devise/
invitations', :password_expired => 'devise/password_expired' }
end


[authorization_rules.rb]
authorization do
role :guest do
has_permission_on :devise_sessions, :to =>
[:new, :create, :delete]
has_permission_on :devise_passwords, :to =>
[:new, :create, :edit, :update]
has_permission_on :devise_unlocks, :to => [:show, :new, :create]
has_permission_on :devise_invitations, :to => [:edit, :update]
end
end

[application_controller.rb]
class ApplicationController < ActionController::Base
protect_from_forgery
# check_authorization :unless => :devise_controller?

# filter_resource_access
filter_access_to :index, :new, :create, :show, :update, :destroy

layout :layout_by_resource
before_filter :set_current_user

protected

def set_current_user
Authorization.current_user = current_user
end

def layout_by_resource
if devise_controller?
"login"
else
"application"
end
end
end

[user.rb]
class User < ActiveRecord::Base

attr_accessible :client_id, :email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :address, :city, :state, :country, :zip, :url, :business_phone, :mobile_phone, :fax


devise :database_authenticatable, :recoverable, :rememberable, :trackable, :lockable, :timeoutable, :password_expirable, :password_archivable, :secure_validatable, :invitable, :invite_for
=> 2.weeks

has_and_belongs_to_many :roles

validates :email, :first_name, :last_name, :client_id, :presence =>
true
validates :password, :length => { :in => password_length, :too_short
=> :password_too_short, :too_long => :password_too_long }, :format =>
{ :with => password_regex, :message
=> :invalid_password_format }, :confirmation => { :message =>
"Confirmation does not match password." }, :if => :password_required?
#:passwords_dont_match }

def role_symbols
(roles || []).map {|r| r.name.to_sym}
end
end


Hope that helps... good luck!


On May 9, 2:33 am, Peter Hamilton <peterghamil...@gmail.com> wrote:
> I'm still lost here.  Here's the trace leading up to the
> Authorization::NotAuthorized
>
> declarative_authorization (0.5.5)
> lib/declarative_authorization/authorization.rb:192:in `permit!'
>
> declarative_authorization (0.5.5)
> lib/declarative_authorization/in_model.rb:159:in `block (3 levels) in
> using_access_control'
>
> activesupport (3.2.2) lib/active_support/callbacks.rb:407:in
> `_run__4541110893238119253__update__4112429618023661094__callbacks'
>
> activesupport (3.2.2) lib/active_support/callbacks.rb:405:in
> `__run_callback'
>
> activesupport (3.2.2) lib/active_support/callbacks.rb:385:in
> `_run_update_callbacks'
>
> activesupport (3.2.2) lib/active_support/callbacks.rb:81:in `run_callbacks'
>
> activerecord (3.2.2) lib/active_record/callbacks.rb:272:in `update'
> .....
>
> It fails right after calling update on the model.  So it's definitely the
> model.
>
> Eduard is right on.  Here's the message where he has his patch to devise:http://groups.google.com/group/declarative_authorization/browse_threa...
>
> Sorry for the multiple replies and thanks for the help.
>
> ...
>
> read more »

Peter Hamilton

unread,
May 9, 2012, 4:40:16 AM5/9/12
to declarative_...@googlegroups.com
The heart of this issue though is that you cannot do some model level authorization rules on your devise model.  There are many secure ways to get around it, but it is something that is broken between the two projects.  Disabling DA before calling current_user for the first time is from what I can tell the best way to keep tight model controls, which I need.  

jearlu

unread,
May 9, 2012, 5:30:03 AM5/9/12
to declarative_authorization
I'm doing it without any problem. The line,
"filter_access_to :create, :update, :destroy, :attribute_check =>
true" comes from my users_controller. In authorization_rules.rb, I
have one role that has the following rule:

has_permission_on :users do
to [:manage]
# user refers to the current_user when evaluating
if_attribute :client_id => is {user.client_id}
end

And it works like a charm.

In this scenario creating a new user poses a bit of a problem, because
you don't have the new user record loaded as it hasn't yet been
created. In this case you will need to do the following in your
users_controller:

[user_controller.rb]
class UsersController < ApplicationController
before_filter :new_user, :only => :create
filter_access_to :create, :update, :destroy, :attribute_check =>
true

def index...

protected
def new_user
@user = User.new(params[:user])
end
end

To be clear, the above authorization logic has nothing to do with
application login. Logging in has nothing to do with model level
authorization for the simple fact that you haven't yet established the
identity of the user.

It seems that you are conceptually confusing devise session initiation
with the user model. Devise has it's own controller (many of the
devise modules do as well for that matter) that has nothing to do with
the users controller. It has its own version of new, create, and
destroy that are different of the new, create, and destroy methods in
the users controller. The create action that occurs when logging in
creates a devise session, not a user. The model authorization on users
only occurs when displaying a list of users or creating/editing/
deleting a user... this all occurs well after devise has initiated
your session.
> ...
>
> read more »

Peter Hamilton

unread,
May 9, 2012, 5:46:42 AM5/9/12
to declarative_...@googlegroups.com
Devise DOES update the model when you log in.  Devise updates your user during session creation.  It updates fields like :sign_in_count and :last_sign_in.  It may be a specific module like :trackable that causes this, I'm not sure.

When Devise updates your user, DA hasn't been passed a user, so you are still operating as :guest according to DA.  If you have the following line in your users.rb model:

using_access_control

then :guest MUST have :update permission on :users, or session creation will fail.

Nowhere in my codebase am I using "filter_access_to".  I'm not trying to protect my controllers, I'm trying to project my models.  I understand the difference.  I'm mainly using the query rewriting abilities of DA to make my life easier.

> ...
>
> read more »

jearlu

unread,
May 9, 2012, 6:08:11 AM5/9/12
to declarative_authorization
Correct, it does update the user model, but this happens after the
user has been authenticated, so there are no security issues. My point
was that none of the model level validation kicks in until the session
has been initiated. Once the session has been initiated the current
user is known and at this point when devise updates the user record,
it is no different than the user clicking on the users tab and
updating a user record. When devise updates the user record for
current_user, current_user is no longer the AnonymousUser that is
giving you the trouble.
> ...
>
> read more »

Edward Rudd

unread,
May 9, 2012, 8:40:16 AM5/9/12
to declarative_authorization
Jearlu, from the code you posted before, it seems you are not enabling decl auth in your model. only in ALL controllers. This is different from Peters and my situations. I explicitly enable DA in controllers (so every controller except the Devise ones), and I have DA enabled in ALL models. Thus when warden logs in a user, it first updates the Trackable and Rememberable fields and THEN assigns the current_user. likewise if you trigger the Recoverable code devise will update the recoverable fields and NOT log the user it.

So in order to workaround that issue I had to wrap those portions of devise with without_access_control blocks.

https://gist.github.com/1712576

This allows me to grant guest NO privileges as I have wrapped certain places where devise is allowed to update "as guest". In theory this should work on any newer version of Devise if the modules are named the same. Also note that it currently doesn't have the registration code wrapped as we aren't using that functionality in our project.

In newer projects I am simply rolling my own auth pieces so I have better control as I don't really use any other piece of Devise.
> --
> You received this message because you are subscribed to the Google Groups "declarative_authorization" group.
> To post to this group, send email to declarative_...@googlegroups.com.
> To unsubscribe from this group, send email to declarative_author...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/declarative_authorization?hl=en.
>

Edward Rudd
OutOfOrder.cc
Skype: outoforder_cc
317-674-3296






Reply all
Reply to author
Forward
0 new messages