From 24784cebf6e45c3c7f79c38c146c2c47aaf233c8 Mon Sep 17 00:00:00 2001 From: Suejung Shin Date: Tue, 31 Dec 2024 10:53:46 -0800 Subject: [PATCH 1/4] fix: Restrict deactivated enterprise user access (#910) --- graphql_api/tests/test_owner.py | 23 ++++++++++++++++++++++- graphql_api/types/query/query.py | 18 ++++++++++++------ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/graphql_api/tests/test_owner.py b/graphql_api/tests/test_owner.py index 0676e23b27..25a5b3ae23 100644 --- a/graphql_api/tests/test_owner.py +++ b/graphql_api/tests/test_owner.py @@ -785,6 +785,27 @@ def test_fetch_owner_on_unauthenticated_enteprise_guest_access(self): assert e.message == UnauthorizedGuestAccess.message assert e.extensions["code"] == UnauthorizedGuestAccess.code + @override_settings(IS_ENTERPRISE=True, GUEST_ACCESS=False) + def test_fetch_owner_on_unauthenticated_enteprise_guest_access_not_activated(self): + user = OwnerFactory(username="sample-user") + owner = OwnerFactory(username="sample-owner", plan_activated_users=[123, 456]) + user.organizations = [owner.ownerid] + user.save() + owner.save() + query = """{ + owner(username: "%s") { + isCurrentUserActivated + } + } + """ % (owner.username) + + try: + self.gql_request(query, owner=user) + + except GraphQLError as e: + assert e.message == UnauthorizedGuestAccess.message + assert e.extensions["code"] == UnauthorizedGuestAccess.code + def test_fetch_current_user_is_okta_authenticated(self): account = AccountFactory() owner = OwnerFactory(username="sample-owner", service="github", account=account) @@ -843,7 +864,7 @@ def test_fetch_current_user_is_not_okta_authenticated_no_account(self): @patch("shared.rate_limits.determine_entity_redis_key") @patch("shared.rate_limits.determine_if_entity_is_rate_limited") - @override_settings(IS_ENTERPRISE=True, GUEST_ACCESS=False) + @override_settings(IS_ENTERPRISE=True, GUEST_ACCESS=True) def test_fetch_is_github_rate_limited( self, mock_determine_rate_limit, mock_determine_redis_key ): diff --git a/graphql_api/types/query/query.py b/graphql_api/types/query/query.py index 1d216def11..e193dc4ca8 100644 --- a/graphql_api/types/query/query.py +++ b/graphql_api/types/query/query.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Any, Optional from ariadne import ObjectType from django.conf import settings @@ -20,7 +20,7 @@ def query_name(info: GraphQLResolveInfo) -> Optional[str]: return info.operation.name.value -def configure_sentry_scope(query_name: str): +def configure_sentry_scope(query_name: Optional[str]) -> None: # this sets the Sentry transaction name to the GraphQL query name which # should make it easier to search/filter transactions # we're configuring this here since it's the main entrypoint into GraphQL resolvers @@ -33,14 +33,16 @@ def configure_sentry_scope(query_name: str): @query_bindable.field("me") @sync_to_async -def resolve_me(_, info) -> Optional[Owner]: +def resolve_me(_: Any, info: GraphQLResolveInfo) -> Optional[Owner]: configure_sentry_scope(query_name(info)) # will be `None` for anonymous users or users w/ no linked owners return info.context["request"].current_owner @query_bindable.field("owner") -def resolve_owner(_, info, username): +async def resolve_owner( + _: Any, info: GraphQLResolveInfo, username: str +) -> Optional[Owner]: configure_sentry_scope(query_name(info)) service = info.context["service"] @@ -50,11 +52,15 @@ def resolve_owner(_, info, username): if not user or not user.is_authenticated: raise UnauthorizedGuestAccess() - return get_owner(service, username) + target = await get_owner(service, username) + if user.ownerid not in target.plan_activated_users: + raise UnauthorizedGuestAccess() + + return await get_owner(service, username) @query_bindable.field("config") -def resolve_config(_, info): +def resolve_config(_: Any, info: GraphQLResolveInfo) -> object: configure_sentry_scope(query_name(info)) # we have to return something here just to allow access to the child resolvers From 31641975ddfae325203f3109a527e163e2ac4f9d Mon Sep 17 00:00:00 2001 From: trent-codecov Date: Wed, 1 Jan 2025 12:26:48 -0500 Subject: [PATCH 2/4] Update config for gunicorn --- prod.sh | 12 ++++++++---- staging.sh | 15 +++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/prod.sh b/prod.sh index 575b65f5ae..1dee61539e 100755 --- a/prod.sh +++ b/prod.sh @@ -7,8 +7,12 @@ if [ -f "/usr/local/bin/berglas" ]; then prefix="berglas exec --" fi -export PROMETHEUS_MULTIPROC_DIR="${PROMETHEUS_MULTIPROC_DIR:-$HOME/.prometheus}" -rm -r ${PROMETHEUS_MULTIPROC_DIR?}/* 2> /dev/null -mkdir -p "$PROMETHEUS_MULTIPROC_DIR" +GUNICORN_WORKERS=${GUNICORN_WORKERS:-2} +if [ "$GUNICORN_WORKERS" -gt 1 ]; +then + export PROMETHEUS_MULTIPROC_DIR="${PROMETHEUS_MULTIPROC_DIR:-$HOME/.prometheus}" + rm -r ${PROMETHEUS_MULTIPROC_DIR?}/* 2> /dev/null + mkdir -p "$PROMETHEUS_MULTIPROC_DIR" +fi -$prefix gunicorn codecov.wsgi:application --workers=2 --bind 0.0.0.0:8000 --access-logfile '-' --statsd-host ${STATSD_HOST}:${STATSD_PORT} --timeout "${GUNICORN_TIMEOUT:-600}" +$prefix gunicorn codecov.wsgi:application --workers=${GUNICORN_WORKERS} --bind 0.0.0.0:8000 --access-logfile '-' --statsd-host ${STATSD_HOST}:${STATSD_PORT} --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --max-requests=50000 --max-requests-jitter=300 diff --git a/staging.sh b/staging.sh index 1b68b944a0..4e70c984d0 100644 --- a/staging.sh +++ b/staging.sh @@ -8,13 +8,12 @@ prefix="" if [ -f "/usr/local/bin/berglas" ]; then prefix="berglas exec --" fi -suffix="" -if [[ "$STATSD_HOST" ]]; then - suffix="--statsd-host ${STATSD_HOST}:${STATSD_PORT}" -fi -export PROMETHEUS_MULTIPROC_DIR="${PROMETHEUS_MULTIPROC_DIR:-$HOME/.prometheus}" -rm -r ${PROMETHEUS_MULTIPROC_DIR?}/* 2> /dev/null -mkdir -p "$PROMETHEUS_MULTIPROC_DIR" +if [ "$GUNICORN_WORKERS" -gt 1 ]; +then + export PROMETHEUS_MULTIPROC_DIR="${PROMETHEUS_MULTIPROC_DIR:-$HOME/.prometheus}" + rm -r ${PROMETHEUS_MULTIPROC_DIR?}/* 2> /dev/null + mkdir -p "$PROMETHEUS_MULTIPROC_DIR" +fi -$prefix gunicorn codecov.wsgi:application --reload --workers=2 --bind 0.0.0.0:8000 --access-logfile '-' --timeout "${GUNICORN_TIMEOUT:-600}" $suffix +$prefix gunicorn codecov.wsgi:application --reload --workers=${GUNICORN_WORKERS:-2} --bind 0.0.0.0:8000 --access-logfile '-' --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --config=gunicorn.conf.py From 9962acc56c42539c885dda3dd759afbb55519acc Mon Sep 17 00:00:00 2001 From: trent-codecov Date: Wed, 1 Jan 2025 12:30:24 -0500 Subject: [PATCH 3/4] Update config for gunicorn --- enterprise.sh | 2 +- prod.sh | 2 +- staging.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/enterprise.sh b/enterprise.sh index d0a29bce42..22b1c8659f 100755 --- a/enterprise.sh +++ b/enterprise.sh @@ -31,7 +31,7 @@ then python manage.py migrate python manage.py migrate --database "timeseries" timeseries # Start api - ${SUB}$prefix gunicorn codecov.wsgi:application --workers=$GUNICORN_WORKERS --bind ${CODECOV_API_BIND:-0.0.0.0}:${CODECOV_API_PORT:-8000} --access-logfile '-' ${statsd}--timeout "${GUNICORN_TIMEOUT:-600}"${POST} + ${SUB}$prefix gunicorn codecov.wsgi:application --workers=$GUNICORN_WORKERS --threads=${GUNICORN_THREADS:-1} --bind ${CODECOV_API_BIND:-0.0.0.0}:${CODECOV_API_PORT:-8000} --access-logfile '-' ${statsd}--timeout "${GUNICORN_TIMEOUT:-600}"${POST} elif [[ "$1" = "rti" ]]; then # Start api diff --git a/prod.sh b/prod.sh index 1dee61539e..63c6d478ee 100755 --- a/prod.sh +++ b/prod.sh @@ -15,4 +15,4 @@ then mkdir -p "$PROMETHEUS_MULTIPROC_DIR" fi -$prefix gunicorn codecov.wsgi:application --workers=${GUNICORN_WORKERS} --bind 0.0.0.0:8000 --access-logfile '-' --statsd-host ${STATSD_HOST}:${STATSD_PORT} --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --max-requests=50000 --max-requests-jitter=300 +$prefix gunicorn codecov.wsgi:application --workers=${GUNICORN_WORKERS} --threads=${GUNICORN_THREADS:-1} --bind 0.0.0.0:8000 --access-logfile '-' --statsd-host ${STATSD_HOST}:${STATSD_PORT} --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --max-requests=50000 --max-requests-jitter=300 diff --git a/staging.sh b/staging.sh index 4e70c984d0..7448958889 100644 --- a/staging.sh +++ b/staging.sh @@ -16,4 +16,4 @@ then mkdir -p "$PROMETHEUS_MULTIPROC_DIR" fi -$prefix gunicorn codecov.wsgi:application --reload --workers=${GUNICORN_WORKERS:-2} --bind 0.0.0.0:8000 --access-logfile '-' --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --config=gunicorn.conf.py +$prefix gunicorn codecov.wsgi:application --reload --workers=${GUNICORN_WORKERS:-2} --threads=${GUNICORN_THREADS:-1} --bind 0.0.0.0:8000 --access-logfile '-' --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --config=gunicorn.conf.py From c672e4baef3c6283db765d9c2a07eb8cee82fdcf Mon Sep 17 00:00:00 2001 From: trent-codecov Date: Wed, 1 Jan 2025 12:31:31 -0500 Subject: [PATCH 4/4] Update config for gunicorn --- enterprise.sh | 2 +- prod.sh | 2 +- staging.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/enterprise.sh b/enterprise.sh index 22b1c8659f..c3e78fa813 100755 --- a/enterprise.sh +++ b/enterprise.sh @@ -31,7 +31,7 @@ then python manage.py migrate python manage.py migrate --database "timeseries" timeseries # Start api - ${SUB}$prefix gunicorn codecov.wsgi:application --workers=$GUNICORN_WORKERS --threads=${GUNICORN_THREADS:-1} --bind ${CODECOV_API_BIND:-0.0.0.0}:${CODECOV_API_PORT:-8000} --access-logfile '-' ${statsd}--timeout "${GUNICORN_TIMEOUT:-600}"${POST} + ${SUB}$prefix gunicorn codecov.wsgi:application --workers=$GUNICORN_WORKERS --threads=${GUNICORN_THREADS:-1} --worker-connections=${GUNICORN_WORKER_CONNECTIONS:-1000} --bind ${CODECOV_API_BIND:-0.0.0.0}:${CODECOV_API_PORT:-8000} --access-logfile '-' ${statsd}--timeout "${GUNICORN_TIMEOUT:-600}"${POST} elif [[ "$1" = "rti" ]]; then # Start api diff --git a/prod.sh b/prod.sh index 63c6d478ee..f87719d145 100755 --- a/prod.sh +++ b/prod.sh @@ -15,4 +15,4 @@ then mkdir -p "$PROMETHEUS_MULTIPROC_DIR" fi -$prefix gunicorn codecov.wsgi:application --workers=${GUNICORN_WORKERS} --threads=${GUNICORN_THREADS:-1} --bind 0.0.0.0:8000 --access-logfile '-' --statsd-host ${STATSD_HOST}:${STATSD_PORT} --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --max-requests=50000 --max-requests-jitter=300 +$prefix gunicorn codecov.wsgi:application --workers=${GUNICORN_WORKERS} --threads=${GUNICORN_THREADS:-1} --worker-connections=${GUNICORN_WORKER_CONNECTIONS:-1000} --bind 0.0.0.0:8000 --access-logfile '-' --statsd-host ${STATSD_HOST}:${STATSD_PORT} --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --max-requests=50000 --max-requests-jitter=300 diff --git a/staging.sh b/staging.sh index 7448958889..cef17032e6 100644 --- a/staging.sh +++ b/staging.sh @@ -16,4 +16,4 @@ then mkdir -p "$PROMETHEUS_MULTIPROC_DIR" fi -$prefix gunicorn codecov.wsgi:application --reload --workers=${GUNICORN_WORKERS:-2} --threads=${GUNICORN_THREADS:-1} --bind 0.0.0.0:8000 --access-logfile '-' --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --config=gunicorn.conf.py +$prefix gunicorn codecov.wsgi:application --reload --workers=${GUNICORN_WORKERS:-2} --threads=${GUNICORN_THREADS:-1} --worker-connections=${GUNICORN_WORKER_CONNECTIONS:-1000} --bind 0.0.0.0:8000 --access-logfile '-' --timeout "${GUNICORN_TIMEOUT:-600}" --disable-redirect-access-to-syslog --config=gunicorn.conf.py