Beispiel #1
0
    def _authorize(self,
                   auth: tuple[str, str] | None = None,
                   force_renewal: bool = False) -> None:
        raw_token = model_cache.retrieve(self._cache_key(), lambda: None)
        if raw_token is not None and not force_renewal:
            token = raw_token["token"]
            if isinstance(token, bytes):
                token = token.decode("ascii")
            self._session.headers["Authorization"] = f"Bearer {token}"
            return

        if force_renewal:
            self._session.headers.pop("Authorization", None)

        # the /v2/ endpoint returns 401 when the client is not authorized.
        # if we get 200, there's no need to proceed.
        resp = self._safe_request(self._session.get, f"{self.base_url}/v2/")
        if resp.status_code == 200:
            return

        www_auth = parse_www_auth(resp.headers.get("www-authenticate", ""))
        scheme = www_auth.get("scheme")
        service = www_auth.get("service")
        realm = www_auth.get("realm")

        if scheme == "Basic" and auth is not None:
            # attach basic auth header to session
            requests.auth.HTTPBasicAuth(auth[0], auth[1])(self._session)
            return

        scope = f"repository:{self._repo}:pull"
        auth_url = f"{realm}?service={service}&scope={scope}"

        basic_auth = None
        if auth is not None:
            basic_auth = requests.auth.HTTPBasicAuth(auth[0], auth[1])

        resp = self._safe_request(self._session.get, auth_url, auth=basic_auth)
        if not resp.ok:
            raise UpstreamRegistryError(
                f"Failed to get token from: '{realm}', with status code: {resp.status_code}"
            )

        resp_json = resp.json()
        token = resp_json.get("token")

        # our cached token will expire a few seconds (TOKEN_RENEWAL_THRESHOLD)
        # before the actual token expiration.
        # we do this so that we can renew the token before actually hitting
        # any 401s, to save some http requests.
        expires_in = resp_json.get("expires_in", TOKEN_VALIDITY_LIFETIME_S)
        expires_in -= TOKEN_RENEWAL_THRESHOLD
        model_cache.retrieve(self._cache_key(expires_in),
                             lambda: {"token": token})
        self._session.headers["Authorization"] = f"{scheme} {token}"
Beispiel #2
0
    def list_applications(self,
                          namespace=None,
                          media_type=None,
                          search=None,
                          username=None,
                          with_channels=False):
        """
        Lists all repositories that contain applications, with optional filtering to a specific
        namespace and view a specific user.
        """
        limit = app.config.get("APP_REGISTRY_RESULTS_LIMIT", 50)
        namespace_whitelist = app.config.get(
            "APP_REGISTRY_PACKAGE_LIST_CACHE_WHITELIST", [])

        # NOTE: This caching only applies for the super-large and commonly requested results
        # sets.
        if (namespace is not None and namespace in namespace_whitelist
                and media_type is None and search is None and username is None
                and not with_channels):

            def _list_applications():
                return [
                    found._asdict()
                    for found in self._list_applications(namespace=namespace,
                                                         limit=limit)
                ]

            apps_cache_key = cache_key.for_appr_applications_list(
                namespace, limit)
            results = [
                CachedApplication(**found) for found in model_cache.retrieve(
                    apps_cache_key, _list_applications)
            ]
        else:
            results = self._list_applications(namespace,
                                              media_type,
                                              search,
                                              username,
                                              with_channels,
                                              limit=limit)

        return [
            ApplicationSummaryView(
                namespace=result.namespace,
                name=result.name,
                visibility=result.visibility,
                default=result.tag_names[0] if result.tag_names else None,
                channels=[
                    ChannelView(name=channel.name,
                                current=channel.linked_tag_name)
                    for channel in result.channels
                ] if result.channels is not None else None,
                manifests=result.manifests,
                releases=result.tag_names,
                updated_at=_timestamp_to_iso(result.latest_lifetime_start),
                created_at=_timestamp_to_iso(result.first_lifetime_start),
            ) for result in results
        ]
Beispiel #3
0
def show_package(namespace, package_name, release, media_type):
    def _retrieve_package():
        reponame = repo_name(namespace, package_name)
        return cnr_registry.show_package(
            reponame, release, media_type, channel_class=Channel, package_class=Package
        )

    namespace_whitelist = app.config.get("APP_REGISTRY_SHOW_PACKAGE_CACHE_WHITELIST", [])
    if not namespace or namespace not in namespace_whitelist:
        return jsonify(_retrieve_package())

    show_package_cache_key = cache_key.for_appr_show_package(
        namespace, package_name, release, media_type, model_cache.cache_config
    )

    result = model_cache.retrieve(show_package_cache_key, _retrieve_package)
    return jsonify(result)
Beispiel #4
0
def catalog_search(start_id, limit, pagination_callback):
    def _load_catalog():
        include_public = bool(features.PUBLIC_CATALOG)
        if not include_public and not get_authenticated_user():
            return []

        username = get_authenticated_user().username if get_authenticated_user(
        ) else None
        if username and not get_authenticated_user().enabled:
            return []

        query = model.repository.get_visible_repositories(
            username,
            kind_filter="image",
            include_public=include_public,
            start_id=start_id,
            limit=limit + 1,
        )
        # NOTE: The repository ID is in `rid` (not `id`) here, as per the requirements of
        # the `get_visible_repositories` call.
        return [
            Repository(repo.rid, repo.namespace_user.username,
                       repo.name)._asdict() for repo in query
        ]

    context_key = get_authenticated_context(
    ).unique_key if get_authenticated_context() else None
    catalog_cache_key = cache_key.for_catalog_page(context_key, start_id,
                                                   limit,
                                                   model_cache.cache_config)
    visible_repositories = [
        Repository(**repo_dict)
        for repo_dict in model_cache.retrieve(catalog_cache_key, _load_catalog)
    ]

    response = jsonify({
        "repositories": [
            "%s/%s" % (repo.namespace_name, repo.name)
            for repo in visible_repositories
        ][0:limit],
    })

    pagination_callback(visible_repositories, response)
    return response
Beispiel #5
0
    def list_applications(self,
                          namespace=None,
                          media_type=None,
                          search=None,
                          username=None,
                          with_channels=False):
        """
        Lists all repositories that contain applications, with optional filtering to a specific
        namespace and view a specific user.
        """
        limit = app.config.get("APP_REGISTRY_RESULTS_LIMIT", 50)
        namespace_whitelist = app.config.get(
            "APP_REGISTRY_PACKAGE_LIST_CACHE_WHITELIST", [])

        # NOTE: This caching only applies for the super-large and commonly requested results
        # sets.
        if (namespace is not None and namespace in namespace_whitelist
                and media_type is None and search is None and username is None
                and not with_channels):

            def _list_applications():
                return [
                    found._asdict()
                    for found in self._list_applications(namespace=namespace,
                                                         limit=limit)
                ]

            apps_cache_key = cache_key.for_appr_applications_list(
                namespace, limit)
            return [
                ApplicationSummaryView(**found) for found in
                model_cache.retrieve(apps_cache_key, _list_applications)
            ]
        else:
            return self._list_applications(namespace,
                                           media_type,
                                           search,
                                           username,
                                           with_channels,
                                           limit=limit)