def POST(self): form = web.input(ch_username="", ch_full_name="", ch_catchphrase="", ch_email="", ch_birthday="", ch_gender="", ch_country="") userid = d.get_int(form.userid) if self.user_id != userid and userid in staff.ADMINS and self.user_id not in staff.TECHNICAL: return d.errorpage(self.user_id, errorcode.permission) if form.get('impersonate'): if self.user_id not in staff.TECHNICAL: return d.errorpage(self.user_id, errorcode.permission) sess = web.ctx.weasyl_session sess.additional_data.setdefault('user-stack', []).append(sess.userid) sess.additional_data.changed() sess.userid = userid sess.save = True d.append_to_log( 'staff.actions', userid=self.user_id, action='impersonate', target=userid) raise web.seeother('/') else: profile.do_manage(self.user_id, userid, username=form.username.strip() if form.ch_username else None, full_name=form.full_name.strip() if form.ch_full_name else None, catchphrase=form.catchphrase.strip() if form.ch_catchphrase else None, birthday=form.birthday if form.ch_birthday else None, gender=form.gender if form.ch_gender else None, country=form.country if form.ch_country else None) raise web.seeother("/admincontrol")
def signin(userid): # 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 = d.get_weasyl_session() sess.userid = userid sess.save = True
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 POST(self): form = web.input(ch_username="", ch_full_name="", ch_catchphrase="", ch_email="", ch_birthday="", ch_gender="", ch_country="") userid = d.get_int(form.userid) if self.user_id != userid and userid in staff.ADMINS and self.user_id not in staff.TECHNICAL: return d.errorpage(self.user_id, errorcode.permission) if form.get('impersonate'): if self.user_id not in staff.TECHNICAL: return d.errorpage(self.user_id, errorcode.permission) sess = web.ctx.weasyl_session sess.additional_data.setdefault('user-stack', []).append(sess.userid) sess.additional_data.changed() sess.userid = userid sess.save = True d.append_to_log('staff.actions', userid=self.user_id, action='impersonate', target=userid) raise web.seeother('/') else: profile.do_manage( self.user_id, userid, username=form.username.strip() if form.ch_username else None, full_name=form.full_name.strip() if form.ch_full_name else None, catchphrase=form.catchphrase.strip() if form.ch_catchphrase else None, birthday=form.birthday if form.ch_birthday else None, gender=form.gender if form.ch_gender else None, country=form.country if form.ch_country else None) raise web.seeother("/admincontrol")
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, session=True): """ Return a result tuple of the form (userid, error); `error` is None if the login was successful. Pass `session` as False to authenticate a user without creating a new session. Possible errors are: - "invalid" - "unexpected" - "address" - "banned" - "suspended" """ # 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 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 = 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 `session` is True, then log the signin # if it succeeded. if session: signin(USERID) d.append_to_log('login.success', userid=USERID, ip=d.get_address()) d.metric('increment', 'logins') 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
def setusermode(userid, form): form.userid = profile.resolve(None, form.userid, form.username) if not form.userid: raise WeasylError('noUser') form.reason = form.reason.strip() if form.mode == "s": if form.datetype == "r": # Relative date magnitude = int(form.duration) if magnitude < 0: raise WeasylError("releaseInvalid") basedate = datetime.datetime.now() if form.durationunit == "y": basedate += datetime.timedelta(days=magnitude * 365) elif form.durationunit == "m": basedate += datetime.timedelta(days=magnitude * 30) elif form.durationunit == "w": basedate += datetime.timedelta(weeks=magnitude) else: # Catchall, days basedate += datetime.timedelta(days=magnitude) form.release = d.convert_unixdate(basedate.day, basedate.month, basedate.year) else: # Absolute date if datetime.date(int(form.year), int(form.month), int(form.day)) < datetime.date.today(): raise WeasylError("releaseInvalid") form.release = d.convert_unixdate(form.day, form.month, form.year) else: form.release = None if userid not in staff.MODS: raise WeasylError("Unexpected") elif form.userid in staff.MODS: raise WeasylError("InsufficientPermissions") if form.mode == "b": query = d.execute( "UPDATE login SET settings = REPLACE(REPLACE(settings, 'b', ''), 's', '') || 'b' WHERE userid = %i" " RETURNING userid", [form.userid]) if query: d.execute("DELETE FROM permaban WHERE userid = %i", [form.userid]) d.execute("DELETE FROM suspension WHERE userid = %i", [form.userid]) d.execute("INSERT INTO permaban VALUES (%i, '%s')", [form.userid, form.reason]) elif form.mode == "s": if not form.release: raise WeasylError("releaseInvalid") query = d.execute( "UPDATE login SET settings = REPLACE(REPLACE(settings, 'b', ''), 's', '') || 's' WHERE userid = %i" " RETURNING userid", [form.userid]) if query: d.execute("DELETE FROM permaban WHERE userid = %i", [form.userid]) d.execute("DELETE FROM suspension WHERE userid = %i", [form.userid]) d.execute("INSERT INTO suspension VALUES (%i, '%s', %i)", [form.userid, form.reason, form.release]) elif form.mode == "x": query = d.execute("UPDATE login SET settings = REPLACE(REPLACE(settings, 's', ''), 'b', '') WHERE userid = %i", [form.userid]) d.execute("DELETE FROM permaban WHERE userid = %i", [form.userid]) d.execute("DELETE FROM suspension WHERE userid = %i", [form.userid]) action = _mode_to_action_map.get(form.mode) if action is not None: isoformat_release = None message = form.reason if form.release is not None: isoformat_release = d.datetime.datetime.fromtimestamp(form.release).isoformat() message = '#### Release date: %s\n\n%s' % (isoformat_release, message) d.append_to_log( 'staff.actions', userid=userid, action=action, target=form.userid, reason=form.reason, release=isoformat_release) d.get_login_settings.invalidate(form.userid) note_about(userid, form.userid, 'User mode changed: action was %r' % (action,), message)
def authenticate_bcrypt(username, password, session=True): """ Return a result tuple of the form (userid, error); `error` is None if the login was successful. Pass `session` as False to authenticate a user without creating a new session. 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 `session` is True, then log the signin # if it succeeded. if session: # If the user's record has ``login.twofa_secret`` set (not nulled), return that password authentication succeeded. if TWOFA: return USERID, "2fa" else: signin(USERID) 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
def setusermode(userid, form): form.userid = profile.resolve(None, form.userid, form.username) if not form.userid: raise WeasylError('noUser') form.reason = form.reason.strip() if form.mode == "s": if form.datetype == "r": # Relative date magnitude = int(form.duration) if magnitude < 0: raise WeasylError("releaseInvalid") basedate = datetime.datetime.now() if form.durationunit == "y": basedate += datetime.timedelta(days=magnitude * 365) elif form.durationunit == "m": basedate += datetime.timedelta(days=magnitude * 30) elif form.durationunit == "w": basedate += datetime.timedelta(weeks=magnitude) else: # Catchall, days basedate += datetime.timedelta(days=magnitude) form.release = d.convert_unixdate(basedate.day, basedate.month, basedate.year) else: # Absolute date if datetime.date(int(form.year), int(form.month), int(form.day)) < datetime.date.today(): raise WeasylError("releaseInvalid") form.release = d.convert_unixdate(form.day, form.month, form.year) else: form.release = None if userid not in staff.MODS: raise WeasylError("Unexpected") elif form.userid in staff.MODS: raise WeasylError("InsufficientPermissions") if form.mode == "b": # Ban user with d.engine.begin() as db: db.execute("DELETE FROM permaban WHERE userid = %(target)s", target=form.userid) db.execute("DELETE FROM suspension WHERE userid = %(target)s", target=form.userid) db.execute("INSERT INTO permaban VALUES (%(target)s, %(reason)s)", target=form.userid, reason=form.reason) elif form.mode == "s": # Suspend user if not form.release: raise WeasylError("releaseInvalid") with d.engine.begin() as db: db.execute("DELETE FROM permaban WHERE userid = %(target)s", target=form.userid) db.execute("DELETE FROM suspension WHERE userid = %(target)s", target=form.userid) db.execute("INSERT INTO suspension VALUES (%(target)s, %(reason)s, %(release)s)", target=form.userid, reason=form.reason, release=form.release) elif form.mode == "x": # Unban/Unsuspend with d.engine.begin() as db: db.execute("DELETE FROM permaban WHERE userid = %(target)s", target=form.userid) db.execute("DELETE FROM suspension WHERE userid = %(target)s", target=form.userid) action = _mode_to_action_map.get(form.mode) if action is not None: isoformat_release = None message = form.reason if form.release is not None: isoformat_release = d.datetime.datetime.fromtimestamp(form.release).isoformat() message = '#### Release date: %s\n\n%s' % (isoformat_release, message) d.append_to_log( 'staff.actions', userid=userid, action=action, target=form.userid, reason=form.reason, release=isoformat_release) d._get_all_config.invalidate(form.userid) note_about(userid, form.userid, 'User mode changed: action was %r' % (action,), message)
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
def setusermode(userid, form): form.userid = profile.resolve(None, form.userid, form.username) if not form.userid: raise WeasylError('noUser') form.reason = form.reason.strip() if form.mode == "s": if form.datetype == "r": # Relative date magnitude = int(form.duration) if magnitude < 0: raise WeasylError("releaseInvalid") basedate = datetime.datetime.now() if form.durationunit == "y": basedate += datetime.timedelta(days=magnitude * 365) elif form.durationunit == "m": basedate += datetime.timedelta(days=magnitude * 30) elif form.durationunit == "w": basedate += datetime.timedelta(weeks=magnitude) else: # Catchall, days basedate += datetime.timedelta(days=magnitude) form.release = d.convert_unixdate(basedate.day, basedate.month, basedate.year) else: # Absolute date if datetime.date(int(form.year), int(form.month), int( form.day)) < datetime.date.today(): raise WeasylError("releaseInvalid") form.release = d.convert_unixdate(form.day, form.month, form.year) else: form.release = None if userid not in staff.MODS: raise WeasylError("Unexpected") elif form.userid in staff.MODS: raise WeasylError("InsufficientPermissions") if form.mode == "b": query = d.execute( "UPDATE login SET settings = REPLACE(REPLACE(settings, 'b', ''), 's', '') || 'b' WHERE userid = %i" " RETURNING userid", [form.userid]) if query: d.execute("DELETE FROM permaban WHERE userid = %i", [form.userid]) d.execute("DELETE FROM suspension WHERE userid = %i", [form.userid]) d.execute("INSERT INTO permaban VALUES (%i, '%s')", [form.userid, form.reason]) elif form.mode == "s": if not form.release: raise WeasylError("releaseInvalid") query = d.execute( "UPDATE login SET settings = REPLACE(REPLACE(settings, 'b', ''), 's', '') || 's' WHERE userid = %i" " RETURNING userid", [form.userid]) if query: d.execute("DELETE FROM permaban WHERE userid = %i", [form.userid]) d.execute("DELETE FROM suspension WHERE userid = %i", [form.userid]) d.execute("INSERT INTO suspension VALUES (%i, '%s', %i)", [form.userid, form.reason, form.release]) elif form.mode == "x": query = d.execute( "UPDATE login SET settings = REPLACE(REPLACE(settings, 's', ''), 'b', '') WHERE userid = %i", [form.userid]) d.execute("DELETE FROM permaban WHERE userid = %i", [form.userid]) d.execute("DELETE FROM suspension WHERE userid = %i", [form.userid]) action = _mode_to_action_map.get(form.mode) if action is not None: isoformat_release = None message = form.reason if form.release is not None: isoformat_release = d.datetime.datetime.fromtimestamp( form.release).isoformat() message = '#### Release date: %s\n\n%s' % (isoformat_release, message) d.append_to_log('staff.actions', userid=userid, action=action, target=form.userid, reason=form.reason, release=isoformat_release) d.get_login_settings.invalidate(form.userid) note_about(userid, form.userid, 'User mode changed: action was %r' % (action, ), message)