Example #1
0
def request(form):
    token = security.generate_key(100)
    email = emailer.normalize_address(form.email)

    # Determine the user associated with `username`; if the user is not found,
    # raise an exception
    user_id = d.engine.scalar("""
        SELECT userid FROM login WHERE email = %(email)s
    """, email=email)

    # If `user_id` exists, then the supplied email was valid; if not valid, do nothing, raising
    #   no errors for plausible deniability of email existence
    if user_id:
        # Insert a record into the forgotpassword table for the user,
        # or update an existing one
        now = d.get_time()
        address = d.get_address()

        d.engine.execute("""
            INSERT INTO forgotpassword (userid, token, set_time, address)
            VALUES (%(id)s, %(token)s, %(time)s, %(address)s)
            ON CONFLICT (userid) DO UPDATE SET
                token = %(token)s,
                set_time = %(time)s,
                address = %(address)s
        """, id=user_id, token=token, time=now, address=address)

        # Generate and send an email to the user containing a password reset link
        emailer.append([email], None, "Weasyl Password Recovery", d.render("email/reset_password.html", [token]))
Example #2
0
def exception_catchall(exc, request):
    expected = isinstance(exc, ExpectedWeasylError)
    exc_type = type(exc)
    request.response.status = exc_type.code if expected else 500
    if not expected and 'sentry.log_error' in request.environ:
        request_id = generate_key(8)
        event_id, = request.environ['sentry.log_error'](request.exc_info, request_id=request_id)
    else:
        if not expected:
            log.error(
                'an error occurred, but sentry was not configured to capture it',
                exc_info=request.exc_info)
        request_id = event_id = None
    ret = {
        'error': True,
        'event_id': event_id,
        'request_id': request_id,
    }
    if expected:
        ret.update({
            'message': str(exc),
            'code': exc_type.__name__,
            'description': exc_type.__doc__,
        })
    return ret
Example #3
0
def request(form):
    token = security.generate_key(100)
    email = emailer.normalize_address(form.email)
    username = d.get_sysname(form.username)

    # Determine the user associated with `username`; if the user is not found,
    # raise an exception
    user = d.engine.execute(
        "SELECT userid, email FROM login WHERE login_name = %(username)s",
        username=username).first()

    if not user:
        raise WeasylError("loginRecordMissing")

    # Check the user's email address against the provided e-mail address,
    # raising an exception if there is a mismatch
    if email != emailer.normalize_address(user.email):
        raise WeasylError("emailInvalid")

    # Insert a record into the forgotpassword table for the user,
    # or update an existing one
    now = d.get_time()
    address = d.get_address()

    d.engine.execute("""
        INSERT INTO forgotpassword (userid, token, set_time, address)
        VALUES (%(id)s, %(token)s, %(time)s, %(address)s)
        ON CONFLICT (userid) DO UPDATE SET
            token = %(token)s,
            set_time = %(time)s,
            address = %(address)s
    """, id=user.userid, token=token, time=now, address=address)

    # Generate and send an email to the user containing a password reset link
    emailer.append([email], None, "Weasyl Password Recovery", d.render("email/reset_password.html", [token]))
Example #4
0
def create(form):
    # Normalize form data
    username = d.plaintext(form.username[:_USERNAME])
    sysname = d.get_sysname(username)

    email = emailer.normalize_address(form.email)
    emailcheck = emailer.normalize_address(form.emailcheck)

    password = form.password
    passcheck = form.passcheck

    if form.day and form.month and form.year:
        try:
            birthday = arrow.Arrow(int(form.year), int(form.month), int(form.day))
        except ValueError:
            raise WeasylError("birthdayInvalid")
    else:
        birthday = None

    # Check mismatched form data
    if password != passcheck:
        raise WeasylError("passwordMismatch")
    if email != emailcheck:
        raise WeasylError("emailMismatch")

    # Check invalid form data
    if birthday is None or d.age_in_years(birthday) < 13:
        raise WeasylError("birthdayInvalid")
    if not password_secure(password):
        raise WeasylError("passwordInsecure")
    if not email:
        raise WeasylError("emailInvalid")
    if not sysname or ";" in username:
        raise WeasylError("usernameInvalid")
    if sysname in ["admin", "administrator", "mod", "moderator", "weasyl",
                   "weasyladmin", "weasylmod", "staff", "security"]:
        raise WeasylError("usernameInvalid")
    if email_exists(email):
        raise WeasylError("emailExists")
    if username_exists(sysname):
        raise WeasylError("usernameExists")

    # Create pending account
    token = security.generate_key(40)

    d.engine.execute(d.meta.tables["logincreate"].insert(), {
        "token": token,
        "username": username,
        "login_name": sysname,
        "hashpass": passhash(password),
        "email": email,
        "birthday": birthday,
        "unixtime": arrow.now(),
    })

    # Queue verification email
    emailer.append([email], None, "Weasyl Account Creation", d.render(
        "email/verify_account.html", [token, sysname]))
    d.metric('increment', 'createdusers')
