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

[WIP][ENG-6650] gravyboa: update boa addon to pull creds from gravyvalet if flag is set #10823

Open
wants to merge 1 commit into
base: feature/gravy_valet_integration
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions addons/boa/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
from flask import request
from rest_framework import status as http_status

from addons.base.views import _get_authenticated_resource, _check_resource_permissions
from addons.base import generic_views
from addons.boa.models import BoaProvider
from addons.boa.serializer import BoaSerializer
from addons.boa.tasks import submit_to_boa
from api.base.utils import waterbutler_api_url_for
from api.waffle.utils import flag_is_active
from framework.auth.decorators import must_be_logged_in
from framework.celery_tasks.handlers import enqueue_task
from osf import features
from osf.models import ExternalAccount, AbstractNode
from osf.utils import permissions
from website import settings as osf_settings
Expand Down Expand Up @@ -74,16 +77,30 @@ def boa_submit_job(node_addon, **kwargs):

req_params = request.json

# Boa addon configuration
provider = node_addon.external_account.provider_id
parts = provider.rsplit(':', 1)
host, username = parts[0], parts[1]
password = node_addon.external_account.oauth_key
if flag_is_active(request, features.ENABLE_GV):
project_guid = req_params['data']['nodeId']
resource = _get_authenticated_resource(project_guid)

# User and project
auth = kwargs['auth']
action = 'upload'
_check_resource_permissions(resource, auth, action)

addon_credentials = resource.serialize_waterbutler_credentials('boa')

host = addon_credentials['host']
username = addon_credentials['username']
password = addon_credentials['password']
else:
# Boa addon configuration from OSF addons
provider = node_addon.external_account.provider_id
parts = provider.rsplit(':', 1)
host, username = parts[0], parts[1]
password = node_addon.external_account.oauth_key
project_guid = req_params['data']['nodeId']

# User
user = kwargs['auth'].user
user_guid = user._id
project_guid = req_params['data']['nodeId']

# Query file
file_name = req_params['data']['name']
Expand Down
15 changes: 14 additions & 1 deletion osf/external/gravy_valet/translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import markupsafe

from addons.bitbucket.apps import BitbucketAddonConfig
from addons.boa.apps import BoaAddonAppConfig
from addons.box.apps import BoxAddonAppConfig
from addons.dataverse.apps import DataverseAddonAppConfig
from addons.dropbox.apps import DropboxAddonAppConfig
Expand Down Expand Up @@ -40,6 +41,7 @@ class _LegacyConfigsForWBKey(enum.Enum):
s3 = S3AddonAppConfig
zotero = ZoteroAddonAppConfig
mendeley = MendeleyAddonConfig
boa = BoaAddonAppConfig


def make_ephemeral_user_settings(gv_account_data, requesting_user):
Expand All @@ -49,7 +51,12 @@ def make_ephemeral_user_settings(gv_account_data, requesting_user):
include_path=include_path,
attribute_name='external_service_name'
)

