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}"
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 ]
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)
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
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)