Forum: Ruby on Rails authentication and user management questions

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
7db3b1680807d393d40260591b4ae5e0?d=identicon&s=25 sa 125 (sa125)
on 2009-02-25 09:47
Hi -

I am using a fairly standard authentication in my app (user model,
before_filter in application_controller). This authentication is taken
almost entirely from the 3rd ed. of the agile web dev w/ rails. A few
questions about all that -

1. I'm still in dev environment, but I noticed that I'm not being logged
out between sessions. I even restarted my machine and pointed my browser
to a page, and wasn't taken to the login page first. Only if I
explicitly invoke the logout action does it actually log me out. How do
I force a logout between sessions? I tried clearing the sessions table
(rake db:sessions:clear) but that didn't do it.

2. what's a good way to implement automatic logout after some
inactivity?

3. I have many users and need to manage different permission levels.
Some users should be able to see and edit a certain page, others need to
view only, and some shouldn't be able to access certain pages. What
would be a good way to manage that? I thought about creating a page
model and controller, and then using some joined table page_users or
something that keeps the user_id, page_id and the different boolean
permissions (can_view, can_edit, etc..). Then I'd probably need to add a
before_filter in each controller/action..

I'm quite new to rails, so I'd love to hear any better or more efficient
ideas - thanks.
80e4cb97cae5c8d745f72337d93fd8f2?d=identicon&s=25 MaD (Guest)
on 2009-02-25 10:40
(Received via mailing list)
for your permissions problem I'd suggest the implementation of roles
or groups. that way you can add users to groups or give them roles,
which can be checked in a before_filter in your controllers.

sessions:
if you are using activerecordstore (and thus your sessions are saved
in a db-table) deleting those entries should result in a logout. there
should be configuration in your environment like this:
  config.action_controller.session_store = :active_record_store

if you clean that table and you are still logged in, it seems you
store your session (or at least your login-info) inside the client-
side cookies (see cookiestore). if in doubt check your cookies for
e.g. with firebug/firecookie.

for automatical timeout and removal put the following in your
application controller:
  # session-timeout after inactivity of one hour.
  MAX_SESSION_PERIOD = 1800

  before_filter :session_expiry

  # checks and (re-)calculates expiry time for sessions
  def session_expiry
    # if there is a session[:expiry_time], check it
    reset_session if session[:expiry_time] and session[:expiry_time] <
Time.now
    # reset expiry_time
    session[:expiry_time] = MAX_SESSION_PERIOD.seconds.from_now
    return true
  end
7db3b1680807d393d40260591b4ae5e0?d=identicon&s=25 sa 125 (sa125)
on 2009-02-25 11:35
MaD wrote:
> for your permissions problem I'd suggest the implementation of roles
> or groups. that way you can add users to groups or give them roles,
> which can be checked in a before_filter in your controllers.

I thought about creating groups, but then won't this complicate things?
I'll have group_pages table, and how do I manage those? can a user
belongs to many groups?


> for your permissions problem I'd suggest the implementation of roles
> or groups. that way you can add users to groups or give them roles,
> which can be checked in a before_filter in your controllers.
>
> sessions:
> if you are using activerecordstore (and thus your sessions are saved
> in a db-table) deleting those entries should result in a logout. there
> should be configuration in your environment like this:
>   config.action_controller.session_store = :active_record_store

after commenting the above line, I got an InvalidAuthentictyToken error,
which probably comes from my login form. This is how my view looks now:

<% form_for :user do |f| %>
  <p>user: <%= f.text_field :user_name %></p>
  <p>pass: <%= f.password_field :password %></p>
  <%= f.submit 'login' %>
<% end %>

I checked the markup and I can see the hidden field containing the
authenticity token.. So I'm not sure why this messes up.

Before these changes I had this view:

<p>user: <%= text_field_tag :user_name, params[:user_name] %></p>
<p>pass: <%= password_field_tag :password, params[:password] %></p>
<%= submit_tag 'login' %>

And things worked (except that the sessions weren't saved in the
database). Help?..
80e4cb97cae5c8d745f72337d93fd8f2?d=identicon&s=25 MaD (Guest)
on 2009-02-25 15:08
(Received via mailing list)
> I thought about creating groups, but then won't this complicate things?
> I'll have group_pages table, and how do I manage those? can a user
> belongs to many groups?

it's actually best to create a many-to-many relationship (user habtm
groups).
there are many plugins outthere that provide just what you need. if
you don't want to just use one of these (either a complete
authentication-plugin or just something like acts_as_role), you could
still take a look at them for inspiration.

> >   config.action_controller.session_store = :active_record_store
> after commenting the above line, I got an InvalidAuthentictyToken error,
> which probably comes from my login form.

just to make sure: you are using active_record_store now, right?

> I checked the markup and I can see the hidden field containing the
> authenticity token.. So I'm not sure why this messes up.

try to delete your browser's cookies and cache as well as your session
table. maybe you have a conflict with one of the old sessions/cookies.

additionally check that you have the following:
  # environment.rb:
  config.action_controller.session = {
    :session_key => '_myapp_session',
    :secret      => 'secretpass' # use a long and good secret!
  }

  config.action_controller.session_store = :active_record_store

  # application.rb
  protect_from_forgery :secret => 'secretpass'

that should do it.
7db3b1680807d393d40260591b4ae5e0?d=identicon&s=25 sa 125 (sa125)
on 2009-02-26 09:46
> just to make sure: you are using active_record_store now, right?

