def create_session(user): """ Creates a session for a user and returns the corresponding WZL cookie. """ session = sessions.create_session(user) db = d.connect() db.add(session) db.flush() return 'WZL=' + session.sessionid
def create_session(user): """ Creates a session for a user and returns the corresponding WZL cookie. """ session = sessions.create_session(user) db = d.connect() db.add(session) db.flush() return 'WZL=' + session.sessionid.encode('utf-8')
def test_verify_login_record_is_updated(): # Use a fake session for this test. user_id = db_utils.create_user() sess = get_current_request().weasyl_session = create_session(user_id) db = d.connect() db.add(sess) db.flush() d.engine.execute("UPDATE login SET last_login = -1 WHERE userid = %(id)s", id=user_id) login.signin(get_current_request(), user_id) last_login = d.engine.scalar("SELECT last_login FROM login WHERE userid = %(id)s", id=user_id) assert last_login > -1
def test_verify_login_record_is_updated(): # Use a fake session for this test. user_id = db_utils.create_user() sess = get_current_request().weasyl_session = create_session(user_id) db = d.connect() db.add(sess) db.flush() time = datetime(2020, 1, 1, 00, 00, 1, tzinfo=pytz.UTC) # Arbitrary date that should be earlier than now. d.engine.execute("UPDATE login SET last_login = %(timestamp)s WHERE userid = %(id)s", id=user_id, timestamp=time) login.signin(get_current_request(), user_id) last_login = d.engine.scalar("SELECT last_login FROM login WHERE userid = %(id)s", id=user_id) assert last_login > time
def signin(request, userid, ip_address=None, user_agent=None): # Update the last login record for the user d.execute("UPDATE login SET last_login = NOW() WHERE userid = %i", [userid]) # Log the successful login and increment the login count d.append_to_log('login.success', userid=userid, ip=d.get_address()) d.metric('increment', 'logins') # set the userid on the session sess = create_session(userid) sess.ip_address = ip_address sess.user_agent_id = get_user_agent_id(user_agent) sess.create = True if not isinstance(request.weasyl_session, GuestSession): request.pg_connection.delete(request.weasyl_session) request.pg_connection.flush() request.weasyl_session = sess
def signin(request, userid, ip_address=None, user_agent=None): # Update the last login record for the user d.execute("UPDATE login SET last_login = %i WHERE userid = %i", [d.get_time(), userid]) # Log the successful login and increment the login count d.append_to_log('login.success', userid=userid, ip=d.get_address()) d.metric('increment', 'logins') # set the userid on the session sess = create_session(userid) sess.ip_address = ip_address sess.user_agent_id = get_user_agent_id(user_agent) sess.create = True if not isinstance(request.weasyl_session, GuestSession): request.pg_connection.delete(request.weasyl_session) request.pg_connection.flush() request.weasyl_session = sess
def authenticate_bcrypt(username, password, request, ip_address=None, user_agent=None): """ Return a result tuple of the form (userid, error); `error` is None if the login was successful. Pass None as the `request` to authenticate a user without creating a new session. :param username: The username of the user attempting authentication. :param password: The user's claimed password to check against the stored hash. :param request: The request, or None :param ip_address: The address requesting authentication. :param user_agent: The user agent string of the submitting client. Possible errors are: - "invalid" - "unexpected" - "banned" - "suspended" - "2fa" - Indicates the user has opted-in to 2FA. Additional authentication required. """ # Check that the user entered potentially valid values for `username` and # `password` before attempting to authenticate them if not username or not password: return 0, "invalid" # Select the authentication data necessary to check that the the user-entered # credentials are valid query = d.engine.execute( "SELECT ab.userid, ab.hashsum, lo.twofa_secret FROM authbcrypt ab" " RIGHT JOIN login lo USING (userid)" " WHERE lo.login_name = %(name)s", name=d.get_sysname(username), ).first() if not query: return 0, "invalid" USERID, HASHSUM, TWOFA = query HASHSUM = HASHSUM.encode('utf-8') _, IS_BANNED, IS_SUSPENDED = d.get_login_settings(USERID) d.metric('increment', 'attemptedlogins') if not bcrypt.checkpw(password.encode('utf-8'), HASHSUM): # Log the failed login attempt in a security log if the account the user # attempted to log into is a privileged account if USERID in staff.MODS: d.append_to_log('login.fail', userid=USERID, ip=d.get_address()) d.metric('increment', 'failedlogins') # Return a zero userid and an error code (indicating the entered password # was incorrect) return 0, "invalid" elif IS_BANNED: # Return the proper userid and an error code (indicating the user's account # has been banned) return USERID, "banned" elif IS_SUSPENDED: from weasyl import moderation suspension = moderation.get_suspension(USERID) if d.get_time() > suspension.release: d.execute("DELETE FROM suspension WHERE userid = %i", [USERID]) d._get_all_config.invalidate(USERID) else: # Return the proper userid and an error code (indicating the user's # account has been temporarily suspended) return USERID, "suspended" # Attempt to create a new session if this is a request to log in, then log the signin # if it succeeded. if request is not None: # If the user's record has ``login.twofa_secret`` set (not nulled), return that password authentication succeeded. if TWOFA: if not isinstance(request.weasyl_session, GuestSession): request.pg_connection.delete(request.weasyl_session) request.pg_connection.flush() request.weasyl_session = create_session(None) request.weasyl_session.additional_data = {} return USERID, "2fa" else: signin(request, USERID, ip_address=ip_address, user_agent=user_agent) # Either way, authentication succeeded, so return the userid and a status. return USERID, None
def authenticate_bcrypt(username, password, request, ip_address=None, user_agent=None): """ Return a result tuple of the form (userid, error); `error` is None if the login was successful. Pass None as the `request` to authenticate a user without creating a new session. :param username: The username of the user attempting authentication. :param password: The user's claimed password to check against the stored hash. :param ip_address: The address requesting authentication. :param user_agent: The user agent string of the submitting client. Possible errors are: - "invalid" - "unexpected" - "address" - "banned" - "suspended" - "2fa" - Indicates the user has opted-in to 2FA. Additional authentication required. """ # Check that the user entered potentially valid values for `username` and # `password` before attempting to authenticate them if not username or not password: return 0, "invalid" # Select the authentication data necessary to check that the the user-entered # credentials are valid query = d.execute("SELECT ab.userid, ab.hashsum, lo.settings, lo.twofa_secret FROM authbcrypt ab" " RIGHT JOIN login lo USING (userid)" " WHERE lo.login_name = '%s'", [d.get_sysname(username)], ["single"]) if not query: return 0, "invalid" USERID, HASHSUM, SETTINGS, TWOFA = query HASHSUM = HASHSUM.encode('utf-8') d.metric('increment', 'attemptedlogins') unicode_success = bcrypt.checkpw(password.encode('utf-8'), HASHSUM) if not unicode_success and not bcrypt.checkpw(d.plaintext(password).encode('utf-8'), HASHSUM): # Log the failed login attempt in a security log if the account the user # attempted to log into is a privileged account if USERID in staff.MODS: d.append_to_log('login.fail', userid=USERID, ip=d.get_address()) d.metric('increment', 'failedlogins') # Return a zero userid and an error code (indicating the entered password # was incorrect) return 0, "invalid" elif "b" in SETTINGS: # Return the proper userid and an error code (indicating the user's account # has been banned) return USERID, "banned" elif "s" in SETTINGS: suspension = moderation.get_suspension(USERID) if d.get_time() > suspension.release: d.execute("UPDATE login SET settings = REPLACE(settings, 's', '') WHERE userid = %i", [USERID]) d.execute("DELETE FROM suspension WHERE userid = %i", [USERID]) d.get_login_settings.invalidate(USERID) else: # Return the proper userid and an error code (indicating the user's # account has been temporarily suspended) return USERID, "suspended" # Attempt to create a new session if this is a request to log in, then log the signin # if it succeeded. if request is not None: # If the user's record has ``login.twofa_secret`` set (not nulled), return that password authentication succeeded. if TWOFA: if not isinstance(request.weasyl_session, GuestSession): request.pg_connection.delete(request.weasyl_session) request.pg_connection.flush() request.weasyl_session = create_session(None) request.weasyl_session.additional_data = {} return USERID, "2fa" else: signin(request, USERID, ip_address=ip_address, user_agent=user_agent) status = None if not unicode_success: # Oops; the user's password was stored badly, but they did successfully authenticate. status = 'unicode-failure' # Either way, authentication succeeded, so return the userid and a status. return USERID, status