elif gv_account_data.resource_type == 'configured-computing-addons':
include_path = ('external_computing_service',)
service_key = gv_account_data.get_included_attribute(
include_path=include_path,
attribute_name='external_service_name'
)
else:
include_path = ('external_storage_service',)
service_key = gv_account_data.get_included_attribute(
Expand All @@ -71,6 +78,12 @@ def make_ephemeral_node_settings(gv_addon_data: gv_requests.JSONAPIResultEntry,
include_path=include_path,
attribute_name='external_service_name'
)
elif gv_addon_data.resource_type == 'configured-computing-addons':
include_path = ('base_account', 'external_computing_service')
service_key = gv_addon_data.get_included_attribute(
include_path=include_path,
attribute_name='external_service_name'
)
else:
include_path = ('base_account', 'external_storage_service')
service_key = gv_addon_data.get_included_attribute(
Expand Down
144 changes: 141 additions & 3 deletions osf_tests/external/gravy_valet/gv_fakes.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,13 @@ def _serialize_relationships(self):
accounts_storage_relationship = self._format_relationship_entry(relationship_path='authorized_storage_accounts')
accounts_citation_relationship = self._format_relationship_entry(
relationship_path='authorized_citation_accounts')
accounts_computing_relationship = self._format_relationship_entry(
relationship_path='authorized_computing_accounts'
)
return {
'authorized_storage_accounts': accounts_storage_relationship,
'authorized_citation_accounts': accounts_citation_relationship
'authorized_citation_accounts': accounts_citation_relationship,
'authorized_computing_accounts': accounts_computing_relationship,
}


Expand Down Expand Up @@ -113,9 +117,13 @@ def _serialize_relationships(self):
relationship_path='configured_storage_addons')
configured_citation_addons_relationship = self._format_relationship_entry(
relationship_path='configured_citation_addons')
configured_computing_addons_relationship = self._format_relationship_entry(
relationship_path='configured_computing_addons'
)
return {
'configured_storage_addons': configured_storage_addons_relationship,
'configured_citation_addons': configured_citation_addons_relationship
'configured_citation_addons': configured_citation_addons_relationship,
'configured_computing_addons': configured_computing_addons_relationship,
}


Expand Down Expand Up @@ -151,11 +159,16 @@ class _FakeCitationAddonProvider(_FakeAddonProvider):
RESOURCE_TYPE = 'external-citation-services'


class _FakeComputingAddonProvider(_FakeAddonProvider):
RESOURCE_TYPE = 'external-computing-services'


@dataclasses.dataclass(frozen=True)
class _FakeAccount(_FakeGVEntity):
RESOURCE_TYPE = 'authorized-accounts'
external_storage_service: _FakeAddonProvider | None
external_citation_service: _FakeCitationAddonProvider | None
external_computing_service: _FakeComputingAddonProvider | None
account_owner_pk: int
display_name: str = ''

Expand Down Expand Up @@ -196,6 +209,17 @@ def _serialize_relationships(self):
relationship_path='configured_citation_addons'
)
})
if self.external_computing_service is not None:
_serialized_relationships.update({
'external_computing_service': self._format_relationship_entry(
relationship_path='external_computing_service',
related_type=_FakeComputingAddonProvider.RESOURCE_TYPE,
related_pk=self.external_computing_service.pk
),
'configured_computing_addons': self._format_relationship_entry(
relationship_path='configured_computing_addons'
)
})
if self.external_storage_service is not None:
_serialized_relationships.update({
'external_storage_service': self._format_relationship_entry(
Expand Down Expand Up @@ -252,6 +276,12 @@ def _serialize_relationships(self):
related_type=_FakeCitationAddonProvider.RESOURCE_TYPE,
related_pk=self.base_account.external_storage_service.pk
),
# TODO: uncomment this after fixing the gv_fakes testing for non-storage
# 'external_computing_service': self._format_relationship_entry(
# relationship_path='external_computing_service',
# related_type=_FakeComputingAddonProvider.RESOURCE_TYPE,
# related_pk=self.base_account.external_computing_service.pk
# ),
'connected_operations': self._format_relationship_entry(
relationship_path='connected_operations'
),
Expand All @@ -264,13 +294,17 @@ class FakeGravyValet:
r'v1/resource-references(/(?P<pk>\d+)|(\?filter\[resource_uri\]=(?P<resource_uri>[^&]+)))': '_get_resource',
r'v1/authorized-storage-accounts/(?P<pk>\d+)': '_get_account',
r'v1/authorized-citation-accounts/(?P<pk>\d+)': '_get_citation_account',
r'v1/authorized-computing-accounts/(?P<pk>\d+)': '_get_computing_account',
r'v1/configured-storage-addons/(?P<pk>\d+)': '_get_addon',
r'v1/configured-citation-addons/(?P<pk>\d+)': '_get_citation_addon',
r'v1/configured-computing-addons/(?P<pk>\d+)': '_get_computing_addon',
r'v1/configured-storage-addons/(?P<pk>\d+)/waterbutler-credentials': '_get_wb_settings',
r'v1/user-references/(?P<user_pk>\d+)/authorized_storage_accounts': '_get_user_accounts',
r'v1/user-references/(?P<user_pk>\d+)/authorized_citation_accounts': '_get_user_citation_accounts',
r'v1/user-references/(?P<user_pk>\d+)/authorized_computing_accounts': '_get_user_computing_accounts',
r'v1/resource-references/(?P<resource_pk>\d+)/configured_storage_addons': '_get_resource_addons',
r'v1/resource-references/(?P<resource_pk>\d+)/configured_citation_addons': '_get_resource_citation_addons',
r'v1/resource-references/(?P<resource_pk>\d+)/configured_computing_addons': '_get_resource_computing_addons',
}

def __init__(self):
Expand Down Expand Up @@ -323,7 +357,7 @@ def _get_or_create_resource_entry(self, resource: AbstractNode):
return resource_uri, resource_pk

def configure_fake_provider(self, provider_name: str, is_citation_provider: bool = False,
**service_attrs) -> _FakeAddonProvider:
is_computing_provider: bool = False, **service_attrs) -> _FakeAddonProvider:
known_provider = self._known_providers.get(provider_name)
provider_pk = known_provider.pk if known_provider else len(self._known_providers) + 1
if is_citation_provider:
Expand All @@ -332,6 +366,12 @@ def configure_fake_provider(self, provider_name: str, is_citation_provider: bool
pk=provider_pk,
**service_attrs
)
elif is_computing_provider:
new_provider = _FakeComputingAddonProvider(
name=provider_name,
pk=provider_pk,
**service_attrs
)
else:
new_provider = _FakeAddonProvider(
name=provider_name,
Expand All @@ -353,9 +393,15 @@ def configure_fake_account(
if isinstance(connected_provider, _FakeCitationAddonProvider):
account_attrs['external_storage_service'] = None
account_attrs['external_citation_service'] = connected_provider
account_attrs['external_computing_service'] = None
elif isinstance(connected_provider, _FakeComputingAddonProvider):
account_attrs['external_storage_service'] = None
account_attrs['external_citation_service'] = None
account_attrs['external_computing_service'] = connected_provider
else:
account_attrs['external_storage_service'] = connected_provider
account_attrs['external_citation_service'] = None
account_attrs['external_computing_service'] = None
new_account = _FakeAccount(
pk=account_pk,
account_owner_pk=user_pk,
Expand Down Expand Up @@ -545,6 +591,34 @@ def _get_citation_account(
)
return _format_response_body(data=[], list_view=True)

def _get_computing_account(
self,
headers: dict,
pk: str,
include_param: str = '',
) -> str:
pk = int(pk)
account = None
for account in itertools.chain.from_iterable(self._user_accounts.values()):
if account.pk == pk:
account = account
break

if not account:
logger.critical('Account not found')
raise FakeGVError(HTTPStatus.NOT_FOUND)

if self.validate_headers:
user_uri = self._known_users[account.account_owner_pk]
_validate_user(user_uri, headers)
if account.external_computing_service is not None:
return _format_response_body(
data=account,
list_view=False,
include_param=include_param,
)
return _format_response_body(data=[], list_view=True)

def _get_addon(
self, headers: dict,
pk: str,
Expand Down Expand Up @@ -596,6 +670,32 @@ def _get_citation_addon(
)
return _format_response_body(data=[], list_view=True)

def _get_computing_addon(
self, headers: dict,
pk: str,
include_param: str = '',
) -> str:
pk = int(pk)
addon = None
for addon in itertools.chain.from_iterable(self._resource_addons.values()):
if addon.pk == pk:
addon = addon
break

if not addon:
raise FakeGVError(HTTPStatus.NOT_FOUND)

if self.validate_headers:
resource_uri = self._known_resources[addon.resource_pk]
_validate_resource_access(resource_uri, headers)
if addon.base_account.external_computing_service is not None:
return _format_response_body(
data=addon,
list_view=False,
include_param=include_param,
)
return _format_response_body(data=[], list_view=True)

def _get_user_accounts(
self,
headers: dict,
Expand Down Expand Up @@ -632,6 +732,25 @@ def _get_user_citation_accounts(
)
return _format_response_body(data=[], list_view=True)

def _get_user_computing_accounts(
self,
headers: dict,
user_pk: str,
include_param: str = '',
) -> str:
user_pk = int(user_pk)
if self.validate_headers:
user_uri = self._known_users[user_pk]
_validate_user(user_uri, headers)
if all(map(lambda x: x.external_computing_service is not None,
self._user_accounts.get(user_pk, []))):
return _format_response_body(
data=self._user_accounts.get(user_pk, []),
list_view=True,
include_param=include_param
)
return _format_response_body(data=[], list_view=True)

def _get_resource_addons(
self,
headers: dict,
Expand Down Expand Up @@ -668,6 +787,25 @@ def _get_resource_citation_addons(
)
return _format_response_body(data=[], list_view=True)

def _get_resource_computing_addons(
self,
headers: dict,
resource_pk: str,
include_param: str = '',
) -> str:
resource_pk = int(resource_pk)
if self.validate_headers:
resource_uri = self._known_resources[resource_pk]
_validate_resource_access(resource_uri, headers)
if all(map(lambda x: x.base_account.external_computing_service is not None,
self._resource_addons.get(resource_pk, []))):
return _format_response_body(
data=self._resource_addons.get(resource_pk, []),
include_param=include_param,
list_view=True,
)
return _format_response_body(data=[], list_view=True)


def _format_response_body(
data, # _FakeGVEntity | list[_FakeGVEntity]
Expand Down
Loading