Skip to content

Commit

Permalink
Update ActiveState code to use actor
Browse files Browse the repository at this point in the history
  • Loading branch information
th3coop committed Aug 16, 2023
1 parent e515534 commit e3e21e6
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 256 deletions.
23 changes: 10 additions & 13 deletions tests/common/db/oidc.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,25 +95,22 @@ class Meta:
model = ActiveStatePublisher

id = factory.Faker("uuid4", cast_to=None)
sub = factory.Faker("pystr", max_chars=12)
organization_id = factory.Faker("uuid4")
organization_url_name = factory.Faker("pystr", max_chars=12)
project_id = factory.Faker("uuid4")
activestate_project_name = factory.Faker("pystr", max_chars=12)
user_id = factory.Faker("uuid4")
branch_id = factory.Faker("uuid4")
organization = factory.Faker("pystr", max_chars=12)
project = factory.Faker("pystr", max_chars=12)
actor = factory.Faker("pystr", max_chars=12)
actor_id = factory.Faker("uuid4")
ingredient = factory.Faker("pystr", max_chars=12)


class PendingActiveStatePublisherFactory(WarehouseFactory):
class Meta:
model = PendingActiveStatePublisher

id = factory.Faker("uuid4", cast_to=None)
sub = factory.Faker("pystr", max_chars=12)
project_name = factory.Faker("pystr", max_chars=12)
organization_id = factory.Faker("uuid4")
organization_url_name = factory.Faker("pystr", max_chars=12)
project_id = factory.Faker("uuid4")
activestate_project_name = factory.Faker("pystr", max_chars=12)
user_id = factory.Faker("uuid4")
organization = factory.Faker("pystr", max_chars=12)
project = factory.Faker("pystr", max_chars=12)
actor = factory.Faker("pystr", max_chars=12)
actor_id = factory.Faker("uuid4")
added_by = factory.SubFactory(UserFactory)
ingredient = factory.Faker("pystr", max_chars=12)
206 changes: 92 additions & 114 deletions tests/unit/oidc/models/test_activestate.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,18 @@
PendingActiveStatePublisher,
)

ORG_ID = "00000000-0000-1000-8000-000000000000"
PROJECT_ID = "00000000-0000-1000-8000-000000000001"
USER_ID = "00000000-0000-1000-8000-000000000002"
BRANCH_ID = "00000000-0000-1000-8000-000000000003"
ORG_URL_NAME = "fakeorg"
PROJECT_NAME = "fakeproject"
ACTOR_ID = "00000000-0000-1000-8000-000000000002"
ACTOR = "fakeuser"
INGREDIENT = "fakeingredientname"
# This follows the format of the subject that ActiveState sends us. We don't
# validate the format when verifying the JWT. That should happen when the
# Publisher is configured. We just need to make sure that the subject matches
#
# Technically, the branch should only be present if a branch was provided in the JWT
# claims
SUBJECT = "org:fake_org_id:project:fake_project_id:branch_id:fake_branch_id"
SUBJECT = f"org:{ORG_URL_NAME}:project:{PROJECT_NAME}"


def test_lookup_strategies():
Expand All @@ -48,27 +49,30 @@ def test_lookup_strategies():

def new_signed_claims(
sub: str = SUBJECT,
organization_id: str = ORG_ID,
org_url_name: str = "fakeorg",
project_id: str = PROJECT_ID,
project_name: str = "fakeproject",
actor: str = ACTOR,
actor_id: str = ACTOR_ID,
ingredient: str = INGREDIENT,
organization: str = ORG_URL_NAME,
org_id: str = "fakeorgid",
project: str = PROJECT_NAME,
project_id: str = "fakeprojectid",
project_path: str = "fakeorg/fakeproject",
user_id: str = USER_ID,
project_visibility: str = "public",
branch_id: str | None = None,
) -> SignedClaims:
project_name = "fakeproject"
org_url_name = "fakeorg"
claims = SignedClaims(
{
"sub": sub,
"organization_id": organization_id,
"organization_url_name": org_url_name,
"actor": actor,
"actor_id": actor_id,
"ingredient": ingredient,
"organization_id": org_id,
"organization": organization,
"project_visibility": project_visibility,
"project_id": project_id,
"project_name": project_name,
"project_path": project_path,
"user_id": user_id,
"project_visibility": project_visibility,
"project": project,
"builder": "pypi-publisher",
}
)
if branch_id:
Expand All @@ -85,9 +89,7 @@ def test_publisher_name(self):
def test_publisher_url(self):
org_name = "fakeorg"
project_name = "fakeproject"
publisher = ActiveStatePublisher(
organization_url_name=org_name, activestate_project_name=project_name
)
publisher = ActiveStatePublisher(organization=org_name, project=project_name)