Example #5
0
File: files.py Project: 0x15/weasyl
def get_temporary(userid, feature):
    """
    Return the full pathname to a temporary file.
    Temporary files are named so as to be owned by a user.
    """

    return "{root}{temp}{userid}.{feature}.{random}".format(root=PATH_ROOT, temp=PATH_TEMP, userid=userid,
                                                            feature=feature, random=security.generate_key(20))
Example #6
0
def get_token():
    from weasyl import api

    request = get_current_request()

    if api.is_api_user(request):
        return ''

    # allow error pages with $:{TOKEN()} in the template to be rendered even
    # when the error occurred before the session middleware set a session
    if not hasattr(request, 'weasyl_session'):
        return security.generate_key(20)

    sess = request.weasyl_session
    if sess.csrf_token is None:
        sess.csrf_token = security.generate_key(64)
        sess.save = True
    return sess.csrf_token
Example #7
0
def get_temporary(userid, feature):
    """
    Return the full pathname to a temporary file.
    Temporary files are named so as to be owned by a user.
    """
    return "{temp}{userid}.{feature}.{random}".format(
        temp=m.MACRO_SYS_TEMP_PATH,
        userid=userid,
        feature=feature,
        random=security.generate_key(20))
Example #8
0
def get_token():
    import api

    if api.is_api_user():
        return ''

    sess = web.ctx.weasyl_session
    if sess.csrf_token is None:
        sess.csrf_token = security.generate_key(64)
        sess.save = True
    return sess.csrf_token
Example #9
0
def get_token():
    import api

    if api.is_api_user():
        return ''

    sess = web.ctx.weasyl_session
    if sess.csrf_token is None:
        sess.csrf_token = security.generate_key(64)
        sess.save = True
    return sess.csrf_token
Example #10
0
def append(db, email, terms):
    token = security.generate_key(40)
    email = emailer.normalize_address(email)

    if not email:
        raise error.WeasylError("emailInvalid")

    define.execute(db, "INSERT INTO premiumpurchase VALUES ('%s', '%s', %i)", [token, email, terms])

    emailer.append([email], None, "Weasyl Premium Verification",
                   define.render("email/verify_premium.html", [token, terms]))
Example #11
0
def get_token():
    from weasyl import api

    if api.is_api_user():
        return ''

    sess = get_current_request().weasyl_session
    if sess.csrf_token is None:
        sess.csrf_token = security.generate_key(64)
        sess.save = True
    return sess.csrf_token
Example #12
0
def get_token():
    from weasyl import api

    if api.is_api_user():
        return ''

    sess = get_current_request().weasyl_session
    if sess.csrf_token is None:
        sess.csrf_token = security.generate_key(64)
        sess.save = True
    return sess.csrf_token
Example #13
0
    def __call__(self, environ, start_response):
        environ['sentry.log_error'] = self.log_error
        environ['sentry.log_message'] = self.log_message

        try:
            return self.app(environ, start_response)
        except Exception:
            exc_info = sys.exc_info()
        request_id = generate_key(8)
        event_id, = self.log_error(exc_info, request_id=request_id)
        start_response('500 Internal Server Error',
                       [('Content-Type', 'text/plain')], exc_info)
        return [('%s-%s' % (event_id, request_id)).encode('utf-8')]
Example #14
0
def create_session(user):
    """
    Creates a session for a user and returns the corresponding WZL cookie.
    """
    session = orm.Session()
    session.sessionid = security.generate_key(64)
    session.userid = user

    db = d.connect()
    db.add(session)
    db.flush()

    return 'WZL=' + session.sessionid.encode('utf-8')
Example #15
0
    def __call__(self, environ, start_response):
        environ['sentry.log_error'] = self.log_error
        environ['sentry.log_message'] = self.log_message

        try:
            return self.app(environ, start_response)
        except Exception:
            exc_info = sys.exc_info()
        request_id = generate_key(8)
        event_id, = self.log_error(exc_info, request_id=request_id)
        start_response(
            '500 Internal Server Error',
            [('Content-Type', 'text/plain')],
            exc_info)
        return [('%s-%s' % (event_id, request_id)).encode('utf-8')]
Example #16
0
 def _serialize(self, request, response):
     if self._invalidated:
         response.delete_cookie(self._cookie_name)
         return
     if not self._changed:
         return
     if self._session_obj is None:
         self._session_obj = Session(sessionid=generate_key(64))
         response.set_cookie(self._cookie_name, value=self._session_obj.sessionid, httponly=True)
     additional_data = self._dict.copy()
     for k in self._static_fields:
         setattr(self._session_obj, k, additional_data.pop(k, None))
     self._session_obj.additional_data = additional_data
     request.db.add(self._session_obj)
     request.db.flush()
