Beispiel #1
0
def user_reference(username):
    user = model.user.get_namespace_user(username)
    if not user:
        return username

    if user.robot:
        parts = parse_robot_username(username)
        user = model.user.get_namespace_user(parts[0])

        return """<span><img src="%s" alt="Robot"> <b>%s</b></span>""" % (
            icon_path("wrench"),
            username,
        )

    avatar_html = avatar.get_mail_html(
        user.username, user.email, 24, "org" if user.organization else "user"
    )

    return """
  <span>
  %s
  <b>%s</b>
  </span>""" % (
        avatar_html,
        username,
    )
Beispiel #2
0
def test_syncing(user_creation, invite_only_user_creation, starting_membership, group_membership,
                 expected_membership, blacklisted_emails, app):
  org = model.organization.get_organization('buynlarge')

  # Necessary for the fake auth entries to be created in FederatedLogin.
  database.LoginService.create(name=_FAKE_AUTH)

  # Assert the team is empty, so we have a clean slate.
  sync_team_info = model.team.get_team_sync_information('buynlarge', 'synced')
  assert len(list(model.team.list_team_users(sync_team_info.team))) == 0

  # Add the existing starting members to the team.
  for starting_member in starting_membership:
    (quay_username, fakeauth_username) = starting_member
    if '+' in quay_username:
      # Add a robot.
      (_, shortname) = parse_robot_username(quay_username)
      robot, _ = model.user.create_robot(shortname, org)
      model.team.add_user_to_team(robot, sync_team_info.team)
    else:
      email = quay_username + '@devtable.com'

      if fakeauth_username is None:
        quay_user = model.user.create_user_noverify(quay_username, email)
      else:
        quay_user = model.user.create_federated_user(quay_username, email, _FAKE_AUTH,
                                                      fakeauth_username, False)

      model.team.add_user_to_team(quay_user, sync_team_info.team)

  # Call syncing on the team.
  fake_auth = FakeUsers(group_membership)
  assert sync_team(fake_auth, sync_team_info)

  # Ensure the last updated time and transaction_id's have changed.
  updated_sync_info = model.team.get_team_sync_information('buynlarge', 'synced')
  assert updated_sync_info.last_updated is not None
  assert updated_sync_info.transaction_id != sync_team_info.transaction_id

  users_expected = set([name for name in expected_membership if '+' not in name])
  robots_expected = set([name for name in expected_membership if '+' in name])
  assert len(users_expected) + len(robots_expected) == len(expected_membership)

  # Check that the team's users match those expected.
  service_user_map = model.team.get_federated_team_member_mapping(sync_team_info.team,
                                                                  _FAKE_AUTH)
  assert set(service_user_map.keys()) == users_expected

  quay_users = model.team.list_team_users(sync_team_info.team)
  assert len(quay_users) == len(users_expected)

  for quay_user in quay_users:
    fakeauth_record = model.user.lookup_federated_login(quay_user, _FAKE_AUTH)
    assert fakeauth_record is not None
    assert fakeauth_record.service_ident in users_expected
    assert service_user_map[fakeauth_record.service_ident] == quay_user.id

  # Check that the team's robots match those expected.
  robots_found = set([r.username for r in model.team.list_team_robots(sync_team_info.team)])
  assert robots_expected == robots_found
Beispiel #3
0
def change_username(user_id, new_username):
    (username_valid, username_issue) = validate_username(new_username)
    if not username_valid:
        raise InvalidUsernameException("Invalid username %s: %s" %
                                       (new_username, username_issue))

    with db_transaction():
        # Reload the user for update
        user = db_for_update(User.select().where(User.id == user_id)).get()

        # Rename the robots
        for robot in db_for_update(
                _list_entity_robots(user.username,
                                    include_metadata=False,
                                    include_token=False)):
            _, robot_shortname = parse_robot_username(robot.username)
            new_robot_name = format_robot_username(new_username,
                                                   robot_shortname)
            robot.username = new_robot_name
            robot.save()

        # Rename the user
        user.username = new_username
        user.save()

        # Remove any prompts for username.
        remove_user_prompt(user, "confirm_username")

        return user
