Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit 52d870a

Browse files
[feat] Hide Codecov tokens from non admins if YAML setting is enabled (#923)
1 parent 4e9c42d commit 52d870a

File tree

6 files changed

+116
-1
lines changed

6 files changed

+116
-1
lines changed

codecov/settings_base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@
356356
"setup", "upload_throttling_enabled", default=True
357357
)
358358

359+
HIDE_ALL_CODECOV_TOKENS = get_config("setup", "hide_all_codecov_tokens", default=False)
360+
361+
359362
SENTRY_JWT_SHARED_SECRET = get_config(
360363
"sentry", "jwt_shared_secret", default=None
361364
) or get_config("setup", "sentry", "jwt_shared_secret", default=None)

graphql_api/tests/test_owner.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
UnauthorizedGuestAccess,
2828
)
2929
from codecov_auth.models import GithubAppInstallation, OwnerProfile
30+
from graphql_api.types.repository.repository import TOKEN_UNAVAILABLE
3031
from plan.constants import PlanName, TrialStatus
3132
from reports.tests.factories import CommitReportFactory, UploadFactory
3233

@@ -426,6 +427,28 @@ def test_get_org_upload_token(self, mocker):
426427
data = self.gql_request(query, owner=self.owner)
427428
assert data["owner"]["orgUploadToken"] == "upload_token"
428429

430+
@override_settings(HIDE_ALL_CODECOV_TOKENS=True)
431+
def test_get_org_upload_token_hide_tokens_setting_owner_not_admin(self):
432+
random_owner = OwnerFactory()
433+
query = """{
434+
owner(username: "%s") {
435+
orgUploadToken
436+
}
437+
}
438+
""" % (self.owner.username)
439+
random_owner.organizations = [self.owner.ownerid]
440+
random_owner.save()
441+
data = self.gql_request(query, owner=random_owner)
442+
assert data["owner"]["orgUploadToken"] == TOKEN_UNAVAILABLE
443+
444+
@patch("codecov_auth.commands.owner.owner.OwnerCommands.get_org_upload_token")
445+
@override_settings(HIDE_ALL_CODECOV_TOKENS=True)
446+
def test_get_org_upload_token_hide_tokens_setting_owner_is_admin(self, mocker):
447+
mocker.return_value = "upload_token"
448+
query = query_repositories % (self.owner.username, "", "")
449+
data = self.gql_request(query, owner=self.owner)
450+
assert data["owner"]["orgUploadToken"] == "upload_token"
451+
429452
# Applies for old users that didn't get their owner profiles created w/ their owner
430453
def test_when_owner_profile_doesnt_exist(self):
431454
owner = OwnerFactory(username="no-profile-user")

graphql_api/tests/test_repository.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
RepositoryTokenFactory,
1212
)
1313

14+
from graphql_api.types.repository.repository import TOKEN_UNAVAILABLE
1415
from services.profiling import CriticalFile
1516

