def attach_gitlab_build_trigger(): state = request.args.get("state", None) if not state: abort(400) state = state[len("repo:") :] try: [namespace, repository] = state.split("/") except ValueError: abort(400) permission = AdministerRepositoryPermission(namespace, repository) if permission.can(): code = request.args.get("code") token = gitlab_trigger.exchange_code_for_token( app.config, client, code, redirect_suffix="/trigger" ) if not token: msg = "Could not exchange token. It may have expired." abort(404, message=msg) repo = model.repository.get_repository(namespace, repository) if not repo: msg = "Invalid repository: %s/%s" % (namespace, repository) abort(404, message=msg) elif repo.kind.name != "image": abort(501) trigger = model.build.create_build_trigger(repo, "gitlab", token, current_user.db_user()) repo_path = "%s/%s" % (namespace, repository) full_url = url_for("web.buildtrigger", path=repo_path, trigger=trigger.uuid) logger.debug("Redirecting to full url: %s", full_url) return redirect(full_url) abort(403)
def attach_bitbucket_trigger(namespace_name, repo_name): permission = AdministerRepositoryPermission(namespace_name, repo_name) if permission.can(): repo = model.repository.get_repository(namespace_name, repo_name) if not repo: msg = "Invalid repository: %s/%s" % (namespace_name, repo_name) abort(404, message=msg) elif repo.kind.name != "image": abort(501) trigger = model.build.create_build_trigger( repo, BitbucketBuildTrigger.service_name(), None, current_user.db_user()) try: oauth_info = BuildTriggerHandler.get_handler( trigger).get_oauth_url() except TriggerProviderException: trigger.delete_instance() logger.debug("Could not retrieve Bitbucket OAuth URL") abort(500) config = {"access_token": oauth_info["access_token"]} access_token_secret = oauth_info["access_token_secret"] model.build.update_build_trigger(trigger, config, auth_token=access_token_secret) return redirect(oauth_info["url"]) abort(403)
def get_repository_build(self, uuid): try: build = model.build.get_repository_build(uuid) except model.InvalidRepositoryBuildException as e: raise InvalidRepositoryBuildException(str(e)) repo_namespace = build.repository_namespace_user_username repo_name = build.repository_name can_read = ReadRepositoryPermission(repo_namespace, repo_name).can() can_write = ModifyRepositoryPermission(repo_namespace, repo_name).can() can_admin = AdministerRepositoryPermission(repo_namespace, repo_name).can() job_config = get_job_config(build.job_config) phase, status, error = _get_build_status(build) url = userfiles.get_file_url(self.resource_key, get_request_ip(), requires_cors=True) return RepositoryBuild( build.uuid, build.logs_archived, repo_namespace, repo_name, can_write, can_read, _create_user(build.pull_robot), build.resource_key, BuildTrigger(build.trigger.uuid, build.trigger.service.name, _create_user(build.trigger.pull_robot), can_read, can_admin, True), build.display_name, build.display_name, build.started, job_config, phase, status, error, url)
def build_status_view(build_obj): phase, status, error = _get_build_status(build_obj) repo_namespace = build_obj.repository.namespace_user.username repo_name = build_obj.repository.name can_read = ReadRepositoryPermission(repo_namespace, repo_name).can() can_write = ModifyRepositoryPermission(repo_namespace, repo_name).can() can_admin = AdministerRepositoryPermission(repo_namespace, repo_name).can() job_config = get_job_config(build_obj) resp = { "id": build_obj.uuid, "phase": phase, "started": format_date(build_obj.started), "display_name": build_obj.display_name, "status": status or {}, "subdirectory": job_config.get("build_subdir", ""), "dockerfile_path": job_config.get("build_subdir", ""), "context": job_config.get("context", ""), "tags": job_config.get("docker_tags", []), "manual_user": job_config.get("manual_user", None), "is_writer": can_write, "trigger": trigger_view(build_obj.trigger, can_read, can_admin, for_build=True), "trigger_metadata": job_config.get("trigger_metadata", None) if can_read else None, "resource_key": build_obj.resource_key, "pull_robot": user_view(build_obj.pull_robot) if build_obj.pull_robot else None, "repository": { "namespace": repo_namespace, "name": repo_name }, "error": error, } if can_write or features.READER_BUILD_LOGS: if build_obj.resource_key is not None: resp["archive_url"] = user_files.get_file_url( build_obj.resource_key, get_request_ip(), requires_cors=True) elif job_config.get("archive_url", None): resp["archive_url"] = job_config["archive_url"] return resp
def build_status_view(build_obj): phase, status, error = _get_build_status(build_obj) repo_namespace = build_obj.repository.namespace_user.username repo_name = build_obj.repository.name can_read = ReadRepositoryPermission(repo_namespace, repo_name).can() can_write = ModifyRepositoryPermission(repo_namespace, repo_name).can() can_admin = AdministerRepositoryPermission(repo_namespace, repo_name).can() job_config = get_job_config(build_obj) resp = { 'id': build_obj.uuid, 'phase': phase, 'started': format_date(build_obj.started), 'display_name': build_obj.display_name, 'status': status or {}, 'subdirectory': job_config.get('build_subdir', ''), 'dockerfile_path': job_config.get('build_subdir', ''), 'context': job_config.get('context', ''), 'tags': job_config.get('docker_tags', []), 'manual_user': job_config.get('manual_user', None), 'is_writer': can_write, 'trigger': trigger_view(build_obj.trigger, can_read, can_admin, for_build=True), 'trigger_metadata': job_config.get('trigger_metadata', None) if can_read else None, 'resource_key': build_obj.resource_key, 'pull_robot': user_view(build_obj.pull_robot) if build_obj.pull_robot else None, 'repository': { 'namespace': repo_namespace, 'name': repo_name }, 'error': error, } if can_write or features.READER_BUILD_LOGS: if build_obj.resource_key is not None: resp['archive_url'] = user_files.get_file_url( build_obj.resource_key, get_request_ip(), requires_cors=True) elif job_config.get('archive_url', None): resp['archive_url'] = job_config['archive_url'] return resp
def attach_custom_build_trigger(namespace_name, repo_name): permission = AdministerRepositoryPermission(namespace_name, repo_name) if permission.can(): repo = model.repository.get_repository(namespace_name, repo_name) if not repo: msg = 'Invalid repository: %s/%s' % (namespace_name, repo_name) abort(404, message=msg) elif repo.kind.name != 'image': abort(501) trigger = model.build.create_build_trigger(repo, CustomBuildTrigger.service_name(), None, current_user.db_user()) repo_path = '%s/%s' % (namespace_name, repo_name) full_url = url_for('web.buildtrigger', path=repo_path, trigger=trigger.uuid) logger.debug('Redirecting to full url: %s', full_url) return redirect(full_url) abort(403)
def attach_gitlab_build_trigger(): state = request.args.get('state', None) if not state: abort(400) state = state[len('repo:'):] try: [namespace, repository] = state.split('/') except ValueError: abort(400) permission = AdministerRepositoryPermission(namespace, repository) if permission.can(): code = request.args.get('code') token = gitlab_trigger.exchange_code_for_token( app.config, client, code, redirect_suffix='/trigger') if not token: msg = 'Could not exchange token. It may have expired.' abort(404, message=msg) repo = model.repository.get_repository(namespace, repository) if not repo: msg = 'Invalid repository: %s/%s' % (namespace, repository) abort(404, message=msg) elif repo.kind.name != 'image': abort(501) trigger = model.build.create_build_trigger(repo, 'gitlab', token, current_user.db_user()) repo_path = '%s/%s' % (namespace, repository) full_url = url_for('web.buildtrigger', path=repo_path, trigger=trigger.uuid) logger.debug('Redirecting to full url: %s', full_url) return redirect(full_url) abort(403)
def attach_github_build_trigger(namespace_name, repo_name): permission = AdministerRepositoryPermission(namespace_name, repo_name) if permission.can(): code = request.args.get('code') token = github_trigger.exchange_code_for_token(app.config, client, code) repo = model.repository.get_repository(namespace_name, repo_name) if not repo: msg = 'Invalid repository: %s/%s' % (namespace_name, repo_name) abort(404, message=msg) elif repo.kind.name != 'image': abort(501) trigger = model.build.create_build_trigger(repo, 'github', token, current_user.db_user()) repo_path = '%s/%s' % (namespace_name, repo_name) full_url = url_for('web.buildtrigger', path=repo_path, trigger=trigger.uuid) logger.debug('Redirecting to full url: %s', full_url) return redirect(full_url) abort(403)
def post(self, namespace_name, repo_name, trigger_uuid, field_name): """ List the field values for a custom run field. """ trigger = get_trigger(trigger_uuid) config = request.get_json() or None if AdministerRepositoryPermission(namespace_name, repo_name).can(): handler = BuildTriggerHandler.get_handler(trigger, config) values = handler.list_field_values(field_name, limit=FIELD_VALUE_LIMIT) if values is None: raise NotFound() return {"values": values} else: raise Unauthorized()
def get(self, namespace, repository, parsed_args): """ Fetch the specified repository. """ logger.debug("Get repo: %s/%s" % (namespace, repository)) include_tags = parsed_args["includeTags"] max_tags = 500 repo = model.get_repo(namespace, repository, get_authenticated_user(), include_tags, max_tags) if repo is None: raise NotFound() has_write_permission = ModifyRepositoryPermission( namespace, repository).can() has_write_permission = has_write_permission and repo.state == RepositoryState.NORMAL repo_data = repo.to_dict() repo_data["can_write"] = has_write_permission repo_data["can_admin"] = AdministerRepositoryPermission( namespace, repository).can() if parsed_args[ "includeStats"] and repo.repository_base_elements.kind_name != "application": stats = [] found_dates = {} for count in repo.counts: stats.append(count.to_dict()) found_dates["%s/%s" % (count.date.month, count.date.day)] = True # Fill in any missing stats with zeros. for day in range(1, MAX_DAYS_IN_3_MONTHS): day_date = datetime.now() - timedelta(days=day) key = "%s/%s" % (day_date.month, day_date.day) if key not in found_dates: stats.append({ "date": day_date.date().isoformat(), "count": 0, }) repo_data["stats"] = stats return repo_data
def _authorize_or_downscope_request(scope_param, has_valid_auth_context): # TODO: The complexity of this function is difficult to follow and maintain. Refactor/Cleanup. if len(scope_param) == 0: if not has_valid_auth_context: # In this case, we are doing an auth flow, and it's not an anonymous pull. logger.debug("No user and no token sent for empty scope list") raise Unauthorized() return None match = _get_scope_regex().match(scope_param) if match is None: logger.debug("Match: %s", match) logger.debug("len: %s", len(scope_param)) logger.warning("Unable to decode repository and actions: %s", scope_param) raise InvalidRequest("Unable to decode repository and actions: %s" % scope_param) logger.debug("Match: %s", match.groups()) registry_and_repo = match.group(1) namespace_and_repo = match.group(2) requested_actions = match.group(3).split(",") lib_namespace = app.config["LIBRARY_NAMESPACE"] namespace, reponame = parse_namespace_repository(namespace_and_repo, lib_namespace) # Ensure that we are never creating an invalid repository. if not REPOSITORY_NAME_REGEX.match(reponame): logger.debug("Found invalid repository name in auth flow: %s", reponame) if len(namespace_and_repo.split("/")) > 1: msg = "Nested repositories are not supported. Found: %s" % namespace_and_repo raise NameInvalid(message=msg) raise NameInvalid(message="Invalid repository name: %s" % namespace_and_repo) # Ensure the namespace is enabled. if registry_model.is_existing_disabled_namespace(namespace): msg = "Namespace %s has been disabled. Please contact a system administrator." % namespace raise NamespaceDisabled(message=msg) final_actions = [] repository_ref = registry_model.lookup_repository(namespace, reponame) repo_is_public = repository_ref is not None and repository_ref.is_public invalid_repo_message = "" if repository_ref is not None and repository_ref.kind != "image": invalid_repo_message = ( "This repository is for managing %s " + "and not container images.") % repository_ref.kind # Ensure the repository is not marked for deletion. if repository_ref is not None and repository_ref.state == RepositoryState.MARKED_FOR_DELETION: raise Unknown(message="Unknown repository") if "push" in requested_actions: # Check if there is a valid user or token, as otherwise the repository cannot be # accessed. if has_valid_auth_context: user = get_authenticated_user() # Lookup the repository. If it exists, make sure the entity has modify # permission. Otherwise, make sure the entity has create permission. if repository_ref: if ModifyRepositoryPermission(namespace, reponame).can(): if repository_ref is not None and repository_ref.kind != "image": raise Unsupported(message=invalid_repo_message) # Check for different repository states. if repository_ref.state == RepositoryState.NORMAL: # In NORMAL mode, if the user has permission, then they can push. final_actions.append("push") elif repository_ref.state == RepositoryState.MIRROR: # In MIRROR mode, only the mirroring robot can push. mirror = model.repo_mirror.get_mirror( repository_ref.id) robot = mirror.internal_robot if mirror is not None else None if robot is not None and user is not None and robot == user: assert robot.robot final_actions.append("push") else: logger.debug( "Repository %s/%s push requested for non-mirror robot %s: %s", namespace, reponame, robot, user, ) elif repository_ref.state == RepositoryState.READ_ONLY: # No pushing allowed in read-only state. pass else: logger.warning( "Unknown state for repository %s: %s", repository_ref, repository_ref.state, ) else: logger.debug("No permission to modify repository %s/%s", namespace, reponame) else: # TODO: Push-to-create functionality should be configurable if CreateRepositoryPermission( namespace).can() and user is not None: logger.debug("Creating repository: %s/%s", namespace, reponame) repository_ref = RepositoryReference.for_repo_obj( model.repository.create_repository( namespace, reponame, user)) final_actions.append("push") else: logger.debug("No permission to create repository %s/%s", namespace, reponame) if "pull" in requested_actions: # Grant pull if the user can read the repo or it is public. if ReadRepositoryPermission(namespace, reponame).can() or repo_is_public: if repository_ref is not None and repository_ref.kind != "image": raise Unsupported(message=invalid_repo_message) final_actions.append("pull") else: logger.debug("No permission to pull repository %s/%s", namespace, reponame) if "*" in requested_actions: # Grant * user is admin if AdministerRepositoryPermission(namespace, reponame).can(): if repository_ref is not None and repository_ref.kind != "image": raise Unsupported(message=invalid_repo_message) if repository_ref and repository_ref.state in ( RepositoryState.MIRROR, RepositoryState.READ_ONLY, ): logger.debug("No permission to administer repository %s/%s", namespace, reponame) else: assert repository_ref.state == RepositoryState.NORMAL final_actions.append("*") else: logger.debug("No permission to administer repository %s/%s", namespace, reponame) # Final sanity checks. if "push" in final_actions: assert repository_ref.state != RepositoryState.READ_ONLY if "*" in final_actions: assert repository_ref.state == RepositoryState.NORMAL return scopeResult( actions=final_actions, namespace=namespace, repository=reponame, registry_and_repo=registry_and_repo, tuf_root=_get_tuf_root(repository_ref, namespace, reponame), )