Yeah.

> try to delete your browser's cookies and cache as well as your session
> table. maybe you have a conflict with one of the old sessions/cookies.

Deleted all cookies, cache and authenticated sessions (in firefox -
Ctrl+Shift+Del).

> additionally check that you have the following:
>   # environment.rb:
>   config.action_controller.session = {
>     :session_key => '_myapp_session',
>     :secret      => 'secretpass' # use a long and good secret!
>   }

Definitely have all those.

>   config.action_controller.session_store = :active_record_store
>
>   # application.rb
>   protect_from_forgery :secret => 'secretpass'

Yup, that too.


When I submit the login form, I get the error screen and the request
parameters are showing that an authenticity token is submitted:

{"commit" => "login"
 "authenticty_token" => "bnadsas9dadasd09as8931012kjk12301i23",
 "user" => { "user_name" => "newbie",
             "password" => "topsecret" } }

So I don't know where the token comes from if it doesn't match the one
submitted - all sessions, cookies and cache were cleared.

Again, my login form looks like this:

<% form_for :user, :url => { :action => 'do_login' } do |f| %>
  <p>user: <%= f.text_field :user_name %></p>
  <p>pass: <%= f.password_field :password %></p>
  <%= f.submit 'login' %>
<% end %>

My do_login action is this:

def do_login
  if request.post?
    user = User.authenticate(params[:user_name], params[:password])
    if user
      session[:user_id] = user.id
      uri = session[:original_uri]
      session[:original_uri] = nil
      redirect_to uri || home_page
    else
      flash.now[:notice] = "Invalid user/password - please try again"
    end
  end
end

I should mention that in my application controller I have:

before_filter :authorize, :except => :do_login

def authorize
  unless User.find_by_id(session[:user_id])
    session[:original_uri] = request.url
    flash[:notice] = "please log in"
    redirect_to :controller => 'login', :action => 'do_login'
  end
end
7db3b1680807d393d40260591b4ae5e0?d=identicon&s=25 sa 125 (sa125)
on 2009-02-27 07:09
It seems the problem stemmed from the session_expiry check:

> for automatical timeout and removal put the following in your
> application controller:
>   # session-timeout after inactivity of one hour.
>   MAX_SESSION_PERIOD = 1800
>
>   before_filter :session_expiry #####<-- this was the problem
>
>   # checks and (re-)calculates expiry time for sessions
>   def session_expiry
>     # if there is a session[:expiry_time], check it
>     reset_session if session[:expiry_time] and session[:expiry_time] <
> Time.now
>     # reset expiry_time
>     session[:expiry_time] = MAX_SESSION_PERIOD.seconds.from_now
>     return true
>   end

Although I used 1800 seconds, it reset the session before I logged in,
rendering the authenticity token invalid. Removing that before filter
solved that. I'll have to keep looking into the whole auto-logout thing
:)

Thanks!
80e4cb97cae5c8d745f72337d93fd8f2?d=identicon&s=25 MaD (Guest)
on 2009-02-27 10:38
(Received via mailing list)
at least that token-problem is solved.

but it's still weird, cause that session_expiry code is actually
working on one of my apps. are you sure you don't have some sort of
typo? (sth. like > instead of <) i would debug that piece of code to
see, why the session is reset.
btw: 1800 sec is of course half an hour (not what my comment stated)
*g*
006a4831843b48f6102d5d3cf92ae283?d=identicon&s=25 Shilo Ayalon (gte351s)
on 2009-03-03 08:54
MaD wrote:
> at least that token-problem is solved.
>
> but it's still weird, cause that session_expiry code is actually
> working on one of my apps. are you sure you don't have some sort of
> typo? (sth. like > instead of <) i would debug that piece of code to
> see, why the session is reset.
> btw: 1800 sec is of course half an hour (not what my comment stated)
> *g*

My code seems intact -

MAX_SESSION_PERIOD = 1800
before_filter :check_session_expiration

def check_session_expiration
  if session[:expiry_time] and seesion[:expiry_time] > Time.now
    reset_session
  end

  session[:expiry_time] = MAX_SESSION_PERIOD.seconds.from_now
  return true
end

I'm not really sure what about it messes things up - on the surface it
seems as though it should work. For now I'll omit it and try to
gradually introduce it.

Regarding my previous question on using groups and users - I decided to
go with users that can only belong to a single group - this makes it
easier to manage on my end, and there really is no need to add the
many-to-many groups/users at this point.

Say that I have many groups (10 or so) and each group has varying
permissions on pages across the site. I also created a page and
permission models, such that:

page has_and_belongs_to_many :groups, :join_table => 'permissions'
group has_and_belongs_to_many :pages, :join_table => 'permissions'

The permissions table holds the page_id and group_id, and booleans for
can_read, can_write and can_execute.

My question is - what's the best way to manage these permissions in my
controllers? I thought about adding an if statement at the beginning of
each action to see if the user's group can access this page, but this
seems like a lot of duplication:

def index
  if user.group.can_access_page?(page_id)
    # render index
  else
    # render some 'you cannot access this page' message
  end
end

def update
  if user.group.can_write_page?(page_id)
   # perform update
  else
   # render 'sorry - no updates allowed'
  end
end

and so on. I could just restrict access to the controller with before
filter, but what if the user uses direct routes in the address bar
(/controller/new/1)?

I'd appreciate insights on this - I really want to dry up my code as
much as possible - thanks.
This topic is locked and can not be replied to.