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

POST logic/Permissions/Test fixes #20

Merged
merged 9 commits into from
Mar 8, 2024
Merged

POST logic/Permissions/Test fixes #20

merged 9 commits into from
Mar 8, 2024

Conversation

jwalz
Copy link
Contributor

@jwalz jwalz commented Mar 7, 2024

WHAT:

  • Fix the logic for POSTing to AuthorizedStorageAccount
    • Infer the appropriate user from the session_user_uri provided by OSF instead of accepting a relationship
    • Accept the actual ID for the relationship to the external_storage_service
  • Fix the logic for POSTing to ConfiguredStorageAddon
    • Expect the resource_reference relationship to POST the resource_uri, not the ID
  • Correct Permssions for AuthorizedStorageAccount creation
    • Any user with an OSF account/session should be able to POST and AuthorizedStorageAccount
  • Correct Permissions for ConfiguredStorageAddon
    • Make sure any user who can view the resource onthe OSF can GET the ConfiguredStorageAddon
    • Optionally use the resource_uri field in the authoirzed_resource relationship to check OSF permissions
  • Fix tests
    • Replace the @with_mocked_httpx_get decorator with a configurable mock for the OSF
    • Fix failing test logic
    • Expand certain tests to account for resource visibility on the OSF

Copy link
Collaborator

@aaxelb aaxelb left a comment

Choose a reason for hiding this comment

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

wandered thru with some thoughts, looks good!

resource_uri = None
try:
resource_uri = ResourceReference.objects.get(
id=request.data.get("authoirized_resource", {}).get("id")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
id=request.data.get("authoirized_resource", {}).get("id")
id=request.data.get("authorized_resource", {}).get("id")

(nit: could also skip the query when there's no id)



class CanCreateCSA(permissions.BasePermission):
class SessionUserIsReferencedResourceAdmin(permissions.BasePermission):
Copy link
Collaborator

Choose a reason for hiding this comment

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

this permission class is still specific to ConfiguredStorageAddon (assumes the referenced resource is at authorized_resource) -- not necessarily a problem, but the rename made me think it would be more generic... could take a field_name param

"Some form of auth is necessary or POSTS are ignored."
]
self._mock_osf = MockOSF()
self.addCleanup(self._mock_osf.stop)
Copy link
Collaborator

Choose a reason for hiding this comment

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

(lil nit/suggestion) if we're on python 3.11+ and MockOSF were usable as context manager (maybe start the patchers in __enter__ and stop them in __exit__), could do

self._mock_osf = MockOSF()
self.enterContext(self._mock_osf)

...which isn't much different, but for some reason i'm still glad enterContext was added

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Didn't know about enter_context! The only examples I'm seeing are in the context of an ExitStack, so the code might look like

with ExitStack as stack:
    self._mock_osf = MockOSF().enter_context()

Maybe worth a quick experiment to see if it works, since that would also make MockOSF easier to use in individual test functions.

Copy link
Collaborator

Choose a reason for hiding this comment

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

oh hey that's confusing -- there's contextlib.ExitStack.enter_context and also unittest.TestCase.enterContext (where TestCase kinda has its own exit stack, but in camelCase) -- it's the TestCase one i think would help, and yeah agree using MockOSF as a context manager makes it nicely reusable

@@ -15,7 +15,7 @@
class AuthorizedResourceField(ResourceRelatedField):
def to_internal_value(self, data):
resource_reference, _ = ResourceReference.objects.get_or_create(
resource_uri=data["id"]
resource_uri=data["resource_uri"]
Copy link
Collaborator

Choose a reason for hiding this comment

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

am i reading correctly that the relationship in json is expected to be

"authorized_resource": {
  "data": {
    "resource_uri": "http://...",
    "type": "resource-references"
  }
}

?

i get the desire to use the resource iri instead of the resource-reference id, but i wonder if there's a more jsonapi-friendly way to do it (rather than a resource identifier object without id/lid) -- maybe a authorized_resource_uri on-create-only attribute?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm fine with re-using the "write-only" field approach we agreed on for credentials here instead of the relationship (though, yeah, need to not let it be PATCH-able). That would clean up the Permissions logic above, too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

https://openscience.atlassian.net/browse/ENG-5418

This should satisfy the comments on the Permissions class, too

reverse("authorized-storage-accounts-list"), payload, format="vnd.api+json"
)
self.assertEqual(_resp.status_code, 201)
created_account_id = int(_resp.data['url'].rstrip('/').split('/')[-1])
Copy link
Collaborator

Choose a reason for hiding this comment

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

might be half-dozen-of-another, but could avoid parsing a url by rendering the response to json

_created_account_id = _resp.json()['data']['id']

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good call, this was just copy-pasted into this class instead of living in its own

@@ -33,6 +33,7 @@ class SkipAuthMethod(exceptions.APIException):


def authenticate_resource(request, uri, required_permission):
print('WHAT?!!!!\n\n\n')
Copy link
Collaborator

Choose a reason for hiding this comment

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

😄

Copy link
Collaborator

@aaxelb aaxelb left a comment

Choose a reason for hiding this comment

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

🥪

@aaxelb aaxelb merged commit a00cf99 into develop Mar 8, 2024
2 checks passed
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