Beispiel #4
0
def test_filter_repositories(username, include_public, filter_to_namespace,
                             repo_kind, initialized_db):
    namespace = username if filter_to_namespace else None
    if '+' in username and filter_to_namespace:
        namespace, _ = parse_robot_username(username)

    user = get_namespace_user(username)
    query = (Repository.select().distinct().join(
        Namespace, on=(Repository.namespace_user == Namespace.id
                       )).switch(Repository).join(RepositoryPermission,
                                                  JOIN.LEFT_OUTER))

    # Prime the cache.
    Repository.kind.get_id('image')

    with assert_query_count(1):
        found = list(
            filter_to_repos_for_user(query,
                                     user.id,
                                     namespace=namespace,
                                     include_public=include_public,
                                     repo_kind=repo_kind))

    expected = list(
        _get_visible_repositories_for_user(user,
                                           repo_kind=repo_kind,
                                           namespace=namespace,
                                           include_public=include_public))

    assert len(found) == len(expected)
    assert {r.id for r in found} == {r.id for r in expected}
Beispiel #5
0
def search_entity_view(username, entity, get_short_name=None):
    kind = "user"
    title = "user"
    avatar_data = avatar.get_data_for_user(entity)
    href = "/user/" + entity.username

    if entity.organization:
        kind = "organization"
        title = "org"
        avatar_data = avatar.get_data_for_org(entity)
        href = "/organization/" + entity.username
    elif entity.robot:
        parts = parse_robot_username(entity.username)
        if parts[0] == username:
            href = "/user/" + username + "?tab=robots&showRobot=" + entity.username
        else:
            href = "/organization/" + parts[0] + "?tab=robots&showRobot=" + entity.username

        kind = "robot"
        title = "robot"
        avatar_data = None

    data = {
        "title": title,
        "kind": kind,
        "avatar": avatar_data,
        "name": entity.username,
        "score": ENTITY_SEARCH_SCORE,
        "href": href,
    }

    if get_short_name:
        data["short_name"] = get_short_name(entity.username)

    return data
Beispiel #6
0
def verify_robot(robot_username, password):
  try:
    password = remove_unicode(password)
  except UnicodeEncodeError:
    msg = ('Could not find robot with username: %s and supplied password.' %
            robot_username)
    raise InvalidRobotException(msg)

  result = parse_robot_username(robot_username)
  if result is None:
    raise InvalidRobotException('%s is an invalid robot name' % robot_username)

  robot = lookup_robot(robot_username)
  assert robot.robot

  # Lookup the token for the robot.
  try:
    token_data = RobotAccountToken.get(robot_account=robot)
    if not token_data.token.matches(password):
      msg = ('Could not find robot with username: %s and supplied password.' %
             robot_username)
      raise InvalidRobotException(msg)
  except RobotAccountToken.DoesNotExist:
    # TODO(remove-unenc): Remove once migrated.
    if not ActiveDataMigration.has_flag(ERTMigrationFlags.READ_OLD_FIELDS):
      raise InvalidRobotException(msg)

    if password.find('robot:') >= 0:
      # Just to be sure.
      raise InvalidRobotException(msg)

    query = (User
             .select()
             .join(FederatedLogin)
             .join(LoginService)
             .where(FederatedLogin.service_ident == password, LoginService.name == 'quayrobot',
                    User.username == robot_username))

    try:
      robot = query.get()
    except User.DoesNotExist:
      msg = ('Could not find robot with username: %s and supplied password.' %
             robot_username)
      raise InvalidRobotException(msg)

  # Find the owner user and ensure it is not disabled.
  try:
    owner = User.get(User.username == result[0])
  except User.DoesNotExist:
    raise InvalidRobotException('Robot %s owner does not exist' % robot_username)

  if not owner.enabled:
    raise InvalidRobotException('This user has been disabled. Please contact your administrator.')

  # Mark that the robot was accessed.
  _basequery.update_last_accessed(robot)

  return robot