Example #17
0
def generate_recovery_codes():
    """
    Generate a set of valid recovery codes.

    Character set is defined as uppercase ASCII characters (string.ascii_uppercase), plus
    numerals '123456789'. Numeral zero is excluded due to the confusion potential between
    '0' and 'O'.

    Parameters: None

    Returns: A set of length `_TFA_RECOVERY_CODES` where each code is `LENGTH_RECOVERY_CODE`
    characters in length.
    """
    # Generate the character-set to use during the generation of the keys.
    charset = string.ascii_uppercase + "123456789"
    return [security.generate_key(size=LENGTH_RECOVERY_CODE, key_characters=charset) for i in range(_TFA_RECOVERY_CODES)]
Example #18
0
def request(form):
    token = security.generate_key(100)
    email = emailer.normalize_address(form.email)
    username = d.get_sysname(form.username)

    # Determine the user associated with `username`; if the user is not found,
    # raise an exception
    user = d.engine.execute(
        "SELECT userid, email FROM login WHERE login_name = %(username)s",
        username=username).first()

    if not user:
        raise WeasylError("loginRecordMissing")

    # Check the user's email address against the provided e-mail address,
    # raising an exception if there is a mismatch
    if email != emailer.normalize_address(user.email):
        raise WeasylError("emailInvalid")

    # Insert a record into the forgotpassword table for the user,
    # or update an existing one
    now = d.get_time()
    address = d.get_address()

    try:
        d.engine.execute(
            "INSERT INTO forgotpassword (userid, token, set_time, address)"
            " VALUES (%(id)s, %(token)s, %(time)s, %(address)s)",
            id=user.userid, token=token, time=now, address=address)
    except IntegrityError:
        # An IntegrityError will probably indicate that a password reset request
        # already exists and that the existing row should be updated. If the update
        # doesn't find anything, though, the original error should be re-raised.
        result = d.engine.execute("""
            UPDATE forgotpassword SET
                token = %(token)s,
                set_time = %(time)s,
                address = %(address)s
            WHERE userid = %(id)s
        """, id=user.userid, token=token, time=now, address=address)

        if result.rowcount != 1:
            raise

    # Generate and send an email to the user containing a password reset link
    emailer.append([email], None, "Weasyl Password Recovery", d.render("email/reset_password.html", [token]))
Example #19
0
 def _serialize(self, request, response):
     if self._invalidated:
         response.delete_cookie(self._cookie_name)
         return
     if not self._changed:
         return
     if self._session_obj is None:
         self._session_obj = Session(sessionid=generate_key(64))
         response.set_cookie(self._cookie_name,
                             value=self._session_obj.sessionid,
                             httponly=True)
     additional_data = self._dict.copy()
     for k in self._static_fields:
         setattr(self._session_obj, k, additional_data.pop(k, None))
     self._session_obj.additional_data = additional_data
     request.db.add(self._session_obj)
     request.db.flush()
Example #20
0
def request(form):
    token = security.generate_key(100)
    email = emailer.normalize_address(form.email)
    username = d.get_sysname(form.username)

    # Determine the user associated with `username`; if the user is not found,
    # raise an exception
    user = d.engine.execute(
        "SELECT userid, email FROM login WHERE login_name = %(username)s",
        username=username).first()

    if not user:
        raise WeasylError("loginRecordMissing")

    # Check the user's email address against the provided e-mail address,
    # raising an exception if there is a mismatch
    if email != emailer.normalize_address(user.email):
        raise WeasylError("emailInvalid")

    # Insert a record into the forgotpassword table for the user,
    # or update an existing one
    now = d.get_time()
    address = d.get_address()

    try:
        d.engine.execute(
            "INSERT INTO forgotpassword (userid, token, set_time, address)"
            " VALUES (%(id)s, %(token)s, %(time)s, %(address)s)",
            id=user.userid, token=token, time=now, address=address)
    except IntegrityError:
        # An IntegrityError will probably indicate that a password reset request
        # already exists and that the existing row should be updated. If the update
        # doesn't find anything, though, the original error should be re-raised.
        result = d.engine.execute("""
            UPDATE forgotpassword SET
                token = %(token)s,
                set_time = %(time)s,
                address = %(address)s
            WHERE userid = %(id)s
        """, id=user.userid, token=token, time=now, address=address)

        if result.rowcount != 1:
            raise

    # Generate and send an email to the user containing a password reset link
    emailer.append([email], None, "Weasyl Password Recovery", d.render("email/reset_password.html", [token]))