1617
from .helper import GraphQLTestHelper
@@ -73,6 +74,11 @@
7374
"""
7475

7576

77+
def mock_get_config_global_upload_tokens(*args):
78+
if args == ("setup", "hide_all_codecov_tokens"):
79+
return True
80+
81+
7682
class TestFetchRepository(GraphQLTestHelper, TransactionTestCase):
7783
def fetch_repository(self, name, fields=None):
7884
data = self.gql_request(
@@ -683,6 +689,68 @@ def test_fetch_is_github_rate_limited_not_on_gh_service(self):
683689

684690
assert data["me"]["owner"]["repository"]["isGithubRateLimited"] == False
685691

692+
@override_settings(HIDE_ALL_CODECOV_TOKENS=True)
693+
def test_repo_upload_token_not_available_config_setting_owner_not_admin(self):
694+
owner = OwnerFactory(service="gitlab")
695+
696+
repo = RepositoryFactory(
697+
author=owner,
698+
author__service="gitlab",
699+
service_id=12345,
700+
active=True,
701+
)
702+
new_owner = OwnerFactory(service="gitlab", organizations=[owner.ownerid])
703+
new_owner.permission = [repo.repoid]
704+
new_owner.save()
705+
owner.admins = []
706+
707+
query = """
708+
query {
709+
owner(username: "%s") {
710+
repository(name: "%s") {
711+
... on Repository {
712+
uploadToken
713+
}
714+
}
715+
}
716+
}
717+
""" % (
718+
owner.username,
719+
repo.name,
720+
)
721+
722+
data = self.gql_request(
723+
query,
724+
owner=new_owner,
725+
variables={"name": repo.name},
726+
provider="gitlab",
727+
)
728+
729+
assert data["owner"]["repository"]["uploadToken"] == TOKEN_UNAVAILABLE
730+
731+
@override_settings(HIDE_ALL_CODECOV_TOKENS=True)
732+
def test_repo_upload_token_not_available_config_setting_owner_is_admin(self):
733+
owner = OwnerFactory(service="gitlab")
734+
repo = RepositoryFactory(
735+
author=owner,
736+
author__service="gitlab",
737+
service_id=12345,
738+
active=True,
739+
)
740+
owner.admins = [owner.ownerid]
741+
742+
data = self.gql_request(
743+
query_repository
744+
% """
745+
uploadToken
746+
""",
747+
owner=owner,
748+
variables={"name": repo.name},
749+
provider="gitlab",
750+
)
751+
752+
assert data["me"]["owner"]["repository"]["uploadToken"] != TOKEN_UNAVAILABLE
753+
686754
@patch("shared.rate_limits.determine_entity_redis_key")
687755
@patch("shared.rate_limits.determine_if_entity_is_rate_limited")
688756
@patch("logging.Logger.warning")

graphql_api/types/owner/owner.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import stripe
77
import yaml
88
from ariadne import ObjectType
9+
from django.conf import settings
910
from graphql import GraphQLResolveInfo
1011

1112
import services.activation as activation
@@ -37,6 +38,7 @@
3738
)
3839
from graphql_api.types.enums import OrderingDirection, RepositoryOrdering
3940
from graphql_api.types.errors.errors import NotFoundError, OwnerNotActivatedError
41+
from graphql_api.types.repository.repository import TOKEN_UNAVAILABLE
4042
from plan.constants import FREE_PLAN_REPRESENTATIONS, PlanData, PlanName
4143
from plan.service import PlanService
4244
from services.billing import BillingService
@@ -205,7 +207,13 @@ def resolve_hash_ownerid(owner: Owner, info: GraphQLResolveInfo) -> str:
205207
def resolve_org_upload_token(
206208
owner: Owner, info: GraphQLResolveInfo, **kwargs: Any
207209
) -> str:
210+
should_hide_tokens = settings.HIDE_ALL_CODECOV_TOKENS
211+
current_owner = info.context["request"].current_owner
208212
command = info.context["executor"].get_command("owner")
213+
is_owner_admin = current_owner.is_admin(owner)
214+
if should_hide_tokens and not is_owner_admin:
215+
return TOKEN_UNAVAILABLE
216+
209217
return command.get_org_upload_token(owner)
210218

211219

graphql_api/types/repository/repository.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import shared.rate_limits as rate_limits
66
import yaml
77
from ariadne import ObjectType, UnionType
8+
from django.conf import settings
89
from graphql.type.definition import GraphQLResolveInfo
910

1011
from codecov.db import sync_to_async
@@ -24,6 +25,8 @@
2425
from services.profiling import CriticalFile, ProfilingSummary
2526
from services.redis_configuration import get_redis_connection
2627

28+
TOKEN_UNAVAILABLE = "Token Unavailable. Please contact your admin."
29+
2730
log = logging.getLogger(__name__)
2831

2932
repository_bindable = ObjectType("Repository")
@@ -68,6 +71,13 @@ def resolve_commit(repository: Repository, info: GraphQLResolveInfo, id):
6871

6972
@repository_bindable.field("uploadToken")
7073
def resolve_upload_token(repository: Repository, info: GraphQLResolveInfo):
74+
should_hide_tokens = settings.HIDE_ALL_CODECOV_TOKENS
75+
76+
current_owner = info.context["request"].current_owner
77+
is_current_user_admin = current_owner.is_admin(repository.author)
78+
79+
if should_hide_tokens and not is_current_user_admin:
80+
return TOKEN_UNAVAILABLE
7181
command = info.context["executor"].get_command("repository")
7282
return command.get_upload_token(repository)
7383

requirements.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ freezegun==1.1.0
177177
# via -r requirements.in
178178
google-api-core[grpc]==2.11.1
179179
# via
180+
# google-api-core
180181
# google-cloud-core
181182
# google-cloud-pubsub
182183
# google-cloud-storage
@@ -408,7 +409,9 @@ requests==2.32.3
408409
# shared
409410
# stripe
410411
rfc3986[idna2008]==1.4.0
411-
# via httpx
412+
# via
413+
# httpx
414+
# rfc3986
412415
rsa==4.7.2
413416
# via google-auth
414417
s3transfer==0.5.0

0 commit comments

Comments
 (0)