Beispiel #7
0
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
Beispiel #8
0
Datei: team.py Projekt: ynnt/quay
        def wrapper(self, *args, **kwargs):
            # Team syncing can only be enabled if we have a federated service.
            if features.TEAM_SYNCING and authentication.federated_service:
                orgname = kwargs["orgname"]
                teamname = kwargs["teamname"]
                if model.team.get_team_sync_information(orgname, teamname):
                    if not except_robots or not parse_robot_username(
                            kwargs.get("membername", "")):
                        raise InvalidRequest(
                            "Cannot call this method on an auth-synced team")

            return func(self, *args, **kwargs)
Beispiel #9
0
def set_mirroring_robot(repository, robot):
    """
    Sets the mirroring robot for the repository.
    """
    assert robot.robot
    namespace, _ = parse_robot_username(robot.username)
    if namespace != repository.namespace_user.username:
        raise DataModelException("Cannot use robot for mirroring")

    mirror = get_mirror(repository)
    mirror.internal_robot = robot
    mirror.save()
Beispiel #10
0
    def get(self, orgname, membername):
        """
        Retrieves the details of a member of the organization.
        """
        permission = AdministerOrganizationPermission(orgname)
        if permission.can():
            # Lookup the user.
            member = model.user.get_user(membername)
            if not member:
                raise NotFound()

            organization = model.user.get_user_or_org(orgname)
            if not organization:
                raise NotFound()

            # Lookup the user's information in the organization.
            teams = list(
                model.team.get_user_teams_within_org(membername, organization))
            if not teams:
                # 404 if the user is not a robot under the organization, as that means the referenced
                # user or robot is not a member of this organization.
                if not member.robot:
                    raise NotFound()

                namespace, _ = parse_robot_username(member.username)
                if namespace != orgname:
                    raise NotFound()

            repo_permissions = model.permission.list_organization_member_permissions(
                organization, member)

            def local_team_view(team):
                return {
                    "name": team.name,
                    "avatar": avatar.get_data_for_team(team),
                }

            return {
                "name":
                member.username,
                "kind":
                "robot" if member.robot else "user",
                "avatar":
                avatar.get_data_for_user(member),
                "teams": [local_team_view(team) for team in teams],
                "repositories": [
                    permission.repository.name
                    for permission in repo_permissions
                ],
            }

        raise Unauthorized()
Beispiel #11
0
  def _setup_robot_for_mirroring(self, namespace_name, repo_name, robot_username):
    """ Validate robot exists and give write permissions. """
    robot = model.user.lookup_robot(robot_username)
    assert robot.robot

    namespace, _ = parse_robot_username(robot_username)
    if namespace != namespace_name:
      raise model.DataModelException('Invalid robot')

    # Ensure the robot specified has access to the repository. If not, grant it.
    permissions = model.permission.get_user_repository_permissions(robot, namespace_name, repo_name)
    if not permissions or permissions[0].role.name == 'read':
      model.permission.set_user_repo_permission(robot.username, namespace_name, repo_name, 'write')

    return robot
