Example #1
0
class AppSpecificAuthToken(BaseModel):
    token_name = CharField(index=True,
                           unique=True,
                           default=random_string_generator(60))
    token_secret = EncryptedCharField(default_token_length=60)
    token_code = CharField(default=random_string_generator(length=120),
                           unique=True,
                           index=True)
class AccessToken(BaseModel):
    code = CharField(default=random_string_generator(length=64),
                     unique=True,
                     index=True)
    token_name = CharField(default=random_string_generator(length=32),
                           unique=True,
                           index=True)
    token_code = EncryptedCharField(default_token_length=32)
Example #3
0
def regenerate_robot_token(robot_shortname, parent):
    robot_username = format_robot_username(parent.username, robot_shortname)

    robot, metadata = lookup_robot_and_metadata(robot_username)
    password = random_string_generator(length=64)()
    robot.email = str(uuid4())
    robot.uuid = str(uuid4())

    service = LoginService.get(name="quayrobot")
    login = FederatedLogin.get(FederatedLogin.user == robot,
                               FederatedLogin.service == service)
    login.service_ident = "robot:%s" % (robot.id)

    try:
        token_data = RobotAccountToken.get(robot_account=robot)
    except RobotAccountToken.DoesNotExist:
        token_data = RobotAccountToken.create(robot_account=robot)

    token_data.token = password

    with db_transaction():
        token_data.save()
        login.save()
        robot.save()

    return robot, password, metadata
Example #4
0
def create_robot(robot_shortname, parent, description="", unstructured_metadata=None):
    (username_valid, username_issue) = validate_username(robot_shortname)
    if not username_valid:
        raise InvalidRobotException(
            "The name for the robot '%s' is invalid: %s" % (robot_shortname, username_issue)
        )

    username = format_robot_username(parent.username, robot_shortname)

    try:
        User.get(User.username == username)

        msg = "Existing robot with name: %s" % username
        logger.info(msg)
        raise InvalidRobotException(msg)
    except User.DoesNotExist:
        pass

    service = LoginService.get(name="quayrobot")
    try:
        with db_transaction():
            created = User.create(username=username, email=str(uuid.uuid4()), robot=True)
            token = random_string_generator(length=64)()
            RobotAccountToken.create(robot_account=created, token=token, fully_migrated=True)
            FederatedLogin.create(
                user=created, service=service, service_ident="robot:%s" % created.id
            )
            RobotAccountMetadata.create(
                robot_account=created,
                description=description[0:255],
                unstructured_json=unstructured_metadata or {},
            )
            return created, token
    except Exception as ex:
        raise DataModelException(ex.message)
Example #5
0
def create_access_token_for_testing(user_obj,
                                    client_id,
                                    scope,
                                    access_token=None,
                                    expires_in=9000):
    access_token = access_token or random_string_generator(length=40)()
    token_name = access_token[:ACCESS_TOKEN_PREFIX_LENGTH]
    token_code = access_token[ACCESS_TOKEN_PREFIX_LENGTH:]

    assert len(token_name) == ACCESS_TOKEN_PREFIX_LENGTH
    assert len(token_code) >= ACCESS_TOKEN_MINIMUM_CODE_LENGTH

    expires_at = datetime.utcnow() + timedelta(seconds=expires_in)
    application = get_application_for_client_id(client_id)
    created = OAuthAccessToken.create(
        application=application,
        authorized_user=user_obj,
        scope=scope,
        token_type="token",
        access_token="",
        token_code=Credential.from_string(token_code),
        token_name=token_name,
        expires_at=expires_at,
        data="",
    )
    return created, access_token
Example #6
0
def create_token(user, title, expiration=_default_expiration_duration_opt):
    """ Creates and returns an app specific token for the given user. If no expiration is specified
      (including `None`), then the default from config is used. """
    if expiration == _default_expiration_duration_opt:
        duration = _default_expiration_duration()
        expiration = duration + datetime.now() if duration else None

    token_code = random_string_generator(TOKEN_NAME_PREFIX_LENGTH +
                                         MINIMUM_TOKEN_SUFFIX_LENGTH)()
    token_name = token_code[:TOKEN_NAME_PREFIX_LENGTH]
    token_secret = token_code[TOKEN_NAME_PREFIX_LENGTH:]

    assert token_name
    assert token_secret

    # TODO(remove-unenc): Remove legacy handling.
    old_token_code = (token_code if ActiveDataMigration.has_flag(
        ERTMigrationFlags.WRITE_OLD_FIELDS) else None)
    return AppSpecificAuthToken.create(
        user=user,
        title=title,
        expiration=expiration,
        token_name=token_name,
        token_secret=DecryptedValue(token_secret),
        token_code=old_token_code,
    )
Example #7
0
def create_token(user, title, expiration=_default_expiration_duration_opt):
    """
    Creates and returns an app specific token for the given user.

    If no expiration is specified (including `None`), then the default from config is used.
    """
    if expiration == _default_expiration_duration_opt:
        duration = _default_expiration_duration()
        expiration = duration + datetime.now() if duration else None

    token_code = random_string_generator(TOKEN_NAME_PREFIX_LENGTH +
                                         MINIMUM_TOKEN_SUFFIX_LENGTH)()
    token_name = token_code[:TOKEN_NAME_PREFIX_LENGTH]
    token_secret = token_code[TOKEN_NAME_PREFIX_LENGTH:]

    assert token_name
    assert token_secret

    return AppSpecificAuthToken.create(
        user=user,
        title=title,
        expiration=expiration,
        token_name=token_name,
        token_secret=DecryptedValue(token_secret),
    )
Example #8
0
File: oauth.py Project: zhill/quay
def reset_client_secret(application):
    client_secret = random_string_generator(length=40)()

    # TODO(remove-unenc): Remove legacy field.
    if ActiveDataMigration.has_flag(ERTMigrationFlags.WRITE_OLD_FIELDS):
        application.client_secret = client_secret

    application.secure_client_secret = DecryptedValue(client_secret)
    application.save()
    return application
Example #9
0
def create_application(org, name, application_uri, redirect_uri, **kwargs):
    client_secret = kwargs.pop("client_secret",
                               random_string_generator(length=40)())
    return OAuthApplication.create(
        organization=org,
        name=name,
        application_uri=application_uri,
        redirect_uri=redirect_uri,
        secure_client_secret=DecryptedValue(client_secret),
        **kwargs)
Example #10
0
File: oauth.py Project: zhill/quay
def create_application(org, name, application_uri, redirect_uri, **kwargs):
    client_secret = kwargs.pop("client_secret",
                               random_string_generator(length=40)())

    # TODO(remove-unenc): Remove legacy field.
    old_client_secret = None
    if ActiveDataMigration.has_flag(ERTMigrationFlags.WRITE_OLD_FIELDS):
        old_client_secret = client_secret

    return OAuthApplication.create(
        organization=org,
        name=name,
        application_uri=application_uri,
        redirect_uri=redirect_uri,
        client_secret=old_client_secret,
        secure_client_secret=DecryptedValue(client_secret),
        **kwargs)
Example #11
0
def upgrade(tables, tester, progress_reporter):
    op = ProgressWrapper(original_op, progress_reporter)

    from app import app

    if app.config.get("SETUP_COMPLETE", False) or tester.is_testing():
        # Empty all access token names to fix the bug where we put the wrong name and code
        # in for some tokens.
        (
            AccessToken.update(token_name=None)
            .where(~(AccessToken.token_name >> None), AccessToken.temporary == False)
            .execute()
        )

        # AccessToken.
        logger.info("Backfilling encrypted credentials for access tokens")
        for access_token in _iterate(
            AccessToken, ((AccessToken.token_name >> None) | (AccessToken.token_name == ""))
        ):
            logger.info("Backfilling encrypted credentials for access token %s", access_token.id)
            assert access_token.code is not None
            assert access_token.code[:ACCESS_TOKEN_NAME_PREFIX_LENGTH]
            assert access_token.code[ACCESS_TOKEN_NAME_PREFIX_LENGTH:]

            token_name = access_token.code[:ACCESS_TOKEN_NAME_PREFIX_LENGTH]
            token_code = _decrypted(access_token.code[ACCESS_TOKEN_NAME_PREFIX_LENGTH:])

            (
                AccessToken.update(token_name=token_name, token_code=token_code)
                .where(AccessToken.id == access_token.id, AccessToken.code == access_token.code)
                .execute()
            )

        assert AccessToken.select().where(AccessToken.token_name >> None).count() == 0

        # Robots.
        logger.info("Backfilling encrypted credentials for robots")
        while True:
            has_row = False
            query = (
                User.select()
                .join(RobotAccountToken, JOIN.LEFT_OUTER)
                .where(User.robot == True, RobotAccountToken.id >> None)
                .limit(BATCH_SIZE)
            )

            for robot_user in query:
                logger.info("Backfilling encrypted credentials for robot %s", robot_user.id)
                has_row = True
                try:
                    RobotAccountToken.create(
                        robot_account=robot_user,
                        token=_decrypted(robot_user.email),
                        fully_migrated=False,
                    )
                except IntegrityError:
                    break

            if not has_row:
                break

        # RepositoryBuildTrigger
        logger.info("Backfilling encrypted credentials for repo build triggers")
        for repo_build_trigger in _iterate(
            RepositoryBuildTrigger, (RepositoryBuildTrigger.fully_migrated == False)
        ):
            logger.info(
                "Backfilling encrypted credentials for repo build trigger %s", repo_build_trigger.id
            )

            (
                RepositoryBuildTrigger.update(
                    secure_auth_token=_decrypted(repo_build_trigger.auth_token),
                    secure_private_key=_decrypted(repo_build_trigger.private_key),
                    fully_migrated=True,
                )
                .where(
                    RepositoryBuildTrigger.id == repo_build_trigger.id,
                    RepositoryBuildTrigger.uuid == repo_build_trigger.uuid,
                )
                .execute()
            )

        assert (
            RepositoryBuildTrigger.select()
            .where(RepositoryBuildTrigger.fully_migrated == False)
            .count()
        ) == 0

        # AppSpecificAuthToken
        logger.info("Backfilling encrypted credentials for app specific auth tokens")
        for token in _iterate(
            AppSpecificAuthToken,
            (
                (AppSpecificAuthToken.token_name >> None)
                | (AppSpecificAuthToken.token_name == "")
                | (AppSpecificAuthToken.token_secret >> None)
            ),
        ):
            logger.info("Backfilling encrypted credentials for app specific auth %s", token.id)
            assert token.token_code[AST_TOKEN_NAME_PREFIX_LENGTH:]

            token_name = token.token_code[:AST_TOKEN_NAME_PREFIX_LENGTH]
            token_secret = _decrypted(token.token_code[AST_TOKEN_NAME_PREFIX_LENGTH:])
            assert token_name
            assert token_secret

            (
                AppSpecificAuthToken.update(token_name=token_name, token_secret=token_secret)
                .where(
                    AppSpecificAuthToken.id == token.id,
                    AppSpecificAuthToken.token_code == token.token_code,
                )
                .execute()
            )

        assert (
            AppSpecificAuthToken.select().where(AppSpecificAuthToken.token_name >> None).count()
        ) == 0

        # OAuthAccessToken
        logger.info("Backfilling credentials for OAuth access tokens")
        for token in _iterate(
            OAuthAccessToken,
            ((OAuthAccessToken.token_name >> None) | (OAuthAccessToken.token_name == "")),
        ):
            logger.info("Backfilling credentials for OAuth access token %s", token.id)
            token_name = token.access_token[:OAUTH_ACCESS_TOKEN_PREFIX_LENGTH]
            token_code = Credential.from_string(
                token.access_token[OAUTH_ACCESS_TOKEN_PREFIX_LENGTH:]
            )
            assert token_name
            assert token.access_token[OAUTH_ACCESS_TOKEN_PREFIX_LENGTH:]

            (
                OAuthAccessToken.update(token_name=token_name, token_code=token_code)
                .where(
                    OAuthAccessToken.id == token.id,
                    OAuthAccessToken.access_token == token.access_token,
                )
                .execute()
            )

        assert (OAuthAccessToken.select().where(OAuthAccessToken.token_name >> None).count()) == 0

        # OAuthAuthorizationCode
        logger.info("Backfilling credentials for OAuth auth code")
        for code in _iterate(
            OAuthAuthorizationCode,
            ((OAuthAuthorizationCode.code_name >> None) | (OAuthAuthorizationCode.code_name == "")),
        ):
            logger.info("Backfilling credentials for OAuth auth code %s", code.id)
            user_code = code.code or random_string_generator(AUTHORIZATION_CODE_PREFIX_LENGTH * 2)()
            code_name = user_code[:AUTHORIZATION_CODE_PREFIX_LENGTH]
            code_credential = Credential.from_string(user_code[AUTHORIZATION_CODE_PREFIX_LENGTH:])
            assert code_name
            assert user_code[AUTHORIZATION_CODE_PREFIX_LENGTH:]

            (
                OAuthAuthorizationCode.update(code_name=code_name, code_credential=code_credential)
                .where(OAuthAuthorizationCode.id == code.id)
                .execute()
            )

        assert (
            OAuthAuthorizationCode.select().where(OAuthAuthorizationCode.code_name >> None).count()
        ) == 0

        # OAuthApplication
        logger.info("Backfilling secret for OAuth applications")
        for app in _iterate(OAuthApplication, OAuthApplication.fully_migrated == False):
            logger.info("Backfilling secret for OAuth application %s", app.id)
            client_secret = app.client_secret or str(uuid.uuid4())
            secure_client_secret = _decrypted(client_secret)

            (
                OAuthApplication.update(
                    secure_client_secret=secure_client_secret, fully_migrated=True
                )
                .where(OAuthApplication.id == app.id, OAuthApplication.fully_migrated == False)
                .execute()
            )

        assert (
            OAuthApplication.select().where(OAuthApplication.fully_migrated == False).count()
        ) == 0

    # Adjust existing fields to be nullable.
    op.alter_column("accesstoken", "code", nullable=True, existing_type=sa.String(length=255))
    op.alter_column(
        "oauthaccesstoken", "access_token", nullable=True, existing_type=sa.String(length=255)
    )
    op.alter_column(
        "oauthauthorizationcode", "code", nullable=True, existing_type=sa.String(length=255)
    )
    op.alter_column(
        "appspecificauthtoken", "token_code", nullable=True, existing_type=sa.String(length=255)
    )

    # Adjust new fields to be non-nullable.
    op.alter_column(
        "accesstoken", "token_name", nullable=False, existing_type=sa.String(length=255)
    )
    op.alter_column(
        "accesstoken", "token_code", nullable=False, existing_type=sa.String(length=255)
    )

    op.alter_column(
        "appspecificauthtoken", "token_name", nullable=False, existing_type=sa.String(length=255)
    )
    op.alter_column(
        "appspecificauthtoken", "token_secret", nullable=False, existing_type=sa.String(length=255)
    )

    op.alter_column(
        "oauthaccesstoken", "token_name", nullable=False, existing_type=sa.String(length=255)
    )
    op.alter_column(
        "oauthaccesstoken", "token_code", nullable=False, existing_type=sa.String(length=255)
    )

    op.alter_column(
        "oauthauthorizationcode", "code_name", nullable=False, existing_type=sa.String(length=255)
    )
    op.alter_column(
        "oauthauthorizationcode",
        "code_credential",
        nullable=False,
        existing_type=sa.String(length=255),
    )
Example #12
0
class OAuthApplication(BaseModel):
    secure_client_secret = EncryptedCharField(default_token_length=40, null=True)
    fully_migrated = BooleanField(default=False)
    client_secret = CharField(default=random_string_generator(length=40))
Example #13
0
def reset_client_secret(application):
    client_secret = random_string_generator(length=40)()
    application.secure_client_secret = DecryptedValue(client_secret)
    application.save()
    return application
Example #14
0
def upgrade(tables, tester, progress_reporter):
    op = ProgressWrapper(original_op, progress_reporter)

    # NOTE: Disconnects the Alembic database connection. We do this because the Peewee calls below
    # use a *different* connection, and if we leave the alembic connection open, it'll time out.
    # See: https://github.com/sqlalchemy/alembic/issues/630
    op.get_bind().execute("COMMIT")
    op.get_bind().invalidate()

    from app import app

    if app.config.get("SETUP_COMPLETE", False) or tester.is_testing():
        # AccessToken.
        logger.info("Backfilling encrypted credentials for access tokens")
        for access_token in _iterate(AccessToken,
                                     ((AccessToken.token_name >> None) |
                                      (AccessToken.token_name == ""))):
            logger.info(
                "Backfilling encrypted credentials for access token %s",
                access_token.id)
            assert access_token.code is not None
            assert access_token.code[:ACCESS_TOKEN_NAME_PREFIX_LENGTH]
            assert access_token.code[ACCESS_TOKEN_NAME_PREFIX_LENGTH:]

            token_name = access_token.code[:ACCESS_TOKEN_NAME_PREFIX_LENGTH]
            token_code = _decrypted(
                access_token.code[ACCESS_TOKEN_NAME_PREFIX_LENGTH:])

            (AccessToken.update(
                token_name=token_name, token_code=token_code).where(
                    AccessToken.id == access_token.id,
                    AccessToken.code == access_token.code).execute())

        assert AccessToken.select().where(
            AccessToken.token_name >> None).count() == 0

        # Robots.
        logger.info("Backfilling encrypted credentials for robots")
        while True:
            has_row = False
            query = (User.select().join(
                RobotAccountToken, JOIN.LEFT_OUTER).where(
                    User.robot == True,
                    RobotAccountToken.id >> None).limit(BATCH_SIZE))

            for robot_user in query:
                logger.info("Backfilling encrypted credentials for robot %s",
                            robot_user.id)
                has_row = True
                try:
                    RobotAccountToken.create(
                        robot_account=robot_user,
                        token=_decrypted(robot_user.email),
                        fully_migrated=False,
                    )
                except IntegrityError:
                    break

            if not has_row:
                break

        # RepositoryBuildTrigger
        logger.info(
            "Backfilling encrypted credentials for repo build triggers")
        for repo_build_trigger in _iterate(
                RepositoryBuildTrigger,
            (RepositoryBuildTrigger.fully_migrated == False)):
            logger.info(
                "Backfilling encrypted credentials for repo build trigger %s",
                repo_build_trigger.id)

            (RepositoryBuildTrigger.update(
                secure_auth_token=_decrypted(repo_build_trigger.auth_token),
                secure_private_key=_decrypted(repo_build_trigger.private_key),
                fully_migrated=True,
            ).where(
                RepositoryBuildTrigger.id == repo_build_trigger.id,
                RepositoryBuildTrigger.uuid == repo_build_trigger.uuid,
            ).execute())

        assert (RepositoryBuildTrigger.select().where(
            RepositoryBuildTrigger.fully_migrated == False).count()) == 0

        # AppSpecificAuthToken
        logger.info(
            "Backfilling encrypted credentials for app specific auth tokens")
        for token in _iterate(
                AppSpecificAuthToken,
            ((AppSpecificAuthToken.token_name >> None)
             | (AppSpecificAuthToken.token_name == "")
             | (AppSpecificAuthToken.token_secret >> None)),
        ):
            logger.info(
                "Backfilling encrypted credentials for app specific auth %s",
                token.id)
            assert token.token_code[AST_TOKEN_NAME_PREFIX_LENGTH:]

            token_name = token.token_code[:AST_TOKEN_NAME_PREFIX_LENGTH]
            token_secret = _decrypted(
                token.token_code[AST_TOKEN_NAME_PREFIX_LENGTH:])
            assert token_name
            assert token_secret

            (AppSpecificAuthToken.update(
                token_name=token_name, token_secret=token_secret).where(
                    AppSpecificAuthToken.id == token.id,
                    AppSpecificAuthToken.token_code == token.token_code,
                ).execute())

        assert (AppSpecificAuthToken.select().where(
            AppSpecificAuthToken.token_name >> None).count()) == 0

        # OAuthAccessToken
        logger.info("Backfilling credentials for OAuth access tokens")
        for token in _iterate(
                OAuthAccessToken,
            ((OAuthAccessToken.token_name >> None) |
             (OAuthAccessToken.token_name == "")),
        ):
            logger.info("Backfilling credentials for OAuth access token %s",
                        token.id)
            token_name = token.access_token[:OAUTH_ACCESS_TOKEN_PREFIX_LENGTH]
            token_code = Credential.from_string(
                token.access_token[OAUTH_ACCESS_TOKEN_PREFIX_LENGTH:])
            assert token_name
            assert token.access_token[OAUTH_ACCESS_TOKEN_PREFIX_LENGTH:]

            (OAuthAccessToken.update(
                token_name=token_name, token_code=token_code).where(
                    OAuthAccessToken.id == token.id,
                    OAuthAccessToken.access_token == token.access_token,
                ).execute())

        assert (OAuthAccessToken.select().where(
            OAuthAccessToken.token_name >> None).count()) == 0

        # OAuthAuthorizationCode
        logger.info("Backfilling credentials for OAuth auth code")
        for code in _iterate(
                OAuthAuthorizationCode,
            ((OAuthAuthorizationCode.code_name >> None) |
             (OAuthAuthorizationCode.code_name == "")),
        ):
            logger.info("Backfilling credentials for OAuth auth code %s",
                        code.id)
            user_code = code.code or random_string_generator(
                AUTHORIZATION_CODE_PREFIX_LENGTH * 2)()
            code_name = user_code[:AUTHORIZATION_CODE_PREFIX_LENGTH]
            code_credential = Credential.from_string(
                user_code[AUTHORIZATION_CODE_PREFIX_LENGTH:])
            assert code_name
            assert user_code[AUTHORIZATION_CODE_PREFIX_LENGTH:]

            (OAuthAuthorizationCode.update(
                code_name=code_name, code_credential=code_credential).where(
                    OAuthAuthorizationCode.id == code.id).execute())

        assert (OAuthAuthorizationCode.select().where(
            OAuthAuthorizationCode.code_name >> None).count()) == 0

        # OAuthApplication
        logger.info("Backfilling secret for OAuth applications")
        for app in _iterate(OAuthApplication,
                            OAuthApplication.fully_migrated == False):
            logger.info("Backfilling secret for OAuth application %s", app.id)
            client_secret = app.client_secret or str(uuid.uuid4())
            secure_client_secret = _decrypted(client_secret)

            (OAuthApplication.update(
                secure_client_secret=secure_client_secret,
                fully_migrated=True).where(
                    OAuthApplication.id == app.id,
                    OAuthApplication.fully_migrated == False).execute())

        assert (OAuthApplication.select().where(
            OAuthApplication.fully_migrated == False).count()) == 0