From 232f72daf36d7c916fdac99d3038c52583a88a02 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 14 Dec 2023 16:16:13 +0900 Subject: [PATCH] Add graphql password for temporary mutation auth --- .github/workflows/unittest.yaml | 1 + tests/graphql_test.py | 10 +++++----- world_boss/app/config.py | 2 ++ world_boss/app/graphql.py | 27 +++++++++++++++++++++------ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/.github/workflows/unittest.yaml b/.github/workflows/unittest.yaml index fec8bce..047f24e 100644 --- a/.github/workflows/unittest.yaml +++ b/.github/workflows/unittest.yaml @@ -39,6 +39,7 @@ jobs: AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} KMS_KEY_ID: ${{ secrets.KMS_KEY_ID }} SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} + GRAPHQL_PASSWORD: ${{ secrets.GRAPHQL_PASSWORD }} run: | poetry run pytest --redis-exec=$(which redis-server) --cov world_boss --cov-report=xml - name: Upload coverage to Codecov diff --git a/tests/graphql_test.py b/tests/graphql_test.py index 29f3fcc..5ade34c 100644 --- a/tests/graphql_test.py +++ b/tests/graphql_test.py @@ -104,7 +104,7 @@ def test_generate_ranking_rewards_csv( }, ) - query = "mutation { generateRankingRewardsCsv(seasonId: 1, totalUsers: 1, startNonce: 1) }" + query = f'mutation {{ generateRankingRewardsCsv(seasonId: 1, totalUsers: 1, startNonce: 1, password: "{config.graphql_password}") }}' with patch("world_boss.app.tasks.client.files_upload_v2") as m: req = fx_test_client.post("/graphql", json={"query": query}) assert req.status_code == 200 @@ -190,7 +190,7 @@ def test_prepare_transfer_assets( content=(header + content).encode() if has_header else content.encode(), ) - query = 'mutation { prepareTransferAssets(link: "https://planetariumhq.slack.com/files/1/2/test.csv", timeStamp: "2022-12-31") }' + query = f'mutation {{ prepareTransferAssets(link: "https://planetariumhq.slack.com/files/1/2/test.csv", timeStamp: "2022-12-31", password:"{config.graphql_password}") }}' with patch( "world_boss.app.api.client.files_info", return_value=mocked_response @@ -257,7 +257,7 @@ def test_prepare_reward_assets(fx_test_client, celery_session_worker, fx_session fx_session.add(transaction) result.append(reward_amount) fx_session.commit() - query = "mutation { prepareRewardAssets(seasonId: 3) }" + query = f'mutation {{ prepareRewardAssets(seasonId: 3, password: "{config.graphql_password}") }}' with patch("world_boss.app.tasks.client.chat_postMessage") as m: req = fx_test_client.post("/graphql", json={"query": query}) assert req.status_code == 200 @@ -286,7 +286,7 @@ def test_stage_transactions( fx_session.add(tx) fx_session.commit() network_type = NetworkType.MAIN - query = "mutation { stageTransactions }" + query = f'mutation {{ stageTransactions(password: "{config.graphql_password}") }}' with patch( "world_boss.app.tasks.signer.stage_transaction", return_value="tx_id" ) as m, patch("world_boss.app.tasks.client.chat_postMessage") as m2: @@ -326,7 +326,7 @@ def test_transaction_result( transaction.signer = "0xCFCd6565287314FF70e4C4CF309dB701C43eA5bD" fx_session.add(transaction) fx_session.commit() - query = "mutation { transactionResult }" + query = f'mutation {{ transactionResult(password: "{config.graphql_password}") }}' with patch("world_boss.app.tasks.client.files_upload_v2") as m: req = fx_test_client.post("/graphql", json={"query": query}) assert req.status_code == 200 diff --git a/world_boss/app/config.py b/world_boss/app/config.py index 2784197..5a7f4e7 100644 --- a/world_boss/app/config.py +++ b/world_boss/app/config.py @@ -25,6 +25,7 @@ class Settings(BaseSettings): sentry_dsn: str = "" sentry_sample_rate: float = 0.1 slack_channel_id: str + graphql_password: str class Config: env_file = ".env" @@ -56,6 +57,7 @@ class Config: }, "sentry_sample_rate": {"env": "SENTRY_SAMPLE_RATE"}, "slack_channel_id": {"env": "SLACK_CHANNEL_ID"}, + "graphql_password": {"env": "GRAPHQL_PASSWORD"}, } diff --git a/world_boss/app/graphql.py b/world_boss/app/graphql.py index 1b8e34a..b0d17eb 100644 --- a/world_boss/app/graphql.py +++ b/world_boss/app/graphql.py @@ -6,6 +6,7 @@ import strawberry from celery import chord from fastapi import Depends +from strawberry import BasePermission from strawberry.fastapi import GraphQLRouter from strawberry.types import Info @@ -41,6 +42,14 @@ async def get_context(db=Depends(get_db)): } +class IsAuthenticated(BasePermission): + message = "User is not authenticated" + + # This method can also be async! + def has_permission(self, source: typing.Any, info: Info, **kwargs) -> bool: + return kwargs["password"] == config.graphql_password + + @strawberry.type class Query: @strawberry.field @@ -67,7 +76,11 @@ def check_balance(self, info: Info) -> typing.List[str]: class Mutation: @strawberry.mutation def generate_ranking_rewards_csv( - self, season_id: int, total_users: int, start_nonce: int + self, + season_id: int, + total_users: int, + start_nonce: int, + password: str, ) -> str: task = get_ranking_rewards.delay( config.slack_channel_id, season_id, total_users, start_nonce @@ -75,7 +88,9 @@ def generate_ranking_rewards_csv( return task.id @strawberry.mutation - def prepare_transfer_assets(self, link: str, time_stamp: str, info: Info) -> str: + def prepare_transfer_assets( + self, link: str, time_stamp: str, password: str, info: Info + ) -> str: db = info.context["db"] file_id = link.split("/")[5] res = client.files_info(file=file_id) @@ -125,12 +140,12 @@ def prepare_transfer_assets(self, link: str, time_stamp: str, info: Info) -> str return task.id @strawberry.mutation - def prepare_reward_assets(self, season_id: int) -> str: + def prepare_reward_assets(self, season_id: int, password: str) -> str: task = upload_prepare_reward_assets.delay(config.slack_channel_id, season_id) return task.id @strawberry.mutation - def stage_transactions(self, info: Info) -> str: + def stage_transactions(self, password: str, info: Info) -> str: db = info.context["db"] nonce_list = ( db.query(Transaction.nonce) @@ -150,8 +165,8 @@ def stage_transactions(self, info: Info) -> str: ) return task.id - @strawberry.mutation - def transaction_result(self, info: Info) -> str: + @strawberry.mutation(permission_classes=[IsAuthenticated]) + def transaction_result(self, password: str, info: Info) -> str: db = info.context["db"] tx_ids = db.query(Transaction.tx_id).filter_by(tx_result=None) url = MINER_URLS[NetworkType.MAIN]