From 3a473cad2a00f9fa7343f9075a42347fe8f855e3 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 10 Oct 2016 13:00:59 -0400 Subject: [PATCH] Enable permanent sessions Fixes #1955 --- config.py | 3 +++ endpoints/api/user.py | 3 +-- endpoints/common.py | 9 +++++++-- endpoints/web.py | 1 - util/timedeltastring.py | 41 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 util/timedeltastring.py diff --git a/config.py b/config.py index 027f36254..a657068cc 100644 --- a/config.py +++ b/config.py @@ -141,6 +141,9 @@ class DefaultConfig(object): # Super user config. Note: This MUST BE an empty list for the default config. SUPER_USERS = [] + # Feature Flag: Whether sessions are permanent. + FEATURE_PERMANENT_SESSIONS = True + # Feature Flag: Whether super users are supported. FEATURE_SUPER_USERS = True diff --git a/endpoints/api/user.py b/endpoints/api/user.py index ba7ba228c..4f09704eb 100644 --- a/endpoints/api/user.py +++ b/endpoints/api/user.py @@ -562,7 +562,7 @@ class Signin(ApiResource): 'invite_code': { 'type': 'string', 'description': 'The optional invite code' - } + }, }, }, } @@ -579,7 +579,6 @@ class Signin(ApiResource): username = signin_data['username'] password = signin_data['password'] invite_code = signin_data.get('invite_code', '') - return conduct_signin(username, password, invite_code=invite_code) diff --git a/endpoints/common.py b/endpoints/common.py index d00c4bc64..aaa164d36 100644 --- a/endpoints/common.py +++ b/endpoints/common.py @@ -23,6 +23,7 @@ from config import frontend_visible_config from external_libraries import get_external_javascript, get_external_css from util.names import parse_namespace_repository from util.secscan import PRIORITY_LEVELS +from util.timedeltastring import convert_to_timedelta logger = logging.getLogger(__name__) @@ -108,16 +109,20 @@ def param_required(param_name, allow_body=False): return wrapper -def common_login(db_user): +def common_login(db_user, permanent_session=True): if login_user(LoginWrappedDBUser(db_user.uuid, db_user)): logger.debug('Successfully signed in as: %s (%s)' % (db_user.username, db_user.uuid)) new_identity = QuayDeferredPermissionUser.for_user(db_user) identity_changed.send(app, identity=new_identity) session['login_time'] = datetime.datetime.now() + if permanent_session and features.PERMANENT_SESSIONS: + session_timeout_str = app.config.get('SESSION_TIMEOUT', '31d') + session.permanent = True + session.permanent_session_lifetime = convert_to_timedelta(session_timeout_str) + # Inform our user analytics that we have a new "lead" user_analytics.create_lead(db_user.email, db_user.username) - return True else: logger.debug('User could not be logged in, inactive?.') diff --git a/endpoints/web.py b/endpoints/web.py index b421f9f86..fa04a01db 100644 --- a/endpoints/web.py +++ b/endpoints/web.py @@ -393,7 +393,6 @@ def confirm_email(): user_analytics.change_email(old_email, new_email) common_login(user) - return redirect(url_for('web.user', tab='email') if new_email else url_for('web.index')) diff --git a/util/timedeltastring.py b/util/timedeltastring.py new file mode 100644 index 000000000..99e459356 --- /dev/null +++ b/util/timedeltastring.py @@ -0,0 +1,41 @@ +from datetime import timedelta + +def convert_to_timedelta(time_val): + """ + From: code.activestate.com/recipes/577894-convert-strings-like-5d-and-60s-to-timedelta-objec + + Given a *time_val* (string) such as '5d', returns a timedelta object + representing the given value (e.g. timedelta(days=5)). Accepts the + following '' formats: + + ========= ======= =================== + Character Meaning Example + ========= ======= =================== + s Seconds '60s' -> 60 Seconds + m Minutes '5m' -> 5 Minutes + h Hours '24h' -> 24 Hours + d Days '7d' -> 7 Days + ========= ======= =================== + + Examples:: + + >>> convert_to_timedelta('7d') + datetime.timedelta(7) + >>> convert_to_timedelta('24h') + datetime.timedelta(1) + >>> convert_to_timedelta('60m') + datetime.timedelta(0, 3600) + >>> convert_to_timedelta('120s') + datetime.timedelta(0, 120) + """ + num = int(time_val[:-1]) + if time_val.endswith('s'): + return timedelta(seconds=num) + elif time_val.endswith('m'): + return timedelta(minutes=num) + elif time_val.endswith('h'): + return timedelta(hours=num) + elif time_val.endswith('d'): + return timedelta(days=num) + else: + raise ValueError('Unknown suffix on timedelta: %s' % time_val)