Example #21
0
def request(email):
    token = security.generate_key(25,
                                  key_characters=string.digits +
                                  string.ascii_lowercase)
    token_sha256 = _hash_token(token)
    email = emailer.normalize_address(email)

    if email is None:
        raise WeasylError("emailInvalid")

    d.engine.execute(
        "INSERT INTO forgotpassword (email, token_sha256)"
        " VALUES (%(email)s, %(token_sha256)s)",
        email=email,
        token_sha256=bytearray(token_sha256))

    # Generate and send an email to the user containing a password reset link
    emailer.send(email, "Weasyl Account Recovery",
                 d.render("email/reset_password.html", [token]))
Example #22
0
    def session_tween(request):
        cookies_to_clear = set()
        if 'beaker.session.id' in request.cookies:
            cookies_to_clear.add('beaker.session.id')

        session = d.connect()
        sess_obj = None
        if 'WZL' in request.cookies:
            sess_obj = session.query(orm.Session).get(request.cookies['WZL'])
            if sess_obj is None:
                # clear an invalid session cookie if nothing ends up trying to create a
                # new one
                cookies_to_clear.add('WZL')

        if sess_obj is None:
            sess_obj = orm.Session()
            sess_obj.create = True
            sess_obj.sessionid = security.generate_key(64)
        # BUG: Because of the way our exception handler relies on a weasyl_session, exceptions
        # thrown before this part will not be handled correctly.
        request.weasyl_session = sess_obj

        # Register a response callback to clear and set the session cookies before returning.
        # Note that this requires that exceptions are handled properly by our exception view.
        def callback(request, response):
            if sess_obj.save:
                session.begin()
                if sess_obj.create:
                    session.add(sess_obj)
                    response.set_cookie('WZL',
                                        sess_obj.sessionid,
                                        max_age=60 * 60 * 24 * 365,
                                        secure=request.scheme == 'https',
                                        httponly=True)
                    # don't try to clear the cookie if we're saving it
                    cookies_to_clear.discard('WZL')
                session.commit()
            for name in cookies_to_clear:
                response.delete_cookie(name)

        request.add_response_callback(callback)
        return handler(request)
Example #23
0
def generate_recovery_codes():
    """
    Generate a set of valid recovery codes.

    Character set is defined as uppercase ASCII characters (string.ascii_uppercase), plus
    numerals '123456789'. Numeral zero is excluded due to the confusion potential between
    '0' and 'O'.

    Parameters: None

    Returns: A set of length `_TFA_RECOVERY_CODES` where each code is `LENGTH_RECOVERY_CODE`
    characters in length.
    """
    # Generate the character-set to use during the generation of the keys.
    charset = string.ascii_uppercase + "123456789"
    return [
        security.generate_key(size=LENGTH_RECOVERY_CODE,
                              key_characters=charset)
        for i in range(_TFA_RECOVERY_CODES)
    ]
Example #24
0
def session_processor(handle):
    cookies = web.cookies()
    cookies_to_clear = set()
    if 'beaker.session.id' in cookies:
        cookies_to_clear.add('beaker.session.id')

    session = d.connect()
    sess_obj = None
    if 'WZL' in cookies:
        sess_obj = session.query(orm.Session).get(cookies['WZL'])
        if sess_obj is None:
            # clear an invalid session cookie if nothing ends up trying to create a
            # new one
            cookies_to_clear.add('WZL')

    if sess_obj is None:
        sess_obj = orm.Session()
        sess_obj.create = True
        sess_obj.sessionid = security.generate_key(64)
    web.ctx.weasyl_session = sess_obj

    try:
        return handle()
    finally:
        if sess_obj.save:
            session.begin()
            if sess_obj.create:
                session.add(sess_obj)
                web.setcookie('WZL',
                              sess_obj.sessionid,
                              expires=60 * 60 * 24 * 365,
                              secure=web.ctx.protocol == 'https',
                              httponly=True)
                # don't try to clear the cookie if we're saving it
                cookies_to_clear.discard('WZL')
            session.commit()

        for name in cookies_to_clear:
            # this sets the cookie to expire one second before the HTTP request was
            # issued, which is the closest you can get to 'clearing' a cookie.
            web.setcookie(name, '', expires=-1)
