def enable_mirroring_for_repository( repository, root_rule, internal_robot, external_reference, sync_interval, external_registry_username=None, external_registry_password=None, external_registry_config=None, is_enabled=True, sync_start_date=None, ): """ Create a RepoMirrorConfig and set the Repository to the MIRROR state. """ assert internal_robot.robot namespace, _ = parse_robot_username(internal_robot.username) if namespace != repository.namespace_user.username: raise DataModelException("Cannot use robot for mirroring") with db_transaction(): # Create the RepoMirrorConfig try: username = ( DecryptedValue(external_registry_username) if external_registry_username else None ) password = ( DecryptedValue(external_registry_password) if external_registry_password else None ) mirror = RepoMirrorConfig.create( repository=repository, root_rule=root_rule, is_enabled=is_enabled, internal_robot=internal_robot, external_reference=external_reference, external_registry_username=username, external_registry_password=password, external_registry_config=external_registry_config or {}, sync_interval=sync_interval, sync_start_date=sync_start_date or datetime.utcnow(), ) except IntegrityError: return RepoMirrorConfig.get(repository=repository) # Change Repository state to mirroring mode as needed if repository.state != RepositoryState.MIRROR: query = Repository.update(state=RepositoryState.MIRROR).where( Repository.id == repository.id ) if not query.execute(): raise DataModelException("Could not change the state of the repository") return mirror
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 update_build_trigger(trigger, config, auth_token=None, write_token=None): trigger.config = json.dumps(config or {}) if auth_token is not None: trigger.secure_auth_token = DecryptedValue(auth_token) if write_token is not None: trigger.write_token = write_token trigger.save()
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 update_build_trigger(trigger, config, auth_token=None, write_token=None): trigger.config = json.dumps(config or {}) # TODO(remove-unenc): Remove legacy field. if auth_token is not None: if ActiveDataMigration.has_flag(ERTMigrationFlags.WRITE_OLD_FIELDS): trigger.auth_token = auth_token trigger.secure_auth_token = DecryptedValue(auth_token) if write_token is not None: trigger.write_token = write_token trigger.save()
def create_build_trigger(repo, service_name, auth_token, user, pull_robot=None, config=None): service = BuildTriggerService.get(name=service_name) secure_auth_token = DecryptedValue(auth_token) if auth_token else None trigger = RepositoryBuildTrigger.create( repository=repo, service=service, secure_auth_token=secure_auth_token, connected_user=user, pull_robot=pull_robot, config=json.dumps(config or {}), ) return trigger
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 create_build_trigger(repo, service_name, auth_token, user, pull_robot=None, config=None): service = BuildTriggerService.get(name=service_name) # TODO(remove-unenc): Remove legacy field. old_auth_token = None if ActiveDataMigration.has_flag(ERTMigrationFlags.WRITE_OLD_FIELDS): old_auth_token = auth_token secure_auth_token = DecryptedValue(auth_token) if auth_token else None trigger = RepositoryBuildTrigger.create( repository=repo, service=service, auth_token=old_auth_token, secure_auth_token=secure_auth_token, connected_user=user, pull_robot=pull_robot, config=json.dumps(config or {}), ) return trigger
def post(self, namespace_name, repo_name, trigger_uuid): """ Activate the specified build trigger. """ trigger = get_trigger(trigger_uuid) handler = BuildTriggerHandler.get_handler(trigger) if handler.is_active(): raise InvalidRequest("Trigger config is not sufficient for activation.") user_permission = UserAdminPermission(trigger.connected_user.username) if user_permission.can(): # Update the pull robot (if any). pull_robot_name = request.get_json().get("pull_robot", None) if pull_robot_name: try: pull_robot = model.user.lookup_robot(pull_robot_name) except model.InvalidRobotException: raise NotFound() # Make sure the user has administer permissions for the robot's namespace. (robot_namespace, _) = parse_robot_username(pull_robot_name) if not AdministerOrganizationPermission(robot_namespace).can(): raise Unauthorized() # Make sure the namespace matches that of the trigger. if robot_namespace != namespace_name: raise Unauthorized() # Set the pull robot. trigger.pull_robot = pull_robot # Update the config. new_config_dict = request.get_json()["config"] write_token_name = "Build Trigger: %s" % trigger.service.name write_token = model.token.create_delegate_token( namespace_name, repo_name, write_token_name, "write" ) try: path = url_for("webhooks.build_trigger_webhook", trigger_uuid=trigger.uuid) authed_url = _prepare_webhook_url( app.config["PREFERRED_URL_SCHEME"], "$token", write_token.get_code(), app.config["SERVER_HOSTNAME"], path, ) handler = BuildTriggerHandler.get_handler(trigger, new_config_dict) final_config, private_config = handler.activate(authed_url) if "private_key" in private_config: trigger.secure_private_key = DecryptedValue(private_config["private_key"]) except TriggerException as exc: write_token.delete_instance() raise request_error(message=exc.message) # Save the updated config. update_build_trigger(trigger, final_config, write_token=write_token) # Log the trigger setup. repo = model.repository.get_repository(namespace_name, repo_name) log_action( "setup_repo_trigger", namespace_name, { "repo": repo_name, "namespace": namespace_name, "trigger_id": trigger.uuid, "service": trigger.service.name, "pull_robot": trigger.pull_robot.username if trigger.pull_robot else None, "config": final_config, }, repo=repo, ) return trigger_view(trigger, can_admin=True) else: raise Unauthorized()
def _decrypted(value): if value is None: return None assert isinstance(value, basestring) return DecryptedValue(value)
def reset_client_secret(application): client_secret = random_string_generator(length=40)() application.secure_client_secret = DecryptedValue(client_secret) application.save() return application
def post(self, namespace_name, repo_name, trigger_uuid): """ Activate the specified build trigger. """ trigger = get_trigger(trigger_uuid) handler = BuildTriggerHandler.get_handler(trigger) if handler.is_active(): raise InvalidRequest('Trigger config is not sufficient for activation.') user_permission = UserAdminPermission(trigger.connected_user.username) if user_permission.can(): # Update the pull robot (if any). pull_robot_name = request.get_json().get('pull_robot', None) if pull_robot_name: try: pull_robot = model.user.lookup_robot(pull_robot_name) except model.InvalidRobotException: raise NotFound() # Make sure the user has administer permissions for the robot's namespace. (robot_namespace, _) = parse_robot_username(pull_robot_name) if not AdministerOrganizationPermission(robot_namespace).can(): raise Unauthorized() # Make sure the namespace matches that of the trigger. if robot_namespace != namespace_name: raise Unauthorized() # Set the pull robot. trigger.pull_robot = pull_robot # Update the config. new_config_dict = request.get_json()['config'] write_token_name = 'Build Trigger: %s' % trigger.service.name write_token = model.token.create_delegate_token(namespace_name, repo_name, write_token_name, 'write') try: path = url_for('webhooks.build_trigger_webhook', trigger_uuid=trigger.uuid) authed_url = _prepare_webhook_url(app.config['PREFERRED_URL_SCHEME'], '$token', write_token.get_code(), app.config['SERVER_HOSTNAME'], path) handler = BuildTriggerHandler.get_handler(trigger, new_config_dict) final_config, private_config = handler.activate(authed_url) if 'private_key' in private_config: trigger.secure_private_key = DecryptedValue(private_config['private_key']) # TODO(remove-unenc): Remove legacy field. if ActiveDataMigration.has_flag(ERTMigrationFlags.WRITE_OLD_FIELDS): trigger.private_key = private_config['private_key'] except TriggerException as exc: write_token.delete_instance() raise request_error(message=exc.message) # Save the updated config. update_build_trigger(trigger, final_config, write_token=write_token) # Log the trigger setup. repo = model.repository.get_repository(namespace_name, repo_name) log_action('setup_repo_trigger', namespace_name, {'repo': repo_name, 'namespace': namespace_name, 'trigger_id': trigger.uuid, 'service': trigger.service.name, 'pull_robot': trigger.pull_robot.username if trigger.pull_robot else None, 'config': final_config}, repo=repo) return trigger_view(trigger, can_admin=True) else: raise Unauthorized()
def upgrade(op, tables, tester): from app import app logger.info("Migrating to external_reference from existing columns") op.add_column("repomirrorconfig", sa.Column("external_reference", sa.Text(), nullable=True)) logger.info("Reencrypting existing columns") if app.config.get("SETUP_COMPLETE", False) and not tester.is_testing(): old_key_encrypter = FieldEncrypter(app.config.get("SECRET_KEY")) starting_id = 0 has_additional = True while has_additional: has_additional = False query = RepoMirrorConfig.select().where(RepoMirrorConfig.id >= starting_id).limit(10) for row in query: starting_id = max(starting_id, row.id + 1) has_additional = True logger.debug("Re-encrypting information for row %s", row.id) has_changes = False try: if row.external_registry_username: row.external_registry_username.decrypt() except DecryptionFailureException: # Encrypted using the older SECRET_KEY. Migrate it. decrypted = row.external_registry_username.decrypt(old_key_encrypter) row.external_registry_username = DecryptedValue(decrypted) has_changes = True try: if row.external_registry_password: row.external_registry_password.decrypt() except DecryptionFailureException: # Encrypted using the older SECRET_KEY. Migrate it. decrypted = row.external_registry_password.decrypt(old_key_encrypter) row.external_registry_password = DecryptedValue(decrypted) has_changes = True if has_changes: logger.debug("Saving re-encrypted information for row %s", row.id) row.save() if app.config.get("SETUP_COMPLETE", False) or tester.is_testing(): for repo_mirror in _iterate( RepoMirrorConfig, (RepoMirrorConfig.external_reference >> None) ): repo = "%s/%s/%s" % ( repo_mirror.external_registry, repo_mirror.external_namespace, repo_mirror.external_repository, ) logger.info("migrating %s" % repo) repo_mirror.external_reference = repo repo_mirror.save() op.drop_column("repomirrorconfig", "external_registry") op.drop_column("repomirrorconfig", "external_namespace") op.drop_column("repomirrorconfig", "external_repository") op.alter_column( "repomirrorconfig", "external_reference", nullable=False, existing_type=sa.Text() ) tester.populate_column("repomirrorconfig", "external_reference", tester.TestDataType.String)