Beispiel #12
0
def cleanup_old_robots(page_size=50, force=False):
    """ Deletes any robots that live under namespaces that no longer exist. """
    if not force and not app.config.get("SETUP_COMPLETE", False):
        return

    # Collect the robot accounts to delete.
    page_number = 1
    to_delete = []
    encountered_namespaces = {}

    while True:
        found_bots = False
        for robot in list(User.select().where(User.robot == True).paginate(
                page_number, page_size)):
            found_bots = True
            logger.info("Checking robot %s (page %s)", robot.username,
                        page_number)
            parsed = parse_robot_username(robot.username)
            if parsed is None:
                continue

            namespace, _ = parsed
            if namespace in encountered_namespaces:
                if not encountered_namespaces[namespace]:
                    logger.info("Marking %s to be deleted", robot.username)
                    to_delete.append(robot)
            else:
                try:
                    User.get(username=namespace)
                    encountered_namespaces[namespace] = True
                except User.DoesNotExist:
                    # Save the robot account for deletion.
                    logger.info("Marking %s to be deleted", robot.username)
                    to_delete.append(robot)
                    encountered_namespaces[namespace] = False

        if not found_bots:
            break

        page_number = page_number + 1

    # Cleanup any robot accounts whose corresponding namespace doesn't exist.
    logger.info("Found %s robots to delete", len(to_delete))
    for index, robot in enumerate(to_delete):
        logger.info("Deleting robot %s of %s (%s)", index, len(to_delete),
                    robot.username)
        robot.delete_instance(recursive=True, delete_nullable=True)
Beispiel #13
0
def set_user_repo_permission(username, namespace_name, repository_name, role_name):
  if username == namespace_name:
    raise DataModelException('Namespace owner must always be admin.')

  try:
    user = User.get(User.username == username)
  except User.DoesNotExist:
    raise DataModelException('Invalid username: %s' % username)

  if user.robot:
    parts = parse_robot_username(user.username)
    if not parts:
      raise DataModelException('Invalid robot: %s' % username)

    robot_namespace, _ = parts
    if robot_namespace != namespace_name:
      raise DataModelException('Cannot add robot %s under namespace %s' %
                               (username, namespace_name))

  return __set_entity_repo_permission(user, 'user', namespace_name, repository_name, role_name)
Beispiel #14
0
def verify_robot(robot_username, password):
    try:
        password.encode("ascii")
    except UnicodeEncodeError:
        msg = "Could not find robot with username: %s and supplied password." % robot_username
        raise InvalidRobotException(msg)

    result = parse_robot_username(robot_username)
    if result is None:
        raise InvalidRobotException("%s is an invalid robot name" %
                                    robot_username)

    robot = lookup_robot(robot_username)
    assert robot.robot

    # Lookup the token for the robot.
    try:
        token_data = RobotAccountToken.get(robot_account=robot)
        if not token_data.token.matches(password):
            msg = "Could not find robot with username: %s and supplied password." % robot_username
            raise InvalidRobotException(msg)
    except RobotAccountToken.DoesNotExist:
        msg = "Could not find robot with username: %s and supplied password." % robot_username
        raise InvalidRobotException(msg)

    # Find the owner user and ensure it is not disabled.
    try:
        owner = User.get(User.username == result[0])
    except User.DoesNotExist:
        raise InvalidRobotException("Robot %s owner does not exist" %
                                    robot_username)

    if not owner.enabled:
        raise InvalidRobotException(
            "This user has been disabled. Please contact your administrator.")

    # Mark that the robot was accessed.
    _basequery.update_last_accessed(robot)

    return robot
Beispiel #15
0
def test_retrieve_robots(endpoint, params, bot_endpoint, include_token, limit,
                         app, client):
    params['token'] = 'true' if include_token else 'false'

    if limit is not None:
        params['limit'] = limit

    with client_with_identity('devtable', client) as cl:
        result = conduct_api_call(cl, endpoint, 'GET', params, None)

        if limit is not None:
            assert len(result.json['robots']) <= limit

        for robot in result.json['robots']:
            assert (robot.get('token') is not None) == include_token
            if include_token:
                bot_params = dict(params)
                bot_params['robot_shortname'] = parse_robot_username(
                    robot['name'])[1]
                result = conduct_api_call(cl, bot_endpoint, 'GET', bot_params,
                                          None)
                assert robot.get('token') == result.json['token']