Example #25
0
def request(form):
    token = security.generate_key(100)
    email = emailer.normalize_address(form.email)
    username = d.get_sysname(form.username)

    # Determine the user associated with `username`; if the user is not found,
    # raise an exception
    user = d.engine.execute(
        "SELECT userid, email FROM login WHERE login_name = %(username)s",
        username=username).first()

    if not user:
        raise WeasylError("loginRecordMissing")

    # Check the user's email address against the provided e-mail address,
    # raising an exception if there is a mismatch
    if email != emailer.normalize_address(user.email):
        raise WeasylError("emailInvalid")

    # Insert a record into the forgotpassword table for the user,
    # or update an existing one
    now = d.get_time()
    address = d.get_address()

    d.engine.execute("""
        INSERT INTO forgotpassword (userid, token, set_time, address)
        VALUES (%(id)s, %(token)s, %(time)s, %(address)s)
        ON CONFLICT (userid) DO UPDATE SET
            token = %(token)s,
            set_time = %(time)s,
            address = %(address)s
    """,
                     id=user.userid,
                     token=token,
                     time=now,
                     address=address)

    # Generate and send an email to the user containing a password reset link
    emailer.append([email], None, "Weasyl Password Recovery",
                   d.render("email/reset_password.html", [token]))
Example #26
0
    def session_tween(request):
        cookies_to_clear = set()
        if 'beaker.session.id' in request.cookies:
            cookies_to_clear.add('beaker.session.id')

        session = d.connect()
        sess_obj = None
        if 'WZL' in request.cookies:
            sess_obj = session.query(orm.Session).get(request.cookies['WZL'])
            if sess_obj is None:
                # clear an invalid session cookie if nothing ends up trying to create a
                # new one
                cookies_to_clear.add('WZL')

        if sess_obj is None:
            sess_obj = orm.Session()
            sess_obj.create = True
            sess_obj.sessionid = security.generate_key(64)
        # BUG: Because of the way our exception handler relies on a weasyl_session, exceptions
        # thrown before this part will not be handled correctly.
        request.weasyl_session = sess_obj

        # Register a response callback to clear and set the session cookies before returning.
        # Note that this requires that exceptions are handled properly by our exception view.
        def callback(request, response):
            if sess_obj.save:
                session.begin()
                if sess_obj.create:
                    session.add(sess_obj)
                    response.set_cookie('WZL', sess_obj.sessionid, max_age=60 * 60 * 24 * 365,
                                        secure=request.scheme == 'https', httponly=True)
                    # don't try to clear the cookie if we're saving it
                    cookies_to_clear.discard('WZL')
                session.commit()
            for name in cookies_to_clear:
                response.delete_cookie(name)

        request.add_response_callback(callback)
        return handler(request)
Example #27
0
def session_processor(handle):
    cookies = web.cookies()
    cookies_to_clear = set()
    if 'beaker.session.id' in cookies:
        cookies_to_clear.add('beaker.session.id')

    session = d.connect()
    sess_obj = None
    if 'WZL' in cookies:
        sess_obj = session.query(orm.Session).get(cookies['WZL'])
        if sess_obj is None:
            # clear an invalid session cookie if nothing ends up trying to create a
            # new one
            cookies_to_clear.add('WZL')

    if sess_obj is None:
        sess_obj = orm.Session()
        sess_obj.create = True
        sess_obj.sessionid = security.generate_key(64)
    web.ctx.weasyl_session = sess_obj

    try:
        return handle()
    finally:
        if sess_obj.save:
            session.begin()
            if sess_obj.create:
                session.add(sess_obj)
                web.setcookie('WZL', sess_obj.sessionid, expires=60 * 60 * 24 * 365,
                              secure=web.ctx.protocol == 'https', httponly=True)
                # don't try to clear the cookie if we're saving it
                cookies_to_clear.discard('WZL')
            session.commit()

        for name in cookies_to_clear:
            # this sets the cookie to expire one second before the HTTP request was
            # issued, which is the closest you can get to 'clearing' a cookie.
            web.setcookie(name, '', expires=-1)
Example #28
0
def create_guest_session():
    token = security.generate_key(GUEST_TOKEN_LENGTH)
    sess = GuestSession(token)
    sess.create = True
    return sess
Example #29
0
 def new_csrf_token(self):
     ret = self['csrf_token'] = generate_key(64)
     return ret
Example #30
0
def add_api_key(userid, description):
    db = Base.dbsession()
    db.add(APIToken(userid=userid, token=security.generate_key(48), description=description))
    db.flush()
