Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Add endpoint that checks whether an owner has Gen AI consent #1102

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

rohitvinnakota-codecov
Copy link
Contributor

We want to authenticate 2 ways from Seer <-> Codecov before invoking any of our AI features. This endpoint will be called from Seer to verify that the owner has a valid app installation with Codecov.

Legal Boilerplate

Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. In 2022 this entity acquired Codecov and as result Sentry is going to need some rights from me in order to utilize my contributions in this PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.

@codecov-notifications
Copy link

codecov-notifications bot commented Jan 15, 2025

Codecov Report

Attention: Patch coverage is 95.91837% with 2 lines in your changes missing coverage. Please review.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
api/gen_ai/views.py 95.34% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@codecov-qa
Copy link

codecov-qa bot commented Jan 15, 2025

❌ 5 Tests Failed:

Tests completed Failed Passed Skipped
2701 5 2696 7
View the top 3 failed tests by shortest run time
api/gen_ai/tests/test_gen_ai.py::GenAIAuthViewTests::test_missing_parameters
Stack Traces | 0.006s run time
self = <test_gen_ai.GenAIAuthViewTests testMethod=test_missing_parameters>
mock_config = <MagicMock name='get_config' id='140087881010880'>

    @patch("utils.config.get_config", return_value=PAYLOAD_SECRET)
    def test_missing_parameters(self, mock_config):
        payload = {}
        sig, data = sign_payload(payload)
        response = self.client.post(
            VIEW_URL,
            data=payload,
            content_type="application/json",
            HTTP_X_GEN_AI_AUTH_SIGNATURE=sig,
        )
>       self.assertEqual(response.status_code, 400)
E       AssertionError: 403 != 400

.../gen_ai/tests/test_gen_ai.py:34: AssertionError
api/gen_ai/tests/test_gen_ai.py::GenAIAuthViewTests::test_owner_not_found
Stack Traces | 0.008s run time
self = <test_gen_ai.GenAIAuthViewTests testMethod=test_owner_not_found>
mock_config = <MagicMock name='get_config' id='140087880766560'>

    @patch("utils.config.get_config", return_value=PAYLOAD_SECRET)
    def test_owner_not_found(self, mock_config):
        payload = {"external_owner_id": "nonexistent_owner", "repo_service_id": "101"}
        sig, serialized_data = sign_payload(payload)
        response = self.client.post(
            VIEW_URL,
            HTTP_X_GEN_AI_AUTH_SIGNATURE=sig,
            data=serialized_data,
            content_type="application/json",
        )
>       self.assertEqual(response.status_code, 404)
E       AssertionError: 403 != 404

.../gen_ai/tests/test_gen_ai.py:62: AssertionError
api/gen_ai/tests/test_gen_ai.py::GenAIAuthViewTests::test_no_installation
Stack Traces | 0.011s run time
self = <test_gen_ai.GenAIAuthViewTests testMethod=test_no_installation>
mock_config = <MagicMock name='get_config' id='140087881449248'>

    @patch("utils.config.get_config", return_value=PAYLOAD_SECRET)
    def test_no_installation(self, mock_config):
        _ = OwnerFactory(service="github", service_id="owner1", username="test1")
        payload = {"external_owner_id": "owner1", "repo_service_id": "101"}
        sig, data = sign_payload(payload)
        response = self.client.post(
            VIEW_URL,
            data=data,
            content_type="application/json",
            HTTP_X_GEN_AI_AUTH_SIGNATURE=sig,
        )
    
>       self.assertEqual(response.status_code, 200)
E       AssertionError: 403 != 200

.../gen_ai/tests/test_gen_ai.py:76: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📢 Thoughts on this report? Let us know!

Copy link

codecov-public-qa bot commented Jan 15, 2025

❌ 5 Tests Failed:

Tests completed Failed Passed Skipped
2702 5 2697 6
View the top 3 failed tests by shortest run time
api/gen_ai/tests/test_gen_ai.py::GenAIAuthViewTests::test_missing_parameters
Stack Traces | 0.006s run time
self = <test_gen_ai.GenAIAuthViewTests testMethod=test_missing_parameters>
mock_config = <MagicMock name='get_config' id='140087881010880'>

    @patch("utils.config.get_config", return_value=PAYLOAD_SECRET)
    def test_missing_parameters(self, mock_config):
        payload = {}
        sig, data = sign_payload(payload)
        response = self.client.post(
            VIEW_URL,
            data=payload,
            content_type="application/json",
            HTTP_X_GEN_AI_AUTH_SIGNATURE=sig,
        )
>       self.assertEqual(response.status_code, 400)
E       AssertionError: 403 != 400