Beispiel #16
0
def test_retrieve_robots(endpoint, params, bot_endpoint, include_token, limit,
                         app, client):
    params["token"] = "true" if include_token else "false"

    if limit is not None:
        params["limit"] = limit

    with client_with_identity("devtable", client) as cl:
        result = conduct_api_call(cl, endpoint, "GET", params, None)

        if limit is not None:
            assert len(result.json["robots"]) <= limit

        for robot in result.json["robots"]:
            assert (robot.get("token") is not None) == include_token
            if include_token:
                bot_params = dict(params)
                bot_params["robot_shortname"] = parse_robot_username(
                    robot["name"])[1]
                result = conduct_api_call(cl, bot_endpoint, "GET", bot_params,
                                          None)
                assert robot.get("token") == result.json["token"]
Beispiel #17
0
def search_entity_view(username, entity, get_short_name=None):
    kind = 'user'
    title = 'user'
    avatar_data = avatar.get_data_for_user(entity)
    href = '/user/' + entity.username

    if entity.organization:
        kind = 'organization'
        title = 'org'
        avatar_data = avatar.get_data_for_org(entity)
        href = '/organization/' + entity.username
    elif entity.robot:
        parts = parse_robot_username(entity.username)
        if parts[0] == username:
            href = '/user/' + username + '?tab=robots&showRobot=' + entity.username
        else:
            href = '/organization/' + parts[
                0] + '?tab=robots&showRobot=' + entity.username

        kind = 'robot'
        title = 'robot'
        avatar_data = None

    data = {
        'title': title,
        'kind': kind,
        'avatar': avatar_data,
        'name': entity.username,
        'score': ENTITY_SEARCH_SCORE,
        'href': href
    }

    if get_short_name:
        data['short_name'] = get_short_name(entity.username)

    return data
Beispiel #18
0
 def get_short_name(name):
     return parse_robot_username(name)[1]
Beispiel #19
0
def validate_credentials(auth_username, auth_password_or_token):
    """ Validates a pair of auth username and password/token credentials. """
    # Check for access tokens.
    if auth_username == ACCESS_TOKEN_USERNAME:
        logger.debug('Found credentials for access token')
        try:
            token = model.token.load_token_data(auth_password_or_token)
            logger.debug(
                'Successfully validated credentials for access token %s',
                token.id)
            return ValidateResult(AuthKind.credentials,
                                  token=token), CredentialKind.token
        except model.DataModelException:
            logger.warning(
                'Failed to validate credentials for access token %s',
                auth_password_or_token)
            return (ValidateResult(AuthKind.credentials,
                                   error_message='Invalid access token'),
                    CredentialKind.token)

    # Check for App Specific tokens.
    if features.APP_SPECIFIC_TOKENS and auth_username == APP_SPECIFIC_TOKEN_USERNAME:
        logger.debug('Found credentials for app specific auth token')
        token = model.appspecifictoken.access_valid_token(
            auth_password_or_token)
        if token is None:
            logger.debug(
                'Failed to validate credentials for app specific token: %s',
                auth_password_or_token)
            return (ValidateResult(AuthKind.credentials,
                                   error_message='Invalid token'),
                    CredentialKind.app_specific_token)

        if not token.user.enabled:
            logger.debug(
                'Tried to use an app specific token for a disabled user: %s',
                token.uuid)
            return (ValidateResult(
                AuthKind.credentials,
                error_message=
                'This user has been disabled. Please contact your administrator.'
            ), CredentialKind.app_specific_token)

        logger.debug(
            'Successfully validated credentials for app specific token %s',
            token.id)
        return (ValidateResult(AuthKind.credentials, appspecifictoken=token),
                CredentialKind.app_specific_token)

    # Check for OAuth tokens.
    if auth_username == OAUTH_TOKEN_USERNAME:
        return validate_oauth_token(
            auth_password_or_token), CredentialKind.oauth_token

    # Check for robots and users.
    is_robot = parse_robot_username(auth_username)
    if is_robot:
        logger.debug('Found credentials header for robot %s', auth_username)
        try:
            robot = model.user.verify_robot(auth_username,
                                            auth_password_or_token)
            logger.debug('Successfully validated credentials for robot %s',
                         auth_username)
            return ValidateResult(AuthKind.credentials,
                                  robot=robot), CredentialKind.robot
        except model.InvalidRobotException as ire:
            logger.warning('Failed to validate credentials for robot %s: %s',
                           auth_username, ire)
            return ValidateResult(AuthKind.credentials,
                                  error_message=str(ire)), CredentialKind.robot

    # Otherwise, treat as a standard user.
    (authenticated,
     err) = authentication.verify_and_link_user(auth_username,
                                                auth_password_or_token,
                                                basic_auth=True)
    if authenticated:
        logger.debug('Successfully validated credentials for user %s',
                     authenticated.username)
        return ValidateResult(AuthKind.credentials,
                              user=authenticated), CredentialKind.user
    else:
        logger.warning('Failed to validate credentials for user %s: %s',
                       auth_username, err)
        return ValidateResult(AuthKind.credentials,
                              error_message=err), CredentialKind.user
