Пример #1
0
def _targetid(link):
    """
    Returns the content service's identifier based on the URL provided.
    """
    service = _service(link)

    if service in _OEMBED_MAP:
        # Validate with oEmbed
        link = link.strip()
        alpha = string.printable
    elif service == "bandcamp":
        content = d.http_get(link).text
        match = _BANDCAMP_EMBED.search(content)

        if match:
            return match.groups()
        else:
            return None
    else:
        return None

    for i in range(len(link)):
        if link[i] not in alpha:
            return link[:i]

    return d.plaintext(link)
Пример #2
0
def _targetid(link):
    """
    Returns the content service's identifier based on the URL provided.
    """
    service = _service(link)

    if service in _OEMBED_MAP:
        # Validate with oEmbed
        link = link.strip()
        alpha = string.printable
    elif service == "bandcamp":
        content = d.http_get(link).text
        match = _BANDCAMP_EMBED.search(content)

        if match:
            return match.groups()
        else:
            return None
    else:
        return None

    for i in range(len(link)):
        if link[i] not in alpha:
            return link[:i]

    return d.plaintext(link)
Пример #3
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')
Пример #4
0
def update_unicode_password(userid, password, password_confirm):
    if password != password_confirm:
        raise WeasylError('passwordMismatch')
    if not password_secure(password):
        raise WeasylError('passwordInsecure')

    hashpw = d.engine.scalar("""
        SELECT hashsum FROM authbcrypt WHERE userid = %(userid)s
    """, userid=userid).encode('utf-8')

    if bcrypt.checkpw(password.encode('utf-8'), hashpw):
        return

    if not bcrypt.checkpw(d.plaintext(password).encode('utf-8'), hashpw):
        raise WeasylError('passwordIncorrect')

    d.engine.execute("""
        UPDATE authbcrypt SET hashsum = %(hashsum)s WHERE userid = %(userid)s
    """, userid=userid, hashsum=passhash(password))
Пример #5
0
def update_unicode_password(userid, password, password_confirm):
    if password != password_confirm:
        raise WeasylError('passwordMismatch')
    if not password_secure(password):
        raise WeasylError('passwordInsecure')

    hashpw = d.engine.scalar("""
        SELECT hashsum FROM authbcrypt WHERE userid = %(userid)s
    """, userid=userid).encode('utf-8')

    if bcrypt.checkpw(password.encode('utf-8'), hashpw):
        return

    if not bcrypt.checkpw(d.plaintext(password).encode('utf-8'), hashpw):
        raise WeasylError('passwordIncorrect')

    d.engine.execute("""
        UPDATE authbcrypt SET hashsum = %(hashsum)s WHERE userid = %(userid)s
    """, userid=userid, hashsum=passhash(password))
Пример #6
0
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
Пример #7
0
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
Пример #8
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]))
Пример #9
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]))
Пример #10
0
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
Пример #11
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')