assert (
publisher.publisher_url()
Expand All @@ -97,9 +99,7 @@ def test_publisher_url(self):
def test_stringifies_as_project_url(self):
org_name = "fakeorg"
project_name = "fakeproject"
publisher = ActiveStatePublisher(
organization_url_name=org_name, activestate_project_name=project_name
)
publisher = ActiveStatePublisher(organization=org_name, project=project_name)

assert (
str(publisher)
Expand All @@ -109,9 +109,11 @@ def test_stringifies_as_project_url(self):
def test_activestate_publisher_all_known_claims(self):
assert ActiveStatePublisher.all_known_claims() == {
# verifiable claims
"organization_id",
"project_id",
"user_id",
"organization",
"project",
"actor_id",
"actor",
"builder",
"sub",
# optional verifiable claims
"branch_id",
Expand All @@ -123,15 +125,14 @@ def test_activestate_publisher_all_known_claims(self):
"aud",
# unchecked claims
"project_visibility",
"project_name",
"project_path",
"organization_url_name",
"ingredient",
"organization_id",
"project_id",
}

def test_activestate_publisher_unaccounted_claims(self, monkeypatch):
publisher = ActiveStatePublisher(
sub=SUBJECT,
)
publisher = ActiveStatePublisher()

scope = pretend.stub()
sentry_sdk = pretend.stub(
Expand Down Expand Up @@ -161,24 +162,28 @@ def test_activestate_publisher_unaccounted_claims(self, monkeypatch):
@pytest.mark.parametrize(
("claim_to_drop", "valid"),
[
("organization_id", False),
("project_id", False),
("user_id", False),
("branch_id", True),
("organization_url_name", True),
("organization", False),
("project", False),
("actor_id", False),
("actor", False),
("builder", False),
("ingredient", True),
("organization_id", True),
("project_id", True),
("project_visibility", True),
("project_name", True),
("project_path", True),
("branch_id", True),
],
)
def test_activestate_publisher_missing_claims(
self, monkeypatch, claim_to_drop: str, valid: bool
):
publisher = ActiveStatePublisher(
sub=SUBJECT,
organization_id=ORG_ID,
project_id=PROJECT_ID,
user_id=USER_ID,
organization=ORG_URL_NAME,
project=PROJECT_NAME,
actor_id=ACTOR_ID,
actor=ACTOR,
ingredient=INGREDIENT,
)

scope = pretend.stub()
Expand Down Expand Up @@ -211,128 +216,99 @@ def test_activestate_publisher_missing_claims(
@pytest.mark.parametrize(
("expect", "actual", "valid"),
[
(ORG_ID, ORG_ID, True),
(ORG_ID, PROJECT_ID, False),
(ORG_URL_NAME, ORG_URL_NAME, True),
(ORG_URL_NAME, PROJECT_NAME, False),
],
)
def test_activestate_publisher_org_id_verified(
self, expect: str, actual: str, valid: bool
):
publisher = ActiveStatePublisher(
sub=SUBJECT,
organization_id=actual,
project_id=PROJECT_ID,
user_id=USER_ID,
organization=actual,
project=PROJECT_NAME,
actor_id=ACTOR_ID,
actor=ACTOR,
ingredient=INGREDIENT,
)

signed_claims = new_signed_claims(organization_id=expect)
signed_claims = new_signed_claims(organization=expect)
assert publisher.verify_claims(signed_claims=signed_claims) is valid

@pytest.mark.parametrize(
("expect", "actual", "valid"),
[
(BRANCH_ID, BRANCH_ID, True),
(BRANCH_ID, PROJECT_ID, False),
# If it's configured in the publisher, it must be present in the claim
(BRANCH_ID, None, False),
# If it's not configured in the publisher, we don't care what it is
# in the claim
(None, None, True),
(None, PROJECT_ID, True),
],
)
def test_activestate_publisher_branch_id_verified(
self, expect: str, actual: str, valid: bool
):
publisher = ActiveStatePublisher(
sub=SUBJECT,
organization_id=ORG_ID,
project_id=PROJECT_ID,
user_id=USER_ID,
branch_id=expect,
)

signed_claims = new_signed_claims(branch_id=actual)
assert publisher.verify_claims(signed_claims=signed_claims) is valid

@pytest.mark.parametrize(
("expect", "actual", "valid"),
[
(PROJECT_ID, PROJECT_ID, True),
(PROJECT_ID, ORG_ID, False),
(PROJECT_NAME, PROJECT_NAME, True),
(PROJECT_NAME, ORG_URL_NAME, False),
],
)
def test_activestate_publisher_project_id_verified(
self, expect: str, actual: str, valid: bool
):
publisher = ActiveStatePublisher(
sub=SUBJECT,
organization_id=ORG_ID,
project_id=actual,
user_id=USER_ID,
organization=ORG_URL_NAME,
project=actual,
actor_id=ACTOR_ID,
actor=ACTOR,
ingredient=INGREDIENT,
)

signed_claims = new_signed_claims(project_id=expect)
signed_claims = new_signed_claims(project=expect)
assert publisher.verify_claims(signed_claims=signed_claims) is valid

@pytest.mark.parametrize(
("expect", "actual", "valid"),
[
(USER_ID, USER_ID, True),
(USER_ID, ORG_ID, False),
(ACTOR_ID, ACTOR_ID, True),
(ACTOR_ID, ORG_URL_NAME, False),
],
)
def test_activestate_publisher_user_id_verified(
self, expect: str, actual: str, valid: bool
):
publisher = ActiveStatePublisher(
sub=SUBJECT,
organization_id=ORG_ID,
project_id=PROJECT_ID,
user_id=actual,
organization=ORG_URL_NAME,
project=PROJECT_NAME,
actor_id=actual,
actor=ACTOR,
ingredient=INGREDIENT,
)

signed_claims = new_signed_claims(user_id=expect)
signed_claims = new_signed_claims(actor_id=expect)
assert publisher.verify_claims(signed_claims=signed_claims) is valid

@pytest.mark.parametrize(
("expected", "actual", "valid"),
[
# Both present: must match.
(
f"org:{ORG_ID}:project:{PROJECT_ID}",
f"org:{ORG_ID}:project:{PROJECT_ID}",
True,
),
# Both present, with branch id: must match.
(
f"org:{ORG_ID}:project:{PROJECT_ID}:branch_id:{BRANCH_ID}",
f"org:{ORG_ID}:project:{PROJECT_ID}:branch_id:{BRANCH_ID}",
f"org:{ORG_URL_NAME}:project:{PROJECT_NAME}",
f"org:{ORG_URL_NAME}:project:{PROJECT_NAME}",
True,
),
# sub configured without branch id, claim has branch id: must fail.
# Wrong value, project, must fail.
(
f"org:{ORG_ID}:project:{PROJECT_ID}",
f"org:{ORG_ID}:project:{PROJECT_ID}:branch_id:{BRANCH_ID}",
f"org:{ORG_URL_NAME}:project:{PROJECT_NAME}",
f"org:{ORG_URL_NAME}:project:{ORG_URL_NAME}",
False,
),
# sub configured with branch id, claim missing branch id: must fail.
# Wrong value, org_id, must fail.
(
f"org:{ORG_ID}:project:{PROJECT_ID}:branch_id:{BRANCH_ID}",
f"org:{ORG_ID}:project:{PROJECT_ID}",
f"org:{ORG_URL_NAME}:project:{PROJECT_NAME}",
f"org:{PROJECT_NAME}:project:{PROJECT_NAME}",
False,
),
# Wrong format for sub to expect from ActiveState: must fail.
# Just nonsenes, must fail.
(
f"org:{ORG_ID}:project:{PROJECT_ID}",
f"org:{ORG_ID}:project:{ORG_ID}",
f"org:{ORG_URL_NAME}:project:{PROJECT_NAME}",
"Nonsense",
False,
),
],
)
def test_activestate_publisher_sub(self, expected: str, actual: str, valid: bool):
check = ActiveStatePublisher.__required_verifiable_claims__["sub"]
assert check(expected, actual, pretend.stub()) is valid
signed_claims = new_signed_claims(sub=actual)
assert check(expected, actual, signed_claims) is valid


class TestPendingActiveStatePublisher:
Expand All @@ -343,8 +319,10 @@ def test_reify_does_not_exist_yet(self, db_request):
assert (
db_request.db.query(ActiveStatePublisher)
.filter_by(
organization_id=pending_publisher.organization_id,
sub=pending_publisher.sub,
organization=pending_publisher.organization,
project=pending_publisher.project,
actor_id=pending_publisher.actor_id,
actor=pending_publisher.actor,
)
.one_or_none()
is None
Expand All @@ -353,16 +331,16 @@ def test_reify_does_not_exist_yet(self, db_request):

assert isinstance(publisher, ActiveStatePublisher)
assert pending_publisher in db_request.db.deleted
assert publisher.organization_id == pending_publisher.organization_id
assert publisher.organization == pending_publisher.organization
assert publisher.sub == pending_publisher.sub

def test_reify_already_exists(self, db_request):
existing_publisher: ActiveStatePublisher = ActiveStatePublisherFactory.create()
pending_publisher = PendingActiveStatePublisherFactory.create(
organization_id=existing_publisher.organization_id,
project_id=existing_publisher.project_id,
branch_id=existing_publisher.branch_id,
sub=existing_publisher.sub,
organization=existing_publisher.organization,
project=existing_publisher.project,
actor_id=existing_publisher.actor_id,
actor=existing_publisher.actor,
)
publisher = pending_publisher.reify(db_request.db)

Expand Down
Loading

0 comments on commit e3e21e6

Please sign in to comment.