Example #31
0
def edit_email_password(userid, username, password, newemail, newemailcheck,
                        newpassword, newpasscheck):
    """
    Edit the email address and/or password for a given Weasyl account.

    After verifying the user's current login credentials, edit the user's email address and/or
    password if validity checks pass. If the email is modified, a confirmation email is sent
    to the user's target email with a token which, if used, finalizes the email address change.

    Parameters:
        userid: The `userid` of the Weasyl account to modify.
        username: User-entered username for password-based authentication.
        password: The user's current plaintext password.
        newemail: If changing the email on the account, the new email address. Optional.
        newemailcheck: A verification field for the above to serve as a typo-check. Optional,
        but mandatory if `newemail` provided.
        newpassword: If changing the password, the user's new password. Optional.
        newpasswordcheck: Verification field for `newpassword`. Optional, but mandatory if
        `newpassword` provided.
    """
    from weasyl import login

    # Track if any changes were made for later display back to the user.
    changes_made = ""

    # Check that credentials are correct
    logid, logerror = login.authenticate_bcrypt(username,
                                                password,
                                                request=None)

    # Run checks prior to modifying anything...
    if userid != logid or logerror is not None:
        raise WeasylError("loginInvalid")

    if newemail:
        if newemail != newemailcheck:
            raise WeasylError("emailMismatch")

    if newpassword:
        if newpassword != newpasscheck:
            raise WeasylError("passwordMismatch")
        elif not login.password_secure(newpassword):
            raise WeasylError("passwordInsecure")

    # If we are setting a new email, then write the email into a holding table pending confirmation
    #   that the email is valid.
    if newemail:
        # Only actually attempt to change the email if unused; prevent finding out if an email is already registered
        if not login.email_exists(newemail):
            token = security.generate_key(40)
            # Store the current token & email, updating them to overwrite a previous attempt if needed
            d.engine.execute("""
                INSERT INTO emailverify (userid, email, token, createtimestamp)
                VALUES (%(userid)s, %(newemail)s, %(token)s, NOW())
                ON CONFLICT (userid) DO
                  UPDATE SET email = %(newemail)s, token = %(token)s, createtimestamp = NOW()
            """,
                             userid=userid,
                             newemail=newemail,
                             token=token)

            # Send out the email containing the verification token.
            emailer.append([newemail], None,
                           "Weasyl Email Change Confirmation",
                           d.render("email/verify_emailchange.html",
                                    [token, d.get_display_name(userid)]))
        else:
            # The target email exists: let the target know this
            query_username = d.engine.scalar("""
                SELECT login_name FROM login WHERE email = %(email)s
            """,
                                             email=newemail)
            emailer.append(
                [newemail], None,
                "Weasyl Account Information - Duplicate Email on Accounts Rejected",
                d.render("email/email_in_use_email_change.html",
                         [query_username]))

        # Then add text to `changes_made` telling that we have completed the email change request, and how to proceed.
        changes_made += "Your email change request is currently pending. An email has been sent to **" + newemail + "**. Follow the instructions within to finalize your email address change.\n"

    # If the password is being updated, update the hash, and clear other sessions.
    if newpassword:
        d.execute("UPDATE authbcrypt SET hashsum = '%s' WHERE userid = %i",
                  [login.passhash(newpassword), userid])

        # Invalidate all sessions for `userid` except for the current one
        invalidate_other_sessions(userid)

        # Then add to `changes_made` detailing that the password change has successfully occurred.
        changes_made += "Your password has been successfully changed. As a security precaution, you have been logged out of all other active sessions."

    if changes_made != "":
        return changes_made
    else:
        return False
Example #32
0
def create_session(userid):
    sess = Session(userid=userid)
    sess.sessionid = security.generate_key(USER_TOKEN_LENGTH)
    sess.create = True
    sess.save = True
    return sess
Example #33
0
def create_guest_session():
    token = security.generate_key(GUEST_TOKEN_LENGTH)
    sess = GuestSession(token)
    sess.create = True
    return sess
Example #34
0
 def new_csrf_token(self):
     ret = self['csrf_token'] = generate_key(64)
     return ret
Example #35
0
def create(form):
    # Normalize form data
    username = d.plaintext(form.username[:_USERNAME])
    sysname = d.get_sysname(username)

    email = emailer.normalize_address(form.email)
    emailcheck = emailer.normalize_address(form.emailcheck)

    password = form.password
    passcheck = form.passcheck

    if form.day and form.month and form.year:
        try:
            birthday = arrow.Arrow(int(form.year), int(form.month),
                                   int(form.day))
        except ValueError:
            raise WeasylError("birthdayInvalid")
    else:
        birthday = None

    # Check mismatched form data
    if password != passcheck:
        raise WeasylError("passwordMismatch")
    if email != emailcheck:
        raise WeasylError("emailMismatch")

    # Check invalid form data
    if birthday is None or d.age_in_years(birthday) < 13:
        raise WeasylError("birthdayInvalid")
    if not password_secure(password):
        raise WeasylError("passwordInsecure")
    if not email:
        raise WeasylError("emailInvalid")
    if not sysname or ";" in username:
        raise WeasylError("usernameInvalid")
    if sysname in [
            "admin", "administrator", "mod", "moderator", "weasyl",
            "weasyladmin", "weasylmod", "staff", "security"
    ]:
        raise WeasylError("usernameInvalid")
    if email_exists(email):
        raise WeasylError("emailExists")
    if username_exists(sysname):
        raise WeasylError("usernameExists")

    # Create pending account
    token = security.generate_key(40)

    d.engine.execute(
        d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": email,
            "birthday": birthday,
            "unixtime": arrow.now(),
        })

    # Queue verification email
    emailer.append([email], None, "Weasyl Account Creation",
                   d.render("email/verify_account.html", [token, sysname]))
    d.metric('increment', 'createdusers')
