Пример #1
0
def test_generate_registry_jwt(scope, username, password, expected_code,
                               expected_scopes, app, client):
    params = {
        "service": original_app.config["SERVER_HOSTNAME"],
        "scope": scope,
    }

    if callable(password):
        password = password(username)

    headers = {}
    if username and password:
        headers["Authorization"] = "Basic %s" % (base64.b64encode(
            "%s:%s" % (username, password)))

    resp = conduct_call(
        client,
        "v2.generate_registry_jwt",
        url_for,
        "GET",
        params,
        {},
        expected_code,
        headers=headers,
    )
    if expected_code != 200:
        return

    token = resp.json["token"]
    decoded = decode_bearer_token(token, instance_keys, original_app.config)
    assert decoded["iss"] == "quay"
    assert decoded["aud"] == original_app.config["SERVER_HOSTNAME"]
    assert decoded["sub"] == username if username else "(anonymous)"

    expected_access = []
    for scope in expected_scopes:
        name, actions_str = scope.split(":")
        actions = actions_str.split(",") if actions_str else []

        expected_access.append({
            "type": "repository",
            "name": name,
            "actions": actions,
        })

    assert decoded["access"] == expected_access
    assert len(decoded["context"][CLAIM_TUF_ROOTS]) == len(expected_scopes)
Пример #2
0
def test_generate_registry_jwt(scope, username, password, expected_code,
                               expected_scopes, app, client):
    params = {
        'service': original_app.config['SERVER_HOSTNAME'],
        'scope': scope,
    }

    if callable(password):
        password = password(username)

    headers = {}
    if username and password:
        headers['Authorization'] = 'Basic %s' % (base64.b64encode(
            '%s:%s' % (username, password)))

    resp = conduct_call(client,
                        'v2.generate_registry_jwt',
                        url_for,
                        'GET',
                        params, {},
                        expected_code,
                        headers=headers)
    if expected_code != 200:
        return

    token = resp.json['token']
    decoded = decode_bearer_token(token, instance_keys, original_app.config)
    assert decoded['iss'] == 'quay'
    assert decoded['aud'] == original_app.config['SERVER_HOSTNAME']
    assert decoded['sub'] == username if username else '(anonymous)'

    expected_access = []
    for scope in expected_scopes:
        name, actions_str = scope.split(':')
        actions = actions_str.split(',') if actions_str else []

        expected_access.append({
            'type': 'repository',
            'name': name,
            'actions': actions,
        })

    assert decoded['access'] == expected_access
    assert len(decoded['context'][CLAIM_TUF_ROOTS]) == len(expected_scopes)
Пример #3
0
def test_generate_registry_jwt(
    scope,
    username,
    password,
    expected_code,
    expected_scopes,
    push_private,
    visibility,
    org_create,
    app,
    client,
):
    params = {
        "service": original_app.config["SERVER_HOSTNAME"],
        "scope": scope,
    }

    if callable(password):
        password = password(username)

    headers = {}
    if username and password:
        headers["Authorization"] = gen_basic_auth(username, password)

    original_app.config["CREATE_PRIVATE_REPO_ON_PUSH"] = push_private
    original_app.config["CREATE_NAMESPACE_ON_PUSH"] = org_create

    resp = conduct_call(
        client,
        "v2.generate_registry_jwt",
        url_for,
        "GET",
        params,
        {},
        expected_code,
        headers=headers,
    )
    if expected_code != 200:
        return

    token = resp.json["token"]
    decoded = decode_bearer_token(token, instance_keys, original_app.config)
    assert decoded["iss"] == "quay"
    assert decoded["aud"] == original_app.config["SERVER_HOSTNAME"]
    assert decoded["sub"] == username if username else "(anonymous)"

    expected_access = []
    for scope in expected_scopes:
        name, actions_str = scope.split(":")
        actions = actions_str.split(",") if actions_str else []

        expected_access.append({
            "type": "repository",
            "name": name,
            "actions": actions,
        })

    assert decoded["access"] == expected_access
    assert len(decoded["context"][CLAIM_TUF_ROOTS]) == len(expected_scopes)

    # Test visibility
    if scope == "repository:devtable/visibility:pull,push,*":
        assert (model.repository.get_repository(
            "devtable", "visibility").visibility.name == visibility)
Пример #4
0
    def _validate_proxy_url(self):
        original_uri = request.headers.get("X-Original-URI", None)
        if not original_uri:
            logger.error("Missing original URI: %s", request.headers)
            abort(401)

        if not original_uri.startswith("/_storage_proxy/"):
            logger.error("Unknown storage proxy path: %s", original_uri)
            abort(401)

        # The proxy path is of the form:
        # /_storage_proxy/{token}/{scheme}/{hostname}/rest/of/path/here
        without_prefix = original_uri[len("/_storage_proxy/"):]
        parts = without_prefix.split("/", 3)
        if len(parts) != 4:
            logger.error("Invalid storage proxy path (found %s parts): %s",
                         len(parts), without_prefix)
            abort(401)

        encoded_token, scheme, host, uri = parts

        try:
            token = base64.urlsafe_b64decode(str(encoded_token))
        except ValueError:
            logger.exception("Could not decode proxy token")
            abort(401)
        except TypeError:
            logger.exception("Could not decode proxy token")
            abort(401)

        logger.debug(
            "Got token %s for storage proxy auth request %s with parts %s",
            token,
            original_uri,
            parts,
        )

        # Decode the bearer token.
        try:
            decoded = decode_bearer_token(token, self.instance_keys,
                                          self.app.config)
        except InvalidBearerTokenException:
            logger.exception("Invalid token for storage proxy")
            abort(401)

        # Ensure it is for the proxy.
        if decoded["sub"] != STORAGE_PROXY_SUBJECT:
            logger.exception("Invalid subject %s for storage proxy auth",
                             decoded["subject"])
            abort(401)

        # Validate that the access matches the token format.
        access = decoded.get("access", {})
        try:
            validate(access, ACCESS_SCHEMA)
        except ValidationError:
            logger.exception(
                "We should not be minting invalid credentials: %s", access)
            abort(401)

        # For now, we only expect a single access credential.
        if len(access) != 1:
            logger.exception(
                "We should not be minting invalid credentials: %s", access)
            abort(401)

        # Ensure the signed access matches the requested URL's pieces.
        granted_access = access[0]
        if granted_access["scheme"] != scheme:
            logger.exception("Mismatch in scheme. %s expected, %s found",
                             granted_access["scheme"], scheme)
            abort(401)

        if granted_access["host"] != host:
            logger.exception("Mismatch in host. %s expected, %s found",
                             granted_access["host"], host)
            abort(401)

        if granted_access["uri"] != uri:
            logger.exception("Mismatch in uri. %s expected, %s found",
                             granted_access["uri"], uri)
            abort(401)

        return "OK"