Authentication and user management questions


#1

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.


#2

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


#3

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| %>

user: <%= f.text_field :user_name %>

pass: <%= f.password_field :password %>

<%= 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:

user: <%= text_field_tag :user_name, params[:user_name] %>

pass: <%= password_field_tag :password, params[:password] %>

<%= submit_tag 'login' %>

And things worked (except that the sessions weren’t saved in the
database). Help?..


#4

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.


#5

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
:slight_smile:

Thanks!


#6

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| %>

user: <%= f.text_field :user_name %>

pass: <%= f.password_field :password %>

<%= 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


#7

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.


#8

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