.../gen_ai/tests/test_gen_ai.py:34: AssertionError
api/gen_ai/tests/test_gen_ai.py::GenAIAuthViewTests::test_owner_not_found
Stack Traces | 0.008s run time
self = <test_gen_ai.GenAIAuthViewTests testMethod=test_owner_not_found>
mock_config = <MagicMock name='get_config' id='140087880766560'>

    @patch("utils.config.get_config", return_value=PAYLOAD_SECRET)
    def test_owner_not_found(self, mock_config):
        payload = {"external_owner_id": "nonexistent_owner", "repo_service_id": "101"}
        sig, serialized_data = sign_payload(payload)
        response = self.client.post(
            VIEW_URL,
            HTTP_X_GEN_AI_AUTH_SIGNATURE=sig,
            data=serialized_data,
            content_type="application/json",
        )
>       self.assertEqual(response.status_code, 404)
E       AssertionError: 403 != 404

.../gen_ai/tests/test_gen_ai.py:62: AssertionError
api/gen_ai/tests/test_gen_ai.py::GenAIAuthViewTests::test_no_installation
Stack Traces | 0.011s run time
self = <test_gen_ai.GenAIAuthViewTests testMethod=test_no_installation>
mock_config = <MagicMock name='get_config' id='140087881449248'>

    @patch("utils.config.get_config", return_value=PAYLOAD_SECRET)
    def test_no_installation(self, mock_config):
        _ = OwnerFactory(service="github", service_id="owner1", username="test1")
        payload = {"external_owner_id": "owner1", "repo_service_id": "101"}
        sig, data = sign_payload(payload)
        response = self.client.post(
            VIEW_URL,
            data=data,
            content_type="application/json",
            HTTP_X_GEN_AI_AUTH_SIGNATURE=sig,
        )
    
>       self.assertEqual(response.status_code, 200)
E       AssertionError: 403 != 200

.../gen_ai/tests/test_gen_ai.py:76: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📢 Thoughts on this report? Let us know!

Copy link
Contributor

github-actions bot commented Jan 15, 2025

✅ All tests successful. No failed tests were found.

📣 Thoughts on this report? Let Codecov know! | Powered by Codecov

Copy link

codecov bot commented Jan 15, 2025

Codecov Report

Attention: Patch coverage is 95.91837% with 2 lines in your changes missing coverage. Please review.

Project coverage is 96.10%. Comparing base (ab5c1ff) to head (ac5908b).
Report is 1 commits behind head on main.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
api/gen_ai/views.py 95.34% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1102   +/-   ##
=======================================
  Coverage   96.09%   96.10%           
=======================================
  Files         832      835    +3     
  Lines       19501    19596   +95     
=======================================
+ Hits        18740    18832   +92     
- Misses        761      764    +3     
Flag Coverage Δ
unit 96.01% <95.91%> (+<0.01%) ⬆️
unit-latest-uploader 96.01% <95.91%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rohitvinnakota-codecov rohitvinnakota-codecov force-pushed the rvinnakota/add-ai-auth-endpoint branch from 20acd52 to 28457dd Compare January 16, 2025 21:34
@rohitvinnakota-codecov rohitvinnakota-codecov marked this pull request as ready for review January 17, 2025 20:41
api/gen_ai/views.py Outdated Show resolved Hide resolved
api/gen_ai/views.py Outdated Show resolved Hide resolved

def validate_signature(self, request):
key = get_config(
"gen_ai", "auth_secret", default=b"testixik8qdauiab1yiffydimvi72ekq"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to avoid setting a default here and instead error out if it is not set?

Comment on lines +17 to +20
def sign_payload(payload, secret=PAYLOAD_SECRET):
data = json.dumps(payload, separators=(",", ":")).encode("utf-8")
signature = "sha256=" + hmac.new(secret, data, digestmod=sha256).hexdigest()
return signature, data
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def sign_payload(payload, secret=PAYLOAD_SECRET):
data = json.dumps(payload, separators=(",", ":")).encode("utf-8")
signature = "sha256=" + hmac.new(secret, data, digestmod=sha256).hexdigest()
return signature, data
def sign_payload(payload: bytes, secret=PAYLOAD_SECRET):
signature = "sha256=" + hmac.new(secret, payload, digestmod=sha256).hexdigest()
return signature, data

I would avoid going from bytes -> string -> bytes with json.dumps(). Ordering of the keys isn't guaranteed (afaik), this can result in different signatures. The recommendation when working with payload signatures like this is to always calculate them based on the raw bytes.

@rohitvinnakota-codecov rohitvinnakota-codecov force-pushed the rvinnakota/add-ai-auth-endpoint branch from 161ab0e to ac5908b Compare January 17, 2025 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants