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)
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
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)
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
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, )
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), )
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
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)
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)
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), )
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))
def reset_client_secret(application): client_secret = random_string_generator(length=40)() application.secure_client_secret = DecryptedValue(client_secret) application.save() return application
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