Skip to content

Commit

Permalink
Change encode secret string resolver to mutation (#615)
Browse files Browse the repository at this point in the history
  • Loading branch information
rohitvinnakota-codecov committed Jun 19, 2024
1 parent 1787236 commit 8ab87fd
Show file tree
Hide file tree
Showing 14 changed files with 119 additions and 66 deletions.
12 changes: 10 additions & 2 deletions core/commands/repository/interactors/encode_secret_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@

class EncodeSecretStringInteractor(BaseInteractor):
@sync_to_async
def execute(self, owner: Owner, repo: Repository, value: str) -> str:
def execute(self, owner: Owner, repo_name: str, value: str) -> str:
if not self.current_user.is_authenticated:
raise Unauthenticated()

author = Owner.objects.filter(
username=owner.username, service=self.service
).first()
repo = (
Repository.objects.viewable_repos(self.current_owner)
.filter(author=author, name=repo_name)
.first()
)
if not repo:
raise ValidationError("Repo not found")

to_encode = "/".join(
(
owner.service,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,23 @@

class EncodeSecretStringInteractorTest(TransactionTestCase):
@async_to_sync
def execute(self, owner, repo, value):
return EncodeSecretStringInteractor(owner, "github").execute(owner, repo, value)
def execute(self, owner, repo_name, value):
return EncodeSecretStringInteractor(owner, "github").execute(
owner, repo_name, value
)

def test_encode_secret_string(self):
owner = OwnerFactory()
repo = RepositoryFactory(author=owner, name="repo-1")
res = self.execute(owner, repo=repo, value="token-1")
RepositoryFactory(author=owner, name="repo-1")
res = self.execute(owner, repo_name="repo-1", value="token-1")
check_encryptor = yaml_secret_encryptor
assert "token-1" in check_encryptor.decode(res[7:])

def test_validation_error_when_repo_not_found(self):
owner = OwnerFactory()
with pytest.raises(ValidationError):
self.execute(owner, repo=None, value="token-1")
self.execute(owner, repo_name=None, value="token-1")

def test_user_is_not_authenticated(self):
with pytest.raises(Unauthenticated) as e:
self.execute(None, repo=None, value="test")
with pytest.raises(Unauthenticated):
self.execute(None, repo_name=None, value="test")
4 changes: 2 additions & 2 deletions core/commands/repository/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def activate_measurements(
def erase_repository(self, repo_name: str, owner: Owner):
return self.get_interactor(EraseRepositoryInteractor).execute(repo_name, owner)

def encode_secret_string(self, owner: Owner, repo: Repository, value: str):
def encode_secret_string(self, owner: Owner, repo_name: str, value: str):
return self.get_interactor(EncodeSecretStringInteractor).execute(
owner, repo, value
owner, repo_name, value
)
44 changes: 44 additions & 0 deletions graphql_api/tests/mutation/test_encode_secret_string.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from django.test import TransactionTestCase
from shared.encryption.yaml_secret import yaml_secret_encryptor

from codecov_auth.tests.factories import OwnerFactory
from core.tests.factories import RepositoryFactory
from graphql_api.tests.helper import GraphQLTestHelper

query = """
mutation($input: EncodeSecretStringInput!) {
encodeSecretString(input: $input) {
value
error {
__typename
... on ResolverError {
message
}
}
}
}
"""


class TestEncodeSecretString(TransactionTestCase, GraphQLTestHelper):
def _request(self):
data = self.gql_request(
query,
owner=self.org,
variables={"input": {"repoName": "test-repo", "value": "token-1"}},
)
return data["encodeSecretString"]["value"]

def setUp(self):
self.org = OwnerFactory(username="test-org")
self.repo = RepositoryFactory(
name="test-repo",
author=self.org,
private=True,
)
self.owner = OwnerFactory(permission=[self.repo.pk])

def test_encoded_secret_string(self):
res = self._request()
check_encryptor = yaml_secret_encryptor
assert "token-1" in check_encryptor.decode(res[7:])
40 changes: 0 additions & 40 deletions graphql_api/tests/test_repository_encoded_secret_string.py

This file was deleted.

4 changes: 4 additions & 0 deletions graphql_api/types/inputs/encode_secret_string.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
input EncodeSecretStringInput {
repoName: String!
value: String!
}
2 changes: 2 additions & 0 deletions graphql_api/types/mutation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .delete_component_measurements import gql_delete_component_measurements
from .delete_flag import gql_delete_flag
from .delete_session import gql_delete_session
from .encode_secret_string import gql_encode_secret_string
from .erase_repository import gql_erase_repository
from .mutation import mutation_resolvers
from .onboard_user import gql_onboard_user
Expand Down Expand Up @@ -48,4 +49,5 @@
mutation = mutation + gql_update_repository
mutation = mutation + gql_update_self_hosted_settings
mutation = mutation + gql_regenerate_repository_upload_token
mutation = mutation + gql_encode_secret_string
mutation = mutation + gql_store_event_metrics
10 changes: 10 additions & 0 deletions graphql_api/types/mutation/encode_secret_string/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from graphql_api.helpers.ariadne import ariadne_load_local_graphql

from .encode_secret_string import (
error_encode_secret_string,
resolve_encode_secret_string,
)

gql_encode_secret_string = ariadne_load_local_graphql(
__file__, "encode_secret_string.graphql"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
union EncodeSecretStringError = ValidationError | UnauthenticatedError

type EncodeSecretStringPayload {
error: EncodeSecretStringError
value: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from ariadne import UnionType

from graphql_api.helpers.mutation import (
require_authenticated,
resolve_union_error_type,
wrap_error_handling_mutation,
)


@wrap_error_handling_mutation
@require_authenticated
async def resolve_encode_secret_string(_, info, input) -> None:
command = info.context["executor"].get_command("repository")
repo_name = input.get("repoName")
value = input.get("value")
current_owner = info.context["request"].current_owner
value = command.encode_secret_string(
repo_name=repo_name, owner=current_owner, value=value
)
return {"value": value}


error_encode_secret_string = UnionType("EraseRepositoryError")
error_encode_secret_string.type_resolver(resolve_union_error_type)
1 change: 1 addition & 0 deletions graphql_api/types/mutation/mutation.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ type Mutation {
updateRepository(input: UpdateRepositoryInput!): UpdateRepositoryPayload
updateSelfHostedSettings(input: UpdateSelfHostedSettingsInput!): UpdateSelfHostedSettingsPayload
regenerateRepositoryUploadToken(input: RegenerateRepositoryUploadTokenInput!): RegenerateRepositoryUploadTokenPayload
encodeSecretString(input: EncodeSecretStringInput!): EncodeSecretStringPayload
storeEventMetric(input: StoreEventMetricsInput!): StoreEventMetricsPayload
}
7 changes: 7 additions & 0 deletions graphql_api/types/mutation/mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
)
from .delete_flag import error_delete_flag, resolve_delete_flag
from .delete_session import error_delete_session, resolve_delete_session
from .encode_secret_string import (
error_encode_secret_string,
resolve_encode_secret_string,
)
from .erase_repository import error_erase_repository, resolve_erase_repository
from .onboard_user import error_onboard_user, resolve_onboard_user
from .regenerate_org_upload_token import (
Expand Down Expand Up @@ -84,6 +88,8 @@
mutation_bindable.field("regenerateRepositoryUploadToken")(
resolve_regenerate_repository_upload_token
)
mutation_bindable.field("encodeSecretString")(resolve_encode_secret_string)

mutation_bindable.field("storeEventMetric")(resolve_store_event_metrics)

mutation_resolvers = [
Expand All @@ -110,5 +116,6 @@
error_update_repository,
error_update_self_hosted_settings,
error_regenerate_repository_upload_token,
error_encode_secret_string,
error_store_event_metrics,
]
5 changes: 0 additions & 5 deletions graphql_api/types/repository/repository.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ type Repository {
orderingDirection: OrderingDirection
): [ComponentMeasurements!]!
componentsYaml(termId: String): [ComponentsYaml]!
encodedSecretString(value: String!): EncodedSecretString!
testAnalyticsEnabled: Boolean
}

Expand Down Expand Up @@ -118,8 +117,4 @@ type BranchEdge {
node: Branch!
}

type EncodedSecretString {
value: String!
}

union RepositoryResult = Repository | NotFoundError | OwnerNotActivatedError
10 changes: 0 additions & 10 deletions graphql_api/types/repository/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,13 +536,3 @@ def resolve_is_first_pull_request(repository: Repository, info) -> bool:
return not first_pr.compared_to

return False


@repository_bindable.field("encodedSecretString")
@sync_to_async
def resolve_encoded_secret_string(
repository: Repository, info: GraphQLResolveInfo, value: str
) -> dict[str, str]:
command = info.context["executor"].get_command("repository")
owner = info.context["request"].current_owner
return {"value": command.encode_secret_string(owner, repository, value)}

0 comments on commit 8ab87fd

Please sign in to comment.