2011-02-01 09:57:29 +08:00
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
class PseudonymSession < Authlogic :: Session :: Base
last_request_at_threshold 10 . minutes
verify_password_method :valid_arbitrary_credentials?
login_field :unique_id
find_by_login_method :custom_find_by_unique_id
remember_me_for 2 . weeks
2011-08-10 06:54:59 +08:00
2011-10-25 06:30:23 +08:00
attr_accessor :remote_ip , :too_many_attempts
2011-08-10 06:54:59 +08:00
# we need to know if the session came from http basic auth, so we override
# authlogic's method here to add a flag that we can check
def persist_by_http_auth
controller . authenticate_with_http_basic do | login , password |
if ! login . blank? && ! password . blank?
send ( " #{ login_field } = " , login )
send ( " #{ password_field } = " , password )
@valid_basic_auth = valid?
return @valid_basic_auth
end
end
false
end
def used_basic_auth?
@valid_basic_auth
end
2011-10-25 06:30:23 +08:00
2014-07-24 01:14:22 +08:00
# In authlogic 3.2.0, it tries to parse the last part of the cookie (delimited by '::')
# as a timestamp to verify whether the cookie is stale.
# This conflicts with the uuid that we use instead in that place,
# so skip that check for now, to keep behavior similar between Rails 2 and 3.
def remember_me_expired?
false
2013-12-20 04:35:12 +08:00
end
2014-07-24 01:14:22 +08:00
secure CanvasRails :: Application . config . session_options [ :secure ]
httponly true
2014-02-28 04:43:39 +08:00
2014-07-24 01:14:22 +08:00
# modifications to authlogic's cookie persistence (used for the "remember me" token)
# see the SessionPersistenceToken class for details
def save_cookie
return unless remember_me?
token = SessionPersistenceToken . generate ( record )
controller . cookies [ cookie_key ] = {
:value = > token . pseudonym_credentials ,
:expires = > remember_me_until ,
:domain = > controller . cookie_domain ,
:httponly = > httponly ,
:secure = > secure ,
}
2011-11-18 05:56:47 +08:00
end
make "stay logged in" use a one-time token
closes #6382
Previously, the "stay logged in" cookie just used the authlogic default
implementation, which is the pseudonym persistence_token. This is a
problem, because that persistence_token only ever changes when the
pseudonym password changes, so it's the same everywhere; so if that
cookie is stolen, it's valid for a very long time.
This switches us to one-time-use tokens that expire as soon as the token
logs the user in once. Each user agent also gets a different
one-time-use token.
Change-Id: I4f20cd7759fd74590e82ed55797552e342243d49
testplan:
* Check that no token is set at all when "stay logged in" isn't
selected.
* Check "stay logged in", and verify:
* That you don't have to login again after restarting your browser,
but your _normandy_session got reset.
* That if you save and try to replay using the same
pseudonym_credentials, they don't work the second time.
* That a second browser will get a different pseudonym_credentials
value, and using one token doesn't affect the other.
* That once the token is used, a new one is generated and set in
your cookies. Verify this new token works as well.
* That logging out removes the pseudonym_credentials cookie in your
browser. And also that manually restoring this cookie still
doesn't log you in, since it was removed server-side as well.
* Change your password, and verify that the existing "stay logged in"
tokens no longer work.
* Delete your pseudonym, and verify the same.
Reviewed-on: https://gerrit.instructure.com/7093
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
Reviewed-by: Zach Wily <zach@instructure.com>
2011-11-22 05:20:48 +08:00
def persist_by_cookie
cookie = controller . cookies [ cookie_key ]
if cookie
token = SessionPersistenceToken . find_by_pseudonym_credentials ( cookie )
self . unauthorized_record = token . use! if token
is_valid = self . valid?
if is_valid
# this token has been used -- destroy it, and generate a new one
# remember_me is implicitly true when they login via the remember_me token
2012-08-01 05:41:13 +08:00
controller . session [ :used_remember_me_token ] = true
make "stay logged in" use a one-time token
closes #6382
Previously, the "stay logged in" cookie just used the authlogic default
implementation, which is the pseudonym persistence_token. This is a
problem, because that persistence_token only ever changes when the
pseudonym password changes, so it's the same everywhere; so if that
cookie is stolen, it's valid for a very long time.
This switches us to one-time-use tokens that expire as soon as the token
logs the user in once. Each user agent also gets a different
one-time-use token.
Change-Id: I4f20cd7759fd74590e82ed55797552e342243d49
testplan:
* Check that no token is set at all when "stay logged in" isn't
selected.
* Check "stay logged in", and verify:
* That you don't have to login again after restarting your browser,
but your _normandy_session got reset.
* That if you save and try to replay using the same
pseudonym_credentials, they don't work the second time.
* That a second browser will get a different pseudonym_credentials
value, and using one token doesn't affect the other.
* That once the token is used, a new one is generated and set in
your cookies. Verify this new token works as well.
* That logging out removes the pseudonym_credentials cookie in your
browser. And also that manually restoring this cookie still
doesn't log you in, since it was removed server-side as well.
* Change your password, and verify that the existing "stay logged in"
tokens no longer work.
* Delete your pseudonym, and verify the same.
Reviewed-on: https://gerrit.instructure.com/7093
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
Reviewed-by: Zach Wily <zach@instructure.com>
2011-11-22 05:20:48 +08:00
self . remember_me = true
self . save!
end
is_valid
else
false
end
end
# added behavior: destroy the server-side SessionPersistenceToken as well as the browser cookie
def destroy_cookie
cookie = controller . cookies . delete cookie_key , :domain = > controller . cookie_domain
return true unless cookie
token = SessionPersistenceToken . find_by_pseudonym_credentials ( cookie )
token . try ( :destroy )
true
end
2011-10-25 06:30:23 +08:00
# Validate the session using password auth (either local or LDAP, but not
# SSO). If too many failed attempts have occured, the validation will fail.
# In this case, `too_many_attempts?` will be true, rather than
# `invalid_password?`.
#
# Note that for IP based max attempt tracking to occur, you'll need to set
# remote_ip on the PseudonymSession before calling save/valid?. Otherwise,
# only total # of failed attempts will be tracked.
def validate_by_password
super
# have to call super first, as that's what loads attempted_record
2012-06-26 06:20:00 +08:00
if too_many_attempts? || attempted_record . try ( :audit_login , remote_ip , ! invalid_password? ) == :too_many_attempts
2011-10-25 06:30:23 +08:00
self . too_many_attempts = true
errors . add ( password_field , I18n . t ( 'errors.max_attempts' , 'Too many failed login attempts. Please try again later or contact your system administrator.' ) )
return
end
end
def too_many_attempts?
too_many_attempts == true
end
2011-02-01 09:57:29 +08:00
end