Example #36
0
def create(form):
    # Normalize form data
    username = d.plaintext(form.username[:_USERNAME])
    sysname = d.get_sysname(username)

    email = emailer.normalize_address(form.email)
    emailcheck = emailer.normalize_address(form.emailcheck)

    password = form.password
    passcheck = form.passcheck

    if form.day and form.month and form.year:
        try:
            birthday = arrow.Arrow(int(form.year), int(form.month), int(form.day))
        except ValueError:
            raise WeasylError("birthdayInvalid")
    else:
        birthday = None

    # Check mismatched form data
    if password != passcheck:
        raise WeasylError("passwordMismatch")
    if email != emailcheck:
        raise WeasylError("emailMismatch")

    # Check invalid form data
    if birthday is None or d.age_in_years(birthday) < 13:
        raise WeasylError("birthdayInvalid")
    if not password_secure(password):
        raise WeasylError("passwordInsecure")
    if not email:
        raise WeasylError("emailInvalid")
    if is_email_blacklisted(email):
        raise WeasylError("emailBlacklisted")
    if not sysname or ";" in username:
        raise WeasylError("usernameInvalid")
    if sysname in ["admin", "administrator", "mod", "moderator", "weasyl",
                   "weasyladmin", "weasylmod", "staff", "security"]:
        raise WeasylError("usernameInvalid")
    if username_exists(sysname):
        raise WeasylError("usernameExists")

    # Account verification token
    token = security.generate_key(40)

    # Only attempt to create the account if the email is unused (as defined by the function)
    if not email_exists(email):
        # Create pending account
        d.engine.execute(d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": email,
            "birthday": birthday,
            "unixtime": arrow.now(),
        })

        # Queue verification email
        emailer.append([email], None, "Weasyl Account Creation", d.render(
            "email/verify_account.html", [token, sysname]))
        d.metric('increment', 'createdusers')
    else:
        # Store a dummy record to support plausible deniability of email addresses
        # So "reserve" the username, but mark the record invalid, and use the token to satisfy the uniqueness
        #  constraint for the email field (e.g., if there is already a valid, pending row in the table).
        d.engine.execute(d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": token,
            "birthday": arrow.now(),
            "unixtime": arrow.now(),
            "invalid": True,
        })
        # The email address in question is already in use in either `login` or `logincreate`;
        #   let the already registered user know this via email (perhaps they forgot their username/password)
        query_username_login = d.engine.scalar("SELECT login_name FROM login WHERE email = %(email)s", email=email)
        query_username_logincreate = d.engine.scalar("SELECT login_name FROM logincreate WHERE email = %(email)s", email=email)
        emailer.append([email], None, "Weasyl Account Creation - Account Already Exists", d.render(
            "email/email_in_use_account_creation.html", [query_username_login or query_username_logincreate]))
Example #37
0
 def test_generate_key(self):
     length = 20
     self.assertEqual(length, len(security.generate_key(length)))
Example #38
0
def create_session(userid):
    sess = Session(userid=userid)
    sess.sessionid = security.generate_key(USER_TOKEN_LENGTH)
    sess.create = True
    sess.save = True
    return sess
Example #39
0
def create(form):
    # Normalize form data
    username = clean_display_name(form.username)
    sysname = d.get_sysname(username)

    email = emailer.normalize_address(form.email)
    emailcheck = emailer.normalize_address(form.emailcheck)

    password = form.password
    passcheck = form.passcheck
    if form.day and form.month and form.year:
        try:
            birthday = arrow.Arrow(int(form.year), int(form.month), int(form.day))
        except ValueError:
            raise WeasylError("birthdayInvalid")
    else:
        birthday = None

    # Check mismatched form data
    if password != passcheck:
        raise WeasylError("passwordMismatch")
    if email != emailcheck:
        raise WeasylError("emailMismatch")

    # Check invalid form data
    if birthday is None or d.age_in_years(birthday) < 13:
        raise WeasylError("birthdayInvalid")
    if not password_secure(password):
        raise WeasylError("passwordInsecure")
    if not email:
        raise WeasylError("emailInvalid")
    if is_email_blacklisted(email):
        raise WeasylError("emailBlacklisted")
    if username_exists(sysname):
        raise WeasylError("usernameExists")

    # Account verification token
    token = security.generate_key(40)

    # Only attempt to create the account if the email is unused (as defined by the function)
    if not email_exists(email):
        # Create pending account
        d.engine.execute(d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": email,
            "birthday": birthday,
        })

        # Send verification email
        emailer.send(email, "Weasyl Account Creation", d.render(
            "email/verify_account.html", [token, sysname]))
        d.metric('increment', 'createdusers')
    else:
        # Store a dummy record to support plausible deniability of email addresses
        # So "reserve" the username, but mark the record invalid, and use the token to satisfy the uniqueness
        #  constraint for the email field (e.g., if there is already a valid, pending row in the table).
        d.engine.execute(d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": token,
            "birthday": arrow.now(),
            "invalid": True,
            # So we have a way for admins to determine which email address collided in the View Pending Accounts Page
            "invalid_email_addr": email,
        })
        # The email address in question is already in use in either `login` or `logincreate`;
        #   let the already registered user know this via email (perhaps they forgot their username/password)
        query_username_login = d.engine.scalar("SELECT login_name FROM login WHERE email = %(email)s", email=email)
        query_username_logincreate = d.engine.scalar("SELECT login_name FROM logincreate WHERE email = %(email)s", email=email)
        emailer.send(email, "Weasyl Account Creation - Account Already Exists", d.render(
            "email/email_in_use_account_creation.html", [query_username_login or query_username_logincreate]))