Beispiel #20
0
    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()
Beispiel #21
0
    def post(self, namespace, repository):
        """
        Request that a repository be built and pushed from the specified input.
        """
        logger.debug("User requested repository initialization.")
        request_json = request.get_json()

        dockerfile_id = request_json.get("file_id", None)
        archive_url = request_json.get("archive_url", None)

        if not dockerfile_id and not archive_url:
            raise InvalidRequest("file_id or archive_url required")

        if archive_url:
            archive_match = None
            try:
                archive_match = urlparse(archive_url)
            except ValueError:
                pass

            if not archive_match:
                raise InvalidRequest(
                    "Invalid Archive URL: Must be a valid URI")

            scheme = archive_match.scheme
            if scheme != "http" and scheme != "https":
                raise InvalidRequest(
                    "Invalid Archive URL: Must be http or https")

        context, subdir = self.get_dockerfile_context(request_json)
        tags = request_json.get("docker_tags", ["latest"])
        pull_robot_name = request_json.get("pull_robot", None)

        # Verify the security behind the pull robot.
        if pull_robot_name:
            result = parse_robot_username(pull_robot_name)
            if result:
                try:
                    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, _) = result
                if not AdministerOrganizationPermission(robot_namespace).can():
                    raise Unauthorized()
            else:
                raise Unauthorized()

        # Check if the dockerfile resource has already been used. If so, then it
        # can only be reused if the user has access to the repository in which the
        # dockerfile was previously built.
        if dockerfile_id:
            associated_repository = model.build.get_repository_for_resource(
                dockerfile_id)
            if associated_repository:
                if not ModifyRepositoryPermission(
                        associated_repository.namespace_user.username,
                        associated_repository.name):
                    raise Unauthorized()

        # Start the build.
        repo = model.repository.get_repository(namespace, repository)
        if repo is None:
            raise NotFound()

        try:
            build_name = (user_files.get_file_checksum(dockerfile_id)
                          if dockerfile_id else hashlib.sha224(
                              archive_url.encode("ascii")).hexdigest()[0:7])
        except IOError:
            raise InvalidRequest("File %s could not be found or is invalid" %
                                 dockerfile_id)

        prepared = PreparedBuild()
        prepared.build_name = build_name
        prepared.dockerfile_id = dockerfile_id
        prepared.archive_url = archive_url
        prepared.tags = tags
        prepared.subdirectory = subdir
        prepared.context = context
        prepared.is_manual = True
        prepared.metadata = {}
        try:
            build_request = start_build(repo,
                                        prepared,
                                        pull_robot_name=pull_robot_name)
        except MaximumBuildsQueuedException:
            abort(429, message="Maximum queued build rate exceeded.")
        except BuildTriggerDisabledException:
            abort(400, message="Build trigger is disabled")

        resp = build_status_view(build_request)
        repo_string = "%s/%s" % (namespace, repository)
        headers = {
            "Location":
            api.url_for(RepositoryBuildStatus,
                        repository=repo_string,
                        build_uuid=build_request.uuid),
        }
        return resp, 201, headers
Beispiel #22
0
  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()