def upgrade(tables, tester, progress_reporter):
    op = ProgressWrapper(original_op, progress_reporter)
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index("oauthaccesstoken_refresh_token", table_name="oauthaccesstoken")
    op.drop_column(u"oauthaccesstoken", "refresh_token")

    op.drop_column("accesstoken", "code")

    op.drop_column("appspecificauthtoken", "token_code")

    op.drop_column("oauthaccesstoken", "access_token")
    op.drop_column("oauthapplication", "client_secret")

    op.drop_column("oauthauthorizationcode", "code")

    op.drop_column("repositorybuildtrigger", "private_key")
    op.drop_column("repositorybuildtrigger", "auth_token")
    # ### end Alembic commands ###

    # Overwrite all plaintext robot credentials.
    from app import app

    if app.config.get("SETUP_COMPLETE", False) or tester.is_testing():
        while True:
            try:
                robot_account_token = RobotAccountToken.get(fully_migrated=False)
                logger.debug("Found robot account token %s migrate", robot_account_token.id)

                robot_account = robot_account_token.robot_account
                assert robot_account.robot

                result = (
                    User.update(email=str(uuid.uuid4()))
                    .where(
                        User.id == robot_account.id,
                        User.robot == True,
                        User.uuid == robot_account.uuid,
                    )
                    .execute()
                )
                assert result == 1

                try:
                    federated_login = FederatedLogin.get(user=robot_account)
                    assert federated_login.service.name == "quayrobot"

                    federated_login.service_ident = "robot:%s" % robot_account.id
                    federated_login.save()
                except FederatedLogin.DoesNotExist:
                    pass

                robot_account_token.fully_migrated = True
                robot_account_token.save()

                logger.debug("Finished migrating robot account token %s", robot_account_token.id)
            except RobotAccountToken.DoesNotExist:
                break
Example #2
0
def verify_user(username_or_email, password):
    """
    Verifies that the given username/email + password pair is valid.

    If the username or e-mail address is invalid, returns None. If the password specified does not
    match for the given user, either returns None or raises TooManyLoginAttemptsException if there
    have been too many invalid login attempts. Returns the user object if the login was valid.
    """

    # Make sure we didn't get any unicode for the username.
    try:
        username_or_email.encode("ascii")
    except UnicodeEncodeError:
        return None

    # Fetch the user with the matching username or e-mail address.
    try:
        fetched = User.get((User.username == username_or_email)
                           | (User.email == username_or_email))
    except User.DoesNotExist:
        return None

    # If the user has any invalid login attempts, check to see if we are within the exponential
    # backoff window for the user. If so, we raise an exception indicating that the user cannot
    # login.
    now = datetime.utcnow()
    if fetched.invalid_login_attempts > 0:
        can_retry_at = exponential_backoff(fetched.invalid_login_attempts,
                                           EXPONENTIAL_BACKOFF_SCALE,
                                           fetched.last_invalid_login)

        if can_retry_at > now:
            retry_after = can_retry_at - now
            raise TooManyLoginAttemptsException("Too many login attempts.",
                                                retry_after.total_seconds())

    # Hash the given password and compare it to the specified password.
    if (fetched.password_hash
            and hash_password(password, fetched.password_hash).decode("ascii")
            == fetched.password_hash):
        # If the user previously had any invalid login attempts, clear them out now.
        if fetched.invalid_login_attempts > 0:
            try:
                (User.update(invalid_login_attempts=0).where(
                    User.id == fetched.id).execute())

                # Mark that the user was accessed.
                _basequery.update_last_accessed(fetched)
            except ReadOnlyModeException:
                pass

        # Return the valid user.
        return fetched

    # Otherwise, update the user's invalid login attempts.
    try:
        (User.update(
            invalid_login_attempts=User.invalid_login_attempts + 1,
            last_invalid_login=now).where(User.id == fetched.id).execute())
    except ReadOnlyModeException:
        pass

    # We weren't able to authorize the user
    return None
Example #3
0
def test_readreplica(init_db_path, tmpdir_factory):
    primary_file = str(tmpdir_factory.mktemp("data").join("primary.db"))
    replica_file = str(tmpdir_factory.mktemp("data").join("replica.db"))

    # Copy the initialized database to two different locations.
    shutil.copy2(init_db_path, primary_file)
    shutil.copy2(init_db_path, replica_file)

    db_config = {
        "DB_URI": "sqlite:///{0}".format(primary_file),
        "DB_READ_REPLICAS": [{"DB_URI": "sqlite:///{0}".format(replica_file)},],
        "DB_CONNECTION_ARGS": {"threadlocals": True, "autorollback": True,},
        "DB_TRANSACTION_FACTORY": lambda x: FakeTransaction(),
        "FOR_TESTING": True,
        "DATABASE_SECRET_KEY": "anothercrazykey!",
    }

    # Initialize the DB with the primary and the replica.
    configure(db_config)
    assert not read_only_config.obj.is_readonly
    assert read_only_config.obj.read_replicas

    # Ensure we can read the data.
    devtable_user = User.get(username="******")
    assert devtable_user.username == "devtable"

    # Configure with a bad primary. Reading should still work since we're hitting the replica.
    db_config["DB_URI"] = "sqlite:///does/not/exist"
    configure(db_config)

    assert not read_only_config.obj.is_readonly
    assert read_only_config.obj.read_replicas

    devtable_user = User.get(username="******")
    assert devtable_user.username == "devtable"

    # Force us to hit the master and ensure it doesn't work.
    with db_disallow_replica_use():
        with pytest.raises(OperationalError):
            User.get(username="******")

    # Test read replica again.
    devtable_user = User.get(username="******")
    assert devtable_user.username == "devtable"

    # Try to change some data. This should fail because the primary is broken.
    with pytest.raises(OperationalError):
        devtable_user.email = "newlychanged"
        devtable_user.save()

    # Fix the primary and try again.
    db_config["DB_URI"] = "sqlite:///{0}".format(primary_file)
    configure(db_config)

    assert not read_only_config.obj.is_readonly
    assert read_only_config.obj.read_replicas

    devtable_user.email = "newlychanged"
    devtable_user.save()

    # Mark the system as readonly.
    db_config["DB_URI"] = "sqlite:///{0}".format(primary_file)
    db_config["REGISTRY_STATE"] = "readonly"
    configure(db_config)

    assert read_only_config.obj.is_readonly
    assert read_only_config.obj.read_replicas

    # Ensure all write operations raise a readonly mode exception.
    with pytest.raises(ReadOnlyModeException):
        devtable_user.email = "newlychanged2"
        devtable_user.save()

    with pytest.raises(ReadOnlyModeException):
        User.create(username="******")

    with pytest.raises(ReadOnlyModeException):
        User.delete().where(User.username == "foo").execute()

    with pytest.raises(ReadOnlyModeException):
        User.update(username="******").where(User.username == "foo").execute()

    # Reset the config on the DB, so we don't mess up other tests.
    configure(
        {
            "DB_URI": "sqlite:///{0}".format(primary_file),
            "DB_CONNECTION_ARGS": {"threadlocals": True, "autorollback": True,},
            "DB_TRANSACTION_FACTORY": lambda x: FakeTransaction(),
            "DATABASE_SECRET_KEY": "anothercrazykey!",
        }
    )