Example #40
0
def edit_email_password(userid, username, password, newemail, newemailcheck,
                        newpassword, newpasscheck):
    """
    Edit the email address and/or password for a given Weasyl account.

    After verifying the user's current login credentials, edit the user's email address and/or
    password if validity checks pass. If the email is modified, a confirmation email is sent
    to the user's target email with a token which, if used, finalizes the email address change.

    Parameters:
        userid: The `userid` of the Weasyl account to modify.
        username: User-entered username for password-based authentication.
        password: The user's current plaintext password.
        newemail: If changing the email on the account, the new email address. Optional.
        newemailcheck: A verification field for the above to serve as a typo-check. Optional,
        but mandatory if `newemail` provided.
        newpassword: If changing the password, the user's new password. Optional.
        newpasswordcheck: Verification field for `newpassword`. Optional, but mandatory if
        `newpassword` provided.
    """
    from weasyl import login

    # Track if any changes were made for later display back to the user.
    changes_made = ""

    # Check that credentials are correct
    logid, logerror = login.authenticate_bcrypt(username, password, request=None)

    # Run checks prior to modifying anything...
    if userid != logid or logerror is not None:
        raise WeasylError("loginInvalid")

    if newemail:
        if newemail != newemailcheck:
            raise WeasylError("emailMismatch")

    if newpassword:
        if newpassword != newpasscheck:
            raise WeasylError("passwordMismatch")
        elif not login.password_secure(newpassword):
            raise WeasylError("passwordInsecure")

    # If we are setting a new email, then write the email into a holding table pending confirmation
    #   that the email is valid.
    if newemail:
        # Only actually attempt to change the email if unused; prevent finding out if an email is already registered
        if not login.email_exists(newemail):
            token = security.generate_key(40)
            # Store the current token & email, updating them to overwrite a previous attempt if needed
            d.engine.execute("""
                INSERT INTO emailverify (userid, email, token, createtimestamp)
                VALUES (%(userid)s, %(newemail)s, %(token)s, NOW())
                ON CONFLICT (userid) DO
                  UPDATE SET email = %(newemail)s, token = %(token)s, createtimestamp = NOW()
            """, userid=userid, newemail=newemail, token=token)

            # Send out the email containing the verification token.
            emailer.append([newemail], None, "Weasyl Email Change Confirmation", d.render("email/verify_emailchange.html", [token, d.get_display_name(userid)]))
        else:
            # The target email exists: let the target know this
            query_username = d.engine.scalar("""
                SELECT login_name FROM login WHERE email = %(email)s
            """, email=newemail)
            emailer.append([newemail], None, "Weasyl Account Information - Duplicate Email on Accounts Rejected", d.render(
                "email/email_in_use_email_change.html", [query_username])
            )

        # Then add text to `changes_made` telling that we have completed the email change request, and how to proceed.
        changes_made += "Your email change request is currently pending. An email has been sent to **" + newemail + "**. Follow the instructions within to finalize your email address change.\n"

    # If the password is being updated, update the hash, and clear other sessions.
    if newpassword:
        d.execute("UPDATE authbcrypt SET hashsum = '%s' WHERE userid = %i", [login.passhash(newpassword), userid])

        # Invalidate all sessions for `userid` except for the current one
        invalidate_other_sessions(userid)

        # Then add to `changes_made` detailing that the password change has successfully occurred.
        changes_made += "Your password has been successfully changed. As a security precaution, you have been logged out of all other active sessions."

    if changes_made != "":
        return changes_made
    else:
        return False