From 0c457e1712280d68c16eccd213530a69e36eceda Mon Sep 17 00:00:00 2001 From: hadleyking Date: Tue, 7 Mar 2023 15:33:09 -0500 Subject: [PATCH 01/11] Added new api for better searches Changes to be committed: modified: bco_api/api/scripts/utilities/DbUtils.py modified: bco_api/api/views.py modified: bco_api/bco_api/settings.py modified: bco_api/bco_api/urls.py new file: bco_api/search/apis.py new file: bco_api/search/selectors.py new file: bco_api/search/urls.py --- bco_api/api/scripts/utilities/DbUtils.py | 2 +- bco_api/api/views.py | 1 - bco_api/bco_api/settings.py | 2 +- bco_api/bco_api/urls.py | 1 + bco_api/search/apis.py | 77 ++++++++++++++++++++++++ bco_api/search/selectors.py | 12 ++++ bco_api/search/urls.py | 8 +++ 7 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 bco_api/search/apis.py create mode 100644 bco_api/search/selectors.py create mode 100644 bco_api/search/urls.py diff --git a/bco_api/api/scripts/utilities/DbUtils.py b/bco_api/api/scripts/utilities/DbUtils.py index f9ef0a01..55818fff 100755 --- a/bco_api/api/scripts/utilities/DbUtils.py +++ b/bco_api/api/scripts/utilities/DbUtils.py @@ -416,7 +416,7 @@ def activate_account(self, p_email): r = requests.post( data=json.dumps(uu.get_user_info(username=new_username), default=str), headers=headers, - url="http://127.0.0.1:8181/users/add_api/", + url="http://127.0.0.1:8080/users/add_api/", ) # Delete the record in the temporary table. diff --git a/bco_api/api/views.py b/bco_api/api/views.py index 8d17cac9..e7163bc3 100755 --- a/bco_api/api/views.py +++ b/bco_api/api/views.py @@ -1009,7 +1009,6 @@ class ApiObjectsPublish(APIView): def post(self, request) -> Response: return check_post_and_process(request, post_api_objects_publish) - class ApiObjectsSearch(APIView): """ Search for BCO diff --git a/bco_api/bco_api/settings.py b/bco_api/bco_api/settings.py index 2ccb4939..69346e46 100644 --- a/bco_api/bco_api/settings.py +++ b/bco_api/bco_api/settings.py @@ -133,7 +133,7 @@ # Application definition # Token-based authentication. -# Source: https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication +# Source: https://www.django-rest-framework.org/api-guide/authentication/#tokenau thentication INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.admindocs", diff --git a/bco_api/bco_api/urls.py b/bco_api/bco_api/urls.py index 3588b5ac..322ebd56 100755 --- a/bco_api/bco_api/urls.py +++ b/bco_api/bco_api/urls.py @@ -8,4 +8,5 @@ urlpatterns = [ path("api/admin/", admin.site.urls), path("", include("api.urls")), + path("api/", include("search.urls")) ] diff --git a/bco_api/search/apis.py b/bco_api/search/apis.py new file mode 100644 index 00000000..2d0bc779 --- /dev/null +++ b/bco_api/search/apis.py @@ -0,0 +1,77 @@ +# search/apis.py +import json +from drf_yasg import openapi +from drf_yasg.utils import swagger_auto_schema +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView +from search.selectors import search_db +from api.models import BCO +from itertools import chain + +class SearchObjectsAPI(APIView): + """Search the BCODB **BETA** + + ------------------- + Endpoint for use of query string based search. + """ + authentication_classes = [] + permission_classes = [] + auth = openapi.Parameter('test', openapi.IN_QUERY, description="test manual param", type=openapi.TYPE_BOOLEAN) + + @swagger_auto_schema( + manual_parameters=[ + openapi.Parameter('contents', + openapi.IN_QUERY, + description="Search in the contents of the BCO", + type=openapi.TYPE_STRING + ), + openapi.Parameter('prefix', + openapi.IN_QUERY, + description="BCO Prefix to search", + type=openapi.TYPE_STRING + ), + openapi.Parameter('owner', + openapi.IN_QUERY, + description="Search by BCO owner", + type=openapi.TYPE_STRING + ), + openapi.Parameter('bco_id', + openapi.IN_QUERY, + description="BCO object_id to search for", + type=openapi.TYPE_STRING + ) + ], + responses={ + 201: "Account has been authorized.", + 208: "Account has already been authorized.", + 403: "Requestor's credentials were rejected.", + 424: "Account has not been registered.", + }, + tags=["Account Management"], + ) + + def get(self, request) -> Response: + """GET search + TODO: multiple values in the URL will only return the last one. + TODO: add authentication options + """ + return_values = [ + "contents", + "last_update", + "object_class", + "object_id", + "owner_group", + "owner_user", + "prefix", + "schema", + "state", + ] + search = self.request.GET + print(search) + result = BCO.objects.all() + for query, value in search.items(): + filter = f'{query}__icontains' + result = search_db(filter, value, result) + search_result = chain(result.values(*return_values)) + return Response(status=status.HTTP_200_OK, data={search_result}) diff --git a/bco_api/search/selectors.py b/bco_api/search/selectors.py new file mode 100644 index 00000000..0925d161 --- /dev/null +++ b/bco_api/search/selectors.py @@ -0,0 +1,12 @@ +# search/selectors.py +from django.db.models import Q +from api.models import BCO +from django.db.models.query import QuerySet + +def search_db(filter:str, value:str, result:QuerySet)-> QuerySet: + """ + """ + new_result = result.filter(**{filter: value}) + print(filter, value) + print("new", len(new_result)) + return new_result \ No newline at end of file diff --git a/bco_api/search/urls.py b/bco_api/search/urls.py new file mode 100644 index 00000000..bf787ac2 --- /dev/null +++ b/bco_api/search/urls.py @@ -0,0 +1,8 @@ +# search/urls.py + +from django.urls import path, re_path +from search.apis import SearchObjectsAPI + +urlpatterns = [ + re_path(r'objects/$', SearchObjectsAPI.as_view()), +] \ No newline at end of file From 2262f8ebda0b76492c3a7229d835dc6cea0ee245 Mon Sep 17 00:00:00 2001 From: Hadley King Date: Tue, 28 Mar 2023 11:17:46 -0400 Subject: [PATCH 02/11] OAuth2 integration start (#157) * OAuth2 integration start Changes to be committed: modified: bco_api/api/views.py modified: bco_api/bco_api/settings.py modified: bco_api/bco_api/urls.py modified: requirements.txt * Major refactor * ObtainJSONWebToken * Saving changes Changes to be committed: modified: api/scripts/method_specific/POST_api_accounts_describe.py modified: api/views.py modified: authentication/services.py modified: bcodb/settings.py modified: requirements.txt * Enabled OAuth creation of BCO Draft Changes to be committed: modified: api/scripts/method_specific/POST_api_objects_drafts_create.py new file: authentication/admin.py modified: authentication/apis.py new file: authentication/apps.py new file: authentication/migrations/0001_initial.py new file: authentication/migrations/__init__.py modified: authentication/models.py modified: authentication/selectors.py modified: authentication/services.py modified: bcodb/settings.py * Cleaning Changes to be committed: modified: api/model/groups.py modified: authentication/services.py --- {bco_api/api => api}/__init__.py | 0 {bco_api/api => api}/admin.py | 0 {bco_api/api => api}/apps.py | 0 {bco_api/api => api}/fixtures/bootstrap.json | 0 {bco_api/api => api}/fixtures/metafixtures | 0 .../api => api}/fixtures/metafixtures.json | 0 {bco_api/api => api}/keys.sh | 0 .../api => api}/migrations/0001_initial.py | 0 .../migrations/0002_auto_20220124_2356.py | 0 .../0003_rename_meta_table_prefix_table.py | 0 .../0004_rename_group_info_groupinfo.py | 0 .../migrations/0005_rename_prefixes_prefix.py | 0 {bco_api/api => api}/migrations/__init__.py | 0 {bco_api/api => api}/model/__init__.py | 0 {bco_api/api => api}/model/groups.py | 2 +- {bco_api/api => api}/model/prefix.py | 0 {bco_api/api => api}/models.py | 0 {bco_api/api => api}/permissions.py | 0 {bco_api/api => api}/rdb.sh | 0 .../request_definitions/GET.schema | 0 .../request_definitions/POST.schema | 0 .../DELETE_delete_object_by_id.schema | 0 .../templates/GET_activate_account.schema | 0 .../templates/GET_get_object_by_id.schema | 0 .../GET_retrieve_available_schema.schema | 0 ...ert_existing_object_between_schemas.schema | 0 .../POST_convert_payload_to_schema.schema | 0 .../templates/POST_new_account.schema | 0 .../POST_object_listing_by_token.schema | 0 .../templates/POST_objects_draft.schema | 0 .../templates/POST_objects_publish.schema | 0 .../templates/POST_read_object.schema | 0 ...OST_validate_payload_against_schema.schema | 0 {bco_api/api => api}/scripts/__init__.py | 0 .../method_specific/GET_activate_account.py | 0 .../method_specific/GET_draft_object_by_id.py | 0 .../GET_published_object_by_id.py | 0 ...GET_published_object_by_id_with_version.py | 0 .../GET_retrieve_available_schema.py | 0 .../POST_api_accounts_describe.py | 1 - .../method_specific/POST_api_accounts_new.py | 0 .../POST_api_objects_drafts_create.py | 9 +- .../POST_api_objects_drafts_delete.py | 0 .../POST_api_objects_drafts_modify.py | 0 .../POST_api_objects_drafts_permissions.py | 0 ...POST_api_objects_drafts_permissions_set.py | 0 .../POST_api_objects_drafts_publish.py | 0 .../POST_api_objects_drafts_read.py | 0 .../POST_api_objects_drafts_token.py | 0 .../POST_api_objects_publish.py | 0 .../POST_api_objects_published.py | 0 .../POST_api_objects_search.py | 0 .../method_specific/POST_api_objects_token.py | 0 .../POST_validate_payload_against_schema.py | 0 .../scripts/method_specific/__init__.py | 0 .../api => api}/scripts/utilities/DbUtils.py | 0 .../scripts/utilities/FileUtils.py | 0 .../scripts/utilities/JsonUtils.py | 0 .../scripts/utilities/RequestUtils.py | 0 .../scripts/utilities/ResponseUtils.py | 0 .../scripts/utilities/SettingsUtils.py | 0 .../scripts/utilities/UserUtils.py | 0 .../api => api}/scripts/utilities/__init__.py | 0 {bco_api/api => api}/serializers.py | 0 {bco_api/api => api}/signals.py | 0 .../api/account_activation_message.html | 0 {bco_api/api => api}/tests/__init__.py | 0 {bco_api/api => api}/tests/test_bcos.json | 0 {bco_api/api => api}/tests/test_forms.py | 0 .../api => api}/tests/test_group_post_api.py | 0 {bco_api/api => api}/tests/test_model_bco.py | 0 .../api => api}/tests/test_model_groups.py | 0 .../api => api}/tests/test_model_prefix.py | 0 {bco_api/api => api}/tests/test_model_user.py | 0 .../api => api}/tests/test_prefix_post_api.py | 0 {bco_api/api => api}/tests/test_views.py | 0 {bco_api/api => api}/tests_automated.py | 0 {bco_api/api => api}/urls.py | 0 .../IEEE/2791object.json | 0 .../IEEE/description_domain.json | 0 .../IEEE/error_domain.json | 0 .../IEEE/execution_domain.json | 0 .../IEEE/io_domain.json | 0 .../IEEE/parametric_domain.json | 0 .../IEEE/provenance_domain.json | 0 .../IEEE/usability_domain.json | 0 .../IEEE_sub/IEEE2791-2020.schema | 0 .../IEEE_sub/domains/description_domain.json | 0 .../IEEE_sub/domains/error_domain.json | 0 .../IEEE_sub/domains/execution_domain.json | 0 .../IEEE_sub/domains/io_domain.json | 0 .../IEEE_sub/domains/parametric_domain.json | 0 .../IEEE_sub/domains/provenance_domain.json | 0 .../IEEE_sub/domains/usability_domain.json | 0 .../validation_definitions/uri_external | 0 {bco_api/api => api}/views.py | 12 +- .../bco_api => authentication}/__init__.py | 0 authentication/admin.py | 7 + authentication/apis.py | 51 +++++ authentication/apps.py | 6 + authentication/migrations/0001_initial.py | 25 +++ .../migrations/__init__.py | 0 authentication/models.py | 13 ++ authentication/selectors.py | 78 ++++++++ authentication/services.py | 174 ++++++++++++++++++ authentication/urls.py | 8 + bco_api/api/templates/api/.DS_Store | Bin 6148 -> 0 bytes bcodb/__init__.py | 0 {bco_api/bco_api => bcodb}/asgi.py | 0 {bco_api/bco_api => bcodb}/settings.py | 26 ++- {bco_api/bco_api => bcodb}/urls.py | 6 +- {bco_api/bco_api => bcodb}/wsgi.py | 2 +- bco_api/manage.py => manage.py | 2 +- requirements.txt | 9 + bco_api/server.conf => server.conf | 0 115 files changed, 417 insertions(+), 14 deletions(-) rename {bco_api/api => api}/__init__.py (100%) rename {bco_api/api => api}/admin.py (100%) rename {bco_api/api => api}/apps.py (100%) rename {bco_api/api => api}/fixtures/bootstrap.json (100%) rename {bco_api/api => api}/fixtures/metafixtures (100%) rename {bco_api/api => api}/fixtures/metafixtures.json (100%) rename {bco_api/api => api}/keys.sh (100%) rename {bco_api/api => api}/migrations/0001_initial.py (100%) rename {bco_api/api => api}/migrations/0002_auto_20220124_2356.py (100%) rename {bco_api/api => api}/migrations/0003_rename_meta_table_prefix_table.py (100%) rename {bco_api/api => api}/migrations/0004_rename_group_info_groupinfo.py (100%) rename {bco_api/api => api}/migrations/0005_rename_prefixes_prefix.py (100%) rename {bco_api/api => api}/migrations/__init__.py (100%) rename {bco_api/api => api}/model/__init__.py (100%) rename {bco_api/api => api}/model/groups.py (99%) rename {bco_api/api => api}/model/prefix.py (100%) rename {bco_api/api => api}/models.py (100%) rename {bco_api/api => api}/permissions.py (100%) rename {bco_api/api => api}/rdb.sh (100%) rename {bco_api/api => api}/request_definitions/GET.schema (100%) rename {bco_api/api => api}/request_definitions/POST.schema (100%) rename {bco_api/api => api}/request_definitions/templates/DELETE_delete_object_by_id.schema (100%) rename {bco_api/api => api}/request_definitions/templates/GET_activate_account.schema (100%) rename {bco_api/api => api}/request_definitions/templates/GET_get_object_by_id.schema (100%) rename {bco_api/api => api}/request_definitions/templates/GET_retrieve_available_schema.schema (100%) rename {bco_api/api => api}/request_definitions/templates/POST_convert_existing_object_between_schemas.schema (100%) rename {bco_api/api => api}/request_definitions/templates/POST_convert_payload_to_schema.schema (100%) rename {bco_api/api => api}/request_definitions/templates/POST_new_account.schema (100%) rename {bco_api/api => api}/request_definitions/templates/POST_object_listing_by_token.schema (100%) rename {bco_api/api => api}/request_definitions/templates/POST_objects_draft.schema (100%) rename {bco_api/api => api}/request_definitions/templates/POST_objects_publish.schema (100%) rename {bco_api/api => api}/request_definitions/templates/POST_read_object.schema (100%) rename {bco_api/api => api}/request_definitions/templates/POST_validate_payload_against_schema.schema (100%) rename {bco_api/api => api}/scripts/__init__.py (100%) rename {bco_api/api => api}/scripts/method_specific/GET_activate_account.py (100%) rename {bco_api/api => api}/scripts/method_specific/GET_draft_object_by_id.py (100%) rename {bco_api/api => api}/scripts/method_specific/GET_published_object_by_id.py (100%) rename {bco_api/api => api}/scripts/method_specific/GET_published_object_by_id_with_version.py (100%) rename {bco_api/api => api}/scripts/method_specific/GET_retrieve_available_schema.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_accounts_describe.py (99%) rename {bco_api/api => api}/scripts/method_specific/POST_api_accounts_new.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_drafts_create.py (94%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_drafts_delete.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_drafts_modify.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_drafts_permissions.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_drafts_permissions_set.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_drafts_publish.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_drafts_read.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_drafts_token.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_publish.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_published.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_search.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_api_objects_token.py (100%) rename {bco_api/api => api}/scripts/method_specific/POST_validate_payload_against_schema.py (100%) rename {bco_api/api => api}/scripts/method_specific/__init__.py (100%) rename {bco_api/api => api}/scripts/utilities/DbUtils.py (100%) rename {bco_api/api => api}/scripts/utilities/FileUtils.py (100%) rename {bco_api/api => api}/scripts/utilities/JsonUtils.py (100%) rename {bco_api/api => api}/scripts/utilities/RequestUtils.py (100%) rename {bco_api/api => api}/scripts/utilities/ResponseUtils.py (100%) rename {bco_api/api => api}/scripts/utilities/SettingsUtils.py (100%) rename {bco_api/api => api}/scripts/utilities/UserUtils.py (100%) rename {bco_api/api => api}/scripts/utilities/__init__.py (100%) rename {bco_api/api => api}/serializers.py (100%) rename {bco_api/api => api}/signals.py (100%) rename {bco_api/api => api}/templates/api/account_activation_message.html (100%) rename {bco_api/api => api}/tests/__init__.py (100%) rename {bco_api/api => api}/tests/test_bcos.json (100%) rename {bco_api/api => api}/tests/test_forms.py (100%) rename {bco_api/api => api}/tests/test_group_post_api.py (100%) rename {bco_api/api => api}/tests/test_model_bco.py (100%) rename {bco_api/api => api}/tests/test_model_groups.py (100%) rename {bco_api/api => api}/tests/test_model_prefix.py (100%) rename {bco_api/api => api}/tests/test_model_user.py (100%) rename {bco_api/api => api}/tests/test_prefix_post_api.py (100%) rename {bco_api/api => api}/tests/test_views.py (100%) rename {bco_api/api => api}/tests_automated.py (100%) rename {bco_api/api => api}/urls.py (100%) rename {bco_api/api => api}/validation_definitions/IEEE/2791object.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE/description_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE/error_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE/execution_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE/io_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE/parametric_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE/provenance_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE/usability_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE_sub/IEEE2791-2020.schema (100%) rename {bco_api/api => api}/validation_definitions/IEEE_sub/domains/description_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE_sub/domains/error_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE_sub/domains/execution_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE_sub/domains/io_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE_sub/domains/parametric_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE_sub/domains/provenance_domain.json (100%) rename {bco_api/api => api}/validation_definitions/IEEE_sub/domains/usability_domain.json (100%) rename {bco_api/api => api}/validation_definitions/uri_external (100%) rename {bco_api/api => api}/views.py (99%) rename {bco_api/bco_api => authentication}/__init__.py (100%) mode change 100755 => 100644 create mode 100644 authentication/admin.py create mode 100644 authentication/apis.py create mode 100644 authentication/apps.py create mode 100644 authentication/migrations/0001_initial.py rename bco_api/main => authentication/migrations/__init__.py (100%) mode change 100755 => 100644 create mode 100644 authentication/models.py create mode 100644 authentication/selectors.py create mode 100644 authentication/services.py create mode 100644 authentication/urls.py delete mode 100644 bco_api/api/templates/api/.DS_Store create mode 100755 bcodb/__init__.py rename {bco_api/bco_api => bcodb}/asgi.py (100%) rename {bco_api/bco_api => bcodb}/settings.py (92%) rename {bco_api/bco_api => bcodb}/urls.py (50%) rename {bco_api/bco_api => bcodb}/wsgi.py (82%) rename bco_api/manage.py => manage.py (89%) rename bco_api/server.conf => server.conf (100%) diff --git a/bco_api/api/__init__.py b/api/__init__.py similarity index 100% rename from bco_api/api/__init__.py rename to api/__init__.py diff --git a/bco_api/api/admin.py b/api/admin.py similarity index 100% rename from bco_api/api/admin.py rename to api/admin.py diff --git a/bco_api/api/apps.py b/api/apps.py similarity index 100% rename from bco_api/api/apps.py rename to api/apps.py diff --git a/bco_api/api/fixtures/bootstrap.json b/api/fixtures/bootstrap.json similarity index 100% rename from bco_api/api/fixtures/bootstrap.json rename to api/fixtures/bootstrap.json diff --git a/bco_api/api/fixtures/metafixtures b/api/fixtures/metafixtures similarity index 100% rename from bco_api/api/fixtures/metafixtures rename to api/fixtures/metafixtures diff --git a/bco_api/api/fixtures/metafixtures.json b/api/fixtures/metafixtures.json similarity index 100% rename from bco_api/api/fixtures/metafixtures.json rename to api/fixtures/metafixtures.json diff --git a/bco_api/api/keys.sh b/api/keys.sh similarity index 100% rename from bco_api/api/keys.sh rename to api/keys.sh diff --git a/bco_api/api/migrations/0001_initial.py b/api/migrations/0001_initial.py similarity index 100% rename from bco_api/api/migrations/0001_initial.py rename to api/migrations/0001_initial.py diff --git a/bco_api/api/migrations/0002_auto_20220124_2356.py b/api/migrations/0002_auto_20220124_2356.py similarity index 100% rename from bco_api/api/migrations/0002_auto_20220124_2356.py rename to api/migrations/0002_auto_20220124_2356.py diff --git a/bco_api/api/migrations/0003_rename_meta_table_prefix_table.py b/api/migrations/0003_rename_meta_table_prefix_table.py similarity index 100% rename from bco_api/api/migrations/0003_rename_meta_table_prefix_table.py rename to api/migrations/0003_rename_meta_table_prefix_table.py diff --git a/bco_api/api/migrations/0004_rename_group_info_groupinfo.py b/api/migrations/0004_rename_group_info_groupinfo.py similarity index 100% rename from bco_api/api/migrations/0004_rename_group_info_groupinfo.py rename to api/migrations/0004_rename_group_info_groupinfo.py diff --git a/bco_api/api/migrations/0005_rename_prefixes_prefix.py b/api/migrations/0005_rename_prefixes_prefix.py similarity index 100% rename from bco_api/api/migrations/0005_rename_prefixes_prefix.py rename to api/migrations/0005_rename_prefixes_prefix.py diff --git a/bco_api/api/migrations/__init__.py b/api/migrations/__init__.py similarity index 100% rename from bco_api/api/migrations/__init__.py rename to api/migrations/__init__.py diff --git a/bco_api/api/model/__init__.py b/api/model/__init__.py similarity index 100% rename from bco_api/api/model/__init__.py rename to api/model/__init__.py diff --git a/bco_api/api/model/groups.py b/api/model/groups.py similarity index 99% rename from bco_api/api/model/groups.py rename to api/model/groups.py index 7c4e1571..b0e66a50 100644 --- a/bco_api/api/model/groups.py +++ b/api/model/groups.py @@ -434,7 +434,7 @@ def associate_user_group(sender, instance, created, **kwargs): Group.objects.create(name=instance) group = Group.objects.get(name=instance) group.user_set.add(instance) - if instance.username not in ["anon", "bco_drafter", "bco_publisher"]: + if instance.username not in ["anon", "bco_drafter", "bco_publisher", "AnonymousUser"]: User.objects.get(username=instance).groups.add( Group.objects.get(name="bco_drafter") ) diff --git a/bco_api/api/model/prefix.py b/api/model/prefix.py similarity index 100% rename from bco_api/api/model/prefix.py rename to api/model/prefix.py diff --git a/bco_api/api/models.py b/api/models.py similarity index 100% rename from bco_api/api/models.py rename to api/models.py diff --git a/bco_api/api/permissions.py b/api/permissions.py similarity index 100% rename from bco_api/api/permissions.py rename to api/permissions.py diff --git a/bco_api/api/rdb.sh b/api/rdb.sh similarity index 100% rename from bco_api/api/rdb.sh rename to api/rdb.sh diff --git a/bco_api/api/request_definitions/GET.schema b/api/request_definitions/GET.schema similarity index 100% rename from bco_api/api/request_definitions/GET.schema rename to api/request_definitions/GET.schema diff --git a/bco_api/api/request_definitions/POST.schema b/api/request_definitions/POST.schema similarity index 100% rename from bco_api/api/request_definitions/POST.schema rename to api/request_definitions/POST.schema diff --git a/bco_api/api/request_definitions/templates/DELETE_delete_object_by_id.schema b/api/request_definitions/templates/DELETE_delete_object_by_id.schema similarity index 100% rename from bco_api/api/request_definitions/templates/DELETE_delete_object_by_id.schema rename to api/request_definitions/templates/DELETE_delete_object_by_id.schema diff --git a/bco_api/api/request_definitions/templates/GET_activate_account.schema b/api/request_definitions/templates/GET_activate_account.schema similarity index 100% rename from bco_api/api/request_definitions/templates/GET_activate_account.schema rename to api/request_definitions/templates/GET_activate_account.schema diff --git a/bco_api/api/request_definitions/templates/GET_get_object_by_id.schema b/api/request_definitions/templates/GET_get_object_by_id.schema similarity index 100% rename from bco_api/api/request_definitions/templates/GET_get_object_by_id.schema rename to api/request_definitions/templates/GET_get_object_by_id.schema diff --git a/bco_api/api/request_definitions/templates/GET_retrieve_available_schema.schema b/api/request_definitions/templates/GET_retrieve_available_schema.schema similarity index 100% rename from bco_api/api/request_definitions/templates/GET_retrieve_available_schema.schema rename to api/request_definitions/templates/GET_retrieve_available_schema.schema diff --git a/bco_api/api/request_definitions/templates/POST_convert_existing_object_between_schemas.schema b/api/request_definitions/templates/POST_convert_existing_object_between_schemas.schema similarity index 100% rename from bco_api/api/request_definitions/templates/POST_convert_existing_object_between_schemas.schema rename to api/request_definitions/templates/POST_convert_existing_object_between_schemas.schema diff --git a/bco_api/api/request_definitions/templates/POST_convert_payload_to_schema.schema b/api/request_definitions/templates/POST_convert_payload_to_schema.schema similarity index 100% rename from bco_api/api/request_definitions/templates/POST_convert_payload_to_schema.schema rename to api/request_definitions/templates/POST_convert_payload_to_schema.schema diff --git a/bco_api/api/request_definitions/templates/POST_new_account.schema b/api/request_definitions/templates/POST_new_account.schema similarity index 100% rename from bco_api/api/request_definitions/templates/POST_new_account.schema rename to api/request_definitions/templates/POST_new_account.schema diff --git a/bco_api/api/request_definitions/templates/POST_object_listing_by_token.schema b/api/request_definitions/templates/POST_object_listing_by_token.schema similarity index 100% rename from bco_api/api/request_definitions/templates/POST_object_listing_by_token.schema rename to api/request_definitions/templates/POST_object_listing_by_token.schema diff --git a/bco_api/api/request_definitions/templates/POST_objects_draft.schema b/api/request_definitions/templates/POST_objects_draft.schema similarity index 100% rename from bco_api/api/request_definitions/templates/POST_objects_draft.schema rename to api/request_definitions/templates/POST_objects_draft.schema diff --git a/bco_api/api/request_definitions/templates/POST_objects_publish.schema b/api/request_definitions/templates/POST_objects_publish.schema similarity index 100% rename from bco_api/api/request_definitions/templates/POST_objects_publish.schema rename to api/request_definitions/templates/POST_objects_publish.schema diff --git a/bco_api/api/request_definitions/templates/POST_read_object.schema b/api/request_definitions/templates/POST_read_object.schema similarity index 100% rename from bco_api/api/request_definitions/templates/POST_read_object.schema rename to api/request_definitions/templates/POST_read_object.schema diff --git a/bco_api/api/request_definitions/templates/POST_validate_payload_against_schema.schema b/api/request_definitions/templates/POST_validate_payload_against_schema.schema similarity index 100% rename from bco_api/api/request_definitions/templates/POST_validate_payload_against_schema.schema rename to api/request_definitions/templates/POST_validate_payload_against_schema.schema diff --git a/bco_api/api/scripts/__init__.py b/api/scripts/__init__.py similarity index 100% rename from bco_api/api/scripts/__init__.py rename to api/scripts/__init__.py diff --git a/bco_api/api/scripts/method_specific/GET_activate_account.py b/api/scripts/method_specific/GET_activate_account.py similarity index 100% rename from bco_api/api/scripts/method_specific/GET_activate_account.py rename to api/scripts/method_specific/GET_activate_account.py diff --git a/bco_api/api/scripts/method_specific/GET_draft_object_by_id.py b/api/scripts/method_specific/GET_draft_object_by_id.py similarity index 100% rename from bco_api/api/scripts/method_specific/GET_draft_object_by_id.py rename to api/scripts/method_specific/GET_draft_object_by_id.py diff --git a/bco_api/api/scripts/method_specific/GET_published_object_by_id.py b/api/scripts/method_specific/GET_published_object_by_id.py similarity index 100% rename from bco_api/api/scripts/method_specific/GET_published_object_by_id.py rename to api/scripts/method_specific/GET_published_object_by_id.py diff --git a/bco_api/api/scripts/method_specific/GET_published_object_by_id_with_version.py b/api/scripts/method_specific/GET_published_object_by_id_with_version.py similarity index 100% rename from bco_api/api/scripts/method_specific/GET_published_object_by_id_with_version.py rename to api/scripts/method_specific/GET_published_object_by_id_with_version.py diff --git a/bco_api/api/scripts/method_specific/GET_retrieve_available_schema.py b/api/scripts/method_specific/GET_retrieve_available_schema.py similarity index 100% rename from bco_api/api/scripts/method_specific/GET_retrieve_available_schema.py rename to api/scripts/method_specific/GET_retrieve_available_schema.py diff --git a/bco_api/api/scripts/method_specific/POST_api_accounts_describe.py b/api/scripts/method_specific/POST_api_accounts_describe.py similarity index 99% rename from bco_api/api/scripts/method_specific/POST_api_accounts_describe.py rename to api/scripts/method_specific/POST_api_accounts_describe.py index 40a662c0..aca72878 100755 --- a/bco_api/api/scripts/method_specific/POST_api_accounts_describe.py +++ b/api/scripts/method_specific/POST_api_accounts_describe.py @@ -20,7 +20,6 @@ def POST_api_accounts_describe(token): # Instantiate UserUtils uu = UserUtils.UserUtils() - # Get the user's information return Response( data=uu.get_user_info(username=Token.objects.get(key=processed).user.username), diff --git a/bco_api/api/scripts/method_specific/POST_api_accounts_new.py b/api/scripts/method_specific/POST_api_accounts_new.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_accounts_new.py rename to api/scripts/method_specific/POST_api_accounts_new.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_drafts_create.py b/api/scripts/method_specific/POST_api_objects_drafts_create.py similarity index 94% rename from bco_api/api/scripts/method_specific/POST_api_objects_drafts_create.py rename to api/scripts/method_specific/POST_api_objects_drafts_create.py index d9aec941..b9a74d27 100755 --- a/bco_api/api/scripts/method_specific/POST_api_objects_drafts_create.py +++ b/api/scripts/method_specific/POST_api_objects_drafts_create.py @@ -11,9 +11,9 @@ from django.conf import settings from django.contrib.auth.models import Group from django.utils import timezone -from rest_framework import status +from rest_framework import status, authtoken from rest_framework.response import Response - +from authentication.selectors import get_user_from_auth_token def post_api_objects_drafts_create(request): """Create BCO Draft @@ -31,7 +31,10 @@ def post_api_objects_drafts_create(request): """ db_utils = DbUtils.DbUtils() - user = UserUtils.UserUtils().user_from_request(request=request) + try: + user = UserUtils.UserUtils().user_from_request(request=request) + except authtoken.models.Token.DoesNotExist: + user = get_user_from_auth_token(request.META.get("HTTP_AUTHORIZATION").split(" ")[1]) prefix_perms = UserUtils.UserUtils().prefix_perms_for_user( flatten=True, user_object=user, specific_permission=["add"] ) diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_drafts_delete.py b/api/scripts/method_specific/POST_api_objects_drafts_delete.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_drafts_delete.py rename to api/scripts/method_specific/POST_api_objects_drafts_delete.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_drafts_modify.py b/api/scripts/method_specific/POST_api_objects_drafts_modify.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_drafts_modify.py rename to api/scripts/method_specific/POST_api_objects_drafts_modify.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_drafts_permissions.py b/api/scripts/method_specific/POST_api_objects_drafts_permissions.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_drafts_permissions.py rename to api/scripts/method_specific/POST_api_objects_drafts_permissions.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_drafts_permissions_set.py b/api/scripts/method_specific/POST_api_objects_drafts_permissions_set.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_drafts_permissions_set.py rename to api/scripts/method_specific/POST_api_objects_drafts_permissions_set.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_drafts_publish.py b/api/scripts/method_specific/POST_api_objects_drafts_publish.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_drafts_publish.py rename to api/scripts/method_specific/POST_api_objects_drafts_publish.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_drafts_read.py b/api/scripts/method_specific/POST_api_objects_drafts_read.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_drafts_read.py rename to api/scripts/method_specific/POST_api_objects_drafts_read.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_drafts_token.py b/api/scripts/method_specific/POST_api_objects_drafts_token.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_drafts_token.py rename to api/scripts/method_specific/POST_api_objects_drafts_token.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_publish.py b/api/scripts/method_specific/POST_api_objects_publish.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_publish.py rename to api/scripts/method_specific/POST_api_objects_publish.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_published.py b/api/scripts/method_specific/POST_api_objects_published.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_published.py rename to api/scripts/method_specific/POST_api_objects_published.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_search.py b/api/scripts/method_specific/POST_api_objects_search.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_search.py rename to api/scripts/method_specific/POST_api_objects_search.py diff --git a/bco_api/api/scripts/method_specific/POST_api_objects_token.py b/api/scripts/method_specific/POST_api_objects_token.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_api_objects_token.py rename to api/scripts/method_specific/POST_api_objects_token.py diff --git a/bco_api/api/scripts/method_specific/POST_validate_payload_against_schema.py b/api/scripts/method_specific/POST_validate_payload_against_schema.py similarity index 100% rename from bco_api/api/scripts/method_specific/POST_validate_payload_against_schema.py rename to api/scripts/method_specific/POST_validate_payload_against_schema.py diff --git a/bco_api/api/scripts/method_specific/__init__.py b/api/scripts/method_specific/__init__.py similarity index 100% rename from bco_api/api/scripts/method_specific/__init__.py rename to api/scripts/method_specific/__init__.py diff --git a/bco_api/api/scripts/utilities/DbUtils.py b/api/scripts/utilities/DbUtils.py similarity index 100% rename from bco_api/api/scripts/utilities/DbUtils.py rename to api/scripts/utilities/DbUtils.py diff --git a/bco_api/api/scripts/utilities/FileUtils.py b/api/scripts/utilities/FileUtils.py similarity index 100% rename from bco_api/api/scripts/utilities/FileUtils.py rename to api/scripts/utilities/FileUtils.py diff --git a/bco_api/api/scripts/utilities/JsonUtils.py b/api/scripts/utilities/JsonUtils.py similarity index 100% rename from bco_api/api/scripts/utilities/JsonUtils.py rename to api/scripts/utilities/JsonUtils.py diff --git a/bco_api/api/scripts/utilities/RequestUtils.py b/api/scripts/utilities/RequestUtils.py similarity index 100% rename from bco_api/api/scripts/utilities/RequestUtils.py rename to api/scripts/utilities/RequestUtils.py diff --git a/bco_api/api/scripts/utilities/ResponseUtils.py b/api/scripts/utilities/ResponseUtils.py similarity index 100% rename from bco_api/api/scripts/utilities/ResponseUtils.py rename to api/scripts/utilities/ResponseUtils.py diff --git a/bco_api/api/scripts/utilities/SettingsUtils.py b/api/scripts/utilities/SettingsUtils.py similarity index 100% rename from bco_api/api/scripts/utilities/SettingsUtils.py rename to api/scripts/utilities/SettingsUtils.py diff --git a/bco_api/api/scripts/utilities/UserUtils.py b/api/scripts/utilities/UserUtils.py similarity index 100% rename from bco_api/api/scripts/utilities/UserUtils.py rename to api/scripts/utilities/UserUtils.py diff --git a/bco_api/api/scripts/utilities/__init__.py b/api/scripts/utilities/__init__.py similarity index 100% rename from bco_api/api/scripts/utilities/__init__.py rename to api/scripts/utilities/__init__.py diff --git a/bco_api/api/serializers.py b/api/serializers.py similarity index 100% rename from bco_api/api/serializers.py rename to api/serializers.py diff --git a/bco_api/api/signals.py b/api/signals.py similarity index 100% rename from bco_api/api/signals.py rename to api/signals.py diff --git a/bco_api/api/templates/api/account_activation_message.html b/api/templates/api/account_activation_message.html similarity index 100% rename from bco_api/api/templates/api/account_activation_message.html rename to api/templates/api/account_activation_message.html diff --git a/bco_api/api/tests/__init__.py b/api/tests/__init__.py similarity index 100% rename from bco_api/api/tests/__init__.py rename to api/tests/__init__.py diff --git a/bco_api/api/tests/test_bcos.json b/api/tests/test_bcos.json similarity index 100% rename from bco_api/api/tests/test_bcos.json rename to api/tests/test_bcos.json diff --git a/bco_api/api/tests/test_forms.py b/api/tests/test_forms.py similarity index 100% rename from bco_api/api/tests/test_forms.py rename to api/tests/test_forms.py diff --git a/bco_api/api/tests/test_group_post_api.py b/api/tests/test_group_post_api.py similarity index 100% rename from bco_api/api/tests/test_group_post_api.py rename to api/tests/test_group_post_api.py diff --git a/bco_api/api/tests/test_model_bco.py b/api/tests/test_model_bco.py similarity index 100% rename from bco_api/api/tests/test_model_bco.py rename to api/tests/test_model_bco.py diff --git a/bco_api/api/tests/test_model_groups.py b/api/tests/test_model_groups.py similarity index 100% rename from bco_api/api/tests/test_model_groups.py rename to api/tests/test_model_groups.py diff --git a/bco_api/api/tests/test_model_prefix.py b/api/tests/test_model_prefix.py similarity index 100% rename from bco_api/api/tests/test_model_prefix.py rename to api/tests/test_model_prefix.py diff --git a/bco_api/api/tests/test_model_user.py b/api/tests/test_model_user.py similarity index 100% rename from bco_api/api/tests/test_model_user.py rename to api/tests/test_model_user.py diff --git a/bco_api/api/tests/test_prefix_post_api.py b/api/tests/test_prefix_post_api.py similarity index 100% rename from bco_api/api/tests/test_prefix_post_api.py rename to api/tests/test_prefix_post_api.py diff --git a/bco_api/api/tests/test_views.py b/api/tests/test_views.py similarity index 100% rename from bco_api/api/tests/test_views.py rename to api/tests/test_views.py diff --git a/bco_api/api/tests_automated.py b/api/tests_automated.py similarity index 100% rename from bco_api/api/tests_automated.py rename to api/tests_automated.py diff --git a/bco_api/api/urls.py b/api/urls.py similarity index 100% rename from bco_api/api/urls.py rename to api/urls.py diff --git a/bco_api/api/validation_definitions/IEEE/2791object.json b/api/validation_definitions/IEEE/2791object.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE/2791object.json rename to api/validation_definitions/IEEE/2791object.json diff --git a/bco_api/api/validation_definitions/IEEE/description_domain.json b/api/validation_definitions/IEEE/description_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE/description_domain.json rename to api/validation_definitions/IEEE/description_domain.json diff --git a/bco_api/api/validation_definitions/IEEE/error_domain.json b/api/validation_definitions/IEEE/error_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE/error_domain.json rename to api/validation_definitions/IEEE/error_domain.json diff --git a/bco_api/api/validation_definitions/IEEE/execution_domain.json b/api/validation_definitions/IEEE/execution_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE/execution_domain.json rename to api/validation_definitions/IEEE/execution_domain.json diff --git a/bco_api/api/validation_definitions/IEEE/io_domain.json b/api/validation_definitions/IEEE/io_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE/io_domain.json rename to api/validation_definitions/IEEE/io_domain.json diff --git a/bco_api/api/validation_definitions/IEEE/parametric_domain.json b/api/validation_definitions/IEEE/parametric_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE/parametric_domain.json rename to api/validation_definitions/IEEE/parametric_domain.json diff --git a/bco_api/api/validation_definitions/IEEE/provenance_domain.json b/api/validation_definitions/IEEE/provenance_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE/provenance_domain.json rename to api/validation_definitions/IEEE/provenance_domain.json diff --git a/bco_api/api/validation_definitions/IEEE/usability_domain.json b/api/validation_definitions/IEEE/usability_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE/usability_domain.json rename to api/validation_definitions/IEEE/usability_domain.json diff --git a/bco_api/api/validation_definitions/IEEE_sub/IEEE2791-2020.schema b/api/validation_definitions/IEEE_sub/IEEE2791-2020.schema similarity index 100% rename from bco_api/api/validation_definitions/IEEE_sub/IEEE2791-2020.schema rename to api/validation_definitions/IEEE_sub/IEEE2791-2020.schema diff --git a/bco_api/api/validation_definitions/IEEE_sub/domains/description_domain.json b/api/validation_definitions/IEEE_sub/domains/description_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE_sub/domains/description_domain.json rename to api/validation_definitions/IEEE_sub/domains/description_domain.json diff --git a/bco_api/api/validation_definitions/IEEE_sub/domains/error_domain.json b/api/validation_definitions/IEEE_sub/domains/error_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE_sub/domains/error_domain.json rename to api/validation_definitions/IEEE_sub/domains/error_domain.json diff --git a/bco_api/api/validation_definitions/IEEE_sub/domains/execution_domain.json b/api/validation_definitions/IEEE_sub/domains/execution_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE_sub/domains/execution_domain.json rename to api/validation_definitions/IEEE_sub/domains/execution_domain.json diff --git a/bco_api/api/validation_definitions/IEEE_sub/domains/io_domain.json b/api/validation_definitions/IEEE_sub/domains/io_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE_sub/domains/io_domain.json rename to api/validation_definitions/IEEE_sub/domains/io_domain.json diff --git a/bco_api/api/validation_definitions/IEEE_sub/domains/parametric_domain.json b/api/validation_definitions/IEEE_sub/domains/parametric_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE_sub/domains/parametric_domain.json rename to api/validation_definitions/IEEE_sub/domains/parametric_domain.json diff --git a/bco_api/api/validation_definitions/IEEE_sub/domains/provenance_domain.json b/api/validation_definitions/IEEE_sub/domains/provenance_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE_sub/domains/provenance_domain.json rename to api/validation_definitions/IEEE_sub/domains/provenance_domain.json diff --git a/bco_api/api/validation_definitions/IEEE_sub/domains/usability_domain.json b/api/validation_definitions/IEEE_sub/domains/usability_domain.json similarity index 100% rename from bco_api/api/validation_definitions/IEEE_sub/domains/usability_domain.json rename to api/validation_definitions/IEEE_sub/domains/usability_domain.json diff --git a/bco_api/api/validation_definitions/uri_external b/api/validation_definitions/uri_external similarity index 100% rename from bco_api/api/validation_definitions/uri_external rename to api/validation_definitions/uri_external diff --git a/bco_api/api/views.py b/api/views.py similarity index 99% rename from bco_api/api/views.py rename to api/views.py index e7163bc3..adb1386c 100755 --- a/bco_api/api/views.py +++ b/api/views.py @@ -4,6 +4,8 @@ Django views for BCODB API """ +import jwt +from django.contrib.auth.models import User from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema from rest_framework import status @@ -11,6 +13,7 @@ from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework.authtoken.models import Token from api.permissions import RequestorInPrefixAdminsGroup from api.scripts.method_specific.GET_activate_account import GET_activate_account from api.scripts.method_specific.GET_draft_object_by_id import get_draft_object_by_id @@ -228,10 +231,17 @@ def post(self, request): Pass the request to the handling function Source: https://stackoverflow.com/a/31813810 """ - if "Authorization" in request.headers: + + if request.headers["Authorization"].split(" ")[0] == "Token": return POST_api_accounts_describe( token=request.META.get("HTTP_AUTHORIZATION") ) + if request.headers["Authorization"].split(" ")[0] == "Bearer": + jw_token=request.META.get("HTTP_AUTHORIZATION").split(" ")[1] + unverified_payload = jwt.decode(jw_token, None, False) + user = User.objects.get(email=unverified_payload['email']) + token = "Thing "+ str(Token.objects.get(user=user)) + return POST_api_accounts_describe(token) else: return Response(status=status.HTTP_400_BAD_REQUEST) diff --git a/bco_api/bco_api/__init__.py b/authentication/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from bco_api/bco_api/__init__.py rename to authentication/__init__.py diff --git a/authentication/admin.py b/authentication/admin.py new file mode 100644 index 00000000..c3776a21 --- /dev/null +++ b/authentication/admin.py @@ -0,0 +1,7 @@ +"""Authentication Admin Pannel +""" + +from django.contrib import admin +from authentication.models import Authentication + +admin.site.register(Authentication) \ No newline at end of file diff --git a/authentication/apis.py b/authentication/apis.py new file mode 100644 index 00000000..f302943a --- /dev/null +++ b/authentication/apis.py @@ -0,0 +1,51 @@ +# authentication/apis.py + +import json +from django.contrib.auth.models import User +from rest_framework import status, serializers +from rest_framework.response import Response +from rest_framework.views import APIView +from authentication.selectors import check_user_email, get_user_info +from authentication.services import validate_token, create_bcodb, send_bcodb +from authentication.models import Authentication + +class RegisterBcodbAPI(APIView): + """ + """ + + class InputSerializer(serializers.Serializer): + hostname= serializers.URLField() + email = serializers.EmailField() + token = serializers.CharField() + + class Meta: + model = User + fields = ["__all__"] + + authentication_classes = [] + permission_classes = [] + + def post(self, request): + """ + """ + user_info = self.InputSerializer(data=request.data) + user_info.is_valid(raise_exception=True) + token = user_info.validated_data['token'] + url = user_info.validated_data['hostname'] + if validate_token(token, url) is False: + return Response(status=status.HTTP_401_UNAUTHORIZED, data={"message": "portal authentication was invalid"}) + if check_user_email(user_info.validated_data['email']) is True: + return Response( + status=status.HTTP_409_CONFLICT, + data={"message": "A BCODB account with that email already exists"} + ) + user = create_bcodb(user_info=user_info.validated_data) + data = json.dumps(get_user_info(user), default=str) + response = send_bcodb( + data=data, request_info=user_info.validated_data + ) + if response.status_code == 200: + return Response(status=status.HTTP_201_CREATED, data={"message": "user account created"}) + + + diff --git a/authentication/apps.py b/authentication/apps.py new file mode 100644 index 00000000..6659dda0 --- /dev/null +++ b/authentication/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class Authentication(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "authentication" diff --git a/authentication/migrations/0001_initial.py b/authentication/migrations/0001_initial.py new file mode 100644 index 00000000..575366d2 --- /dev/null +++ b/authentication/migrations/0001_initial.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.10 on 2023-03-27 20:46 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Authentication', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('auth_service', models.JSONField(default=list)), + ('username', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='username')), + ], + ), + ] diff --git a/bco_api/main b/authentication/migrations/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from bco_api/main rename to authentication/migrations/__init__.py diff --git a/authentication/models.py b/authentication/models.py new file mode 100644 index 00000000..57f4037f --- /dev/null +++ b/authentication/models.py @@ -0,0 +1,13 @@ +import json +from django.db import models +from django.contrib.auth.models import User + +class Authentication(models.Model): + """""" + username = models.ForeignKey(User, on_delete=models.CASCADE, to_field="username") + auth_service = models.JSONField(default=list) + + + def __username__(self): + """String for representing the model in Admin site.""" + return str(self.username) diff --git a/authentication/selectors.py b/authentication/selectors.py new file mode 100644 index 00000000..a5010876 --- /dev/null +++ b/authentication/selectors.py @@ -0,0 +1,78 @@ +# authentication/selectors.py + +import jwt +from django.conf import settings +from django.contrib.auth.models import User, Permission +from authentication.models import Authentication +from rest_framework.authtoken.models import Token + +def get_user_from_auth_token(token: str)-> User: + """Get user from Auth Token + """ + payload = jwt.decode(token, None, False) + + if payload['iss'] == 'https://orcid.org' or payload['iss'] == 'https://sandbox.orcid.org': + try: + return User.objects.get(username=Authentication.objects.get(auth_service__icontains=payload['iss']).username) + except User.DoesNotExist: + return None + if payload['iss'] == 'accounts.google.com': + try: + return User.objects.get(email=payload['email']) + except User.DoesNotExist: + return None + +def check_user_email(email: str)-> bool: + """Check for user + + Using the provided email check for a user in the DB + """ + try: + if User.objects.get(email=email): + return True + except User.DoesNotExist: + return False + +def get_user_info(user: User) -> dict: + """Get User Info + + Arguments + --------- + user: the user object. + + Returns + ------- + A dict with the user information. + """ + + token = Token.objects.get(user=user.pk) + other_info = { + "permissions": {}, + "account_creation": "", + "account_expiration": "", + } + user_perms = {"user": [], "groups": []} + + for permission in user.user_permissions.all(): + if permission.name not in user_perms["user"]: + user_perms["user"].append(permission.name) + + for group in user.groups.all(): + if group.name not in user_perms["groups"]: + user_perms["groups"].append(group.name) + for permission in Permission.objects.filter(group=group): + if permission.name not in user_perms["user"]: + user_perms["user"].append(permission.name) + + other_info["permissions"] = user_perms + + other_info["account_creation"] = user.date_joined + + return { + "hostname": settings.ALLOWED_HOSTS[0], + "human_readable_hostname": settings.HUMAN_READABLE_HOSTNAME, + "public_hostname": settings.PUBLIC_HOSTNAME, + "token": token.key, + "username": user.username, + "other_info": other_info, + } \ No newline at end of file diff --git a/authentication/services.py b/authentication/services.py new file mode 100644 index 00000000..06def140 --- /dev/null +++ b/authentication/services.py @@ -0,0 +1,174 @@ +# authenticaton/services.api + +import jwt +import json +import requests +from django.contrib.auth.models import User, Group +from rest_framework import exceptions, status, serializers +from rest_framework.response import Response +from rest_framework_jwt.authentication import BaseAuthentication +from rest_framework_jwt.settings import api_settings +from rest_framework_jwt.utils import jwt_get_secret_key +from google.oauth2 import id_token +from google.auth.transport import requests as g_requests +from authentication.selectors import check_user_email +from authentication.models import Authentication +jwt_decode_handler = api_settings.JWT_DECODE_HANDLER + +class AuthInputSerializer(serializers.ModelSerializer): + auth_service = serializers.JSONField() + + class Meta: + model = Authentication + fields = ('username', 'auth_service') + + def validate_auth_service(self, value): + for auth in value: + if 'sub' not in auth or 'iss' not in auth: + raise serializers.ValidationError('Each item in auth_service', + ' list must be in the form of {"sub": "###########", "iss":', + ' "http://example.org"}') + return value + + def create(self, validated_data): + auth_service = validated_data.pop('auth_service') + instance = super().create(validated_data) + instance.auth_service = auth_service + instance.save() + return instance + +class CustomJSONWebTokenAuthentication(BaseAuthentication): + + def authenticate(self, request): + if 'Authorization' in request.headers: + type, token = request.headers['Authorization'].split(' ') + if type == 'Bearer': + try: + unverified_payload = jwt.decode(token, None, False) + except Exception as exp: + raise exceptions.AuthenticationFailed(exp) + + if unverified_payload['iss'] == 'https://orcid.org' or unverified_payload['iss'] == 'https://sandbox.orcid.org': + user = authenticate_orcid(unverified_payload, token) + if unverified_payload['iss'] == 'accounts.google.com': + user = authenticate_google(token) + if unverified_payload['iss'] in ['http://localhost:8080', 'https://test.portal.biochemistry.gwu.edu/', 'https://biocomputeobject.org/']: + user = authenticate_portal(unverified_payload, token) + + return (user, token) + + if type == 'Token': + pass + pass + +def authenticate_portal(payload: dict, token:str)-> User: + """Authenticate Portal + Custom function to authenticate BCO Portal credentials. + """ + + response = requests.post( + payload['iss']+'/users/auth/verify/', json={"token":token} + ) + if response.status_code == 201: + try: + return User.objects.get(email=payload['email']) + except User.DoesNotExist: + return None + else: + exceptions.AuthenticationFailed(response.reason) + + +def authenticate_orcid(payload:dict, token:str)-> User: + """Authenticate ORCID + + Custom function to authenticate ORCID credentials. + """ + + orcid_jwks = { + jwk['kid']: json.dumps(jwk) + for jwk in requests.get(payload['iss']+'/oauth/jwks').json()['keys'] + } + orcid_jwk = next(iter(orcid_jwks.values())) + orcid_key = jwt.algorithms.RSAAlgorithm.from_jwk(orcid_jwk) + + try: + jwt.decode(token, key=orcid_key, algorithms=['RS256'], audience='APP-88DEA42BRILGEHKC') + except Exception as exp: + print('exp:', exp) + raise exceptions.AuthenticationFailed(exp) + try: + user = User.objects.get(username=Authentication.objects.get(auth_service__icontains=payload['iss']).username) + except (Authentication.DoesNotExist, User.DoesNotExist): + return None + return user + +def authenticate_google(token: str) -> bool: + """Authenticate Google + + Custom function to authenticate Google credentials. + """ + idinfo = id_token.verify_oauth2_token(token, g_requests.Request()) + try: + return User.objects.get(email=idinfo['email']) + except User.DoesNotExist: + return None + +def custom_jwt_handler(token, user=None, request=None, public_key=None): + """Custom JWT Handler + Triggered by any user authentication. This will gater all the associated + user information and return that along with the validated JWT + """ + + print('hadley', token) + return request + +def validate_token(token: str, url: str)-> bool: + """ + """ + + headers = {"Content-type": "application/json; charset=UTF-8",} + import pdb; pdb.set_trace + response = requests.post( + data=json.dumps({"token": token}, default=str), + headers=headers, + url=url+'auth/verify/', + ) + + if response.status_code != 201: + return False + return True + + +def create_bcodb(user_info: dict) -> User: + """Create BCODB user + """ + + username = user_info["email"].split("@")[0] + user = User.objects.create_user( + username=username, email=user_info["email"] + ) + user.set_unusable_password() + user.full_clean() + user.save() + user.groups.add(Group.objects.get(name="bco_drafter")) + user.groups.add(Group.objects.get(name="bco_publisher")) + + return user + + +def send_bcodb(data: str, request_info: dict): + """ + """ + + token = request_info['token'] + headers = { + "Authorization": f"Bearer {token}", + "Content-type": "application/json; charset=UTF-8", + } + + response = requests.post( + data=data, + headers=headers, + url=request_info['hostname']+'bcodb/add/', + ) + return response diff --git a/authentication/urls.py b/authentication/urls.py new file mode 100644 index 00000000..7f769d05 --- /dev/null +++ b/authentication/urls.py @@ -0,0 +1,8 @@ +# authentication/urls.py + +from django.urls import path +from authentication.apis import RegisterBcodbAPI + +urlpatterns = [ + path("auth/register/", RegisterBcodbAPI.as_view()) +] \ No newline at end of file diff --git a/bco_api/api/templates/api/.DS_Store b/bco_api/api/templates/api/.DS_Store deleted file mode 100644 index 519bb1a0f2a94204dcc983f8fa780d4f46b65f27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKu};G<5PdEkidZ_bkspAFKZvR#7!XSZYf0Kbfu@bx1i_Y1=bg_~RDlVE3c9Q8 zXP@t#o#!a70k}auJq7vzdMtvSeO8~Cj7w=*CwK&%&rxH63S*?0w4%+iiwelz9r^v5 zV2O|WSK$HA91&b{U&knrW6fWRd$vw6<5+=tS!I{?x>_;Q3?sZUqT+ag7&m0jnN^uF zhCW})k!wcfSh0@DN-5oEO60lKJI-H-GdBV&t+)$jKo&Ly&!zGGVn*yf5wgP+F?aTRp_VxXLn`Avxz!dmb z3b^iYG#v1e!r9t-I5}%0mRlAviE9)$6gK=(%vd>!4_Um?A4!9l1Z))Pq1hh+FN0O4 Iz^^Ls2E~hE!T Date: Tue, 28 Mar 2023 11:36:26 -0400 Subject: [PATCH 03/11] Fix app error Changes to be committed: renamed: bco_api/search/apis.py -> search/apis.py renamed: bco_api/search/selectors.py -> search/selectors.py renamed: bco_api/search/urls.py -> search/urls.py --- {bco_api/search => search}/apis.py | 0 {bco_api/search => search}/selectors.py | 0 {bco_api/search => search}/urls.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {bco_api/search => search}/apis.py (100%) rename {bco_api/search => search}/selectors.py (100%) rename {bco_api/search => search}/urls.py (100%) diff --git a/bco_api/search/apis.py b/search/apis.py similarity index 100% rename from bco_api/search/apis.py rename to search/apis.py diff --git a/bco_api/search/selectors.py b/search/selectors.py similarity index 100% rename from bco_api/search/selectors.py rename to search/selectors.py diff --git a/bco_api/search/urls.py b/search/urls.py similarity index 100% rename from bco_api/search/urls.py rename to search/urls.py From 106b4bacff2f02fd16039f777db993557e7e18b3 Mon Sep 17 00:00:00 2001 From: hadleyking Date: Mon, 10 Apr 2023 08:55:43 -0400 Subject: [PATCH 04/11] APIs for add and remove authentication Changes to be committed: modified: api/views.py modified: authentication/apis.py modified: authentication/services.py modified: authentication/urls.py --- api/views.py | 2 +- authentication/apis.py | 184 ++++++++++++++++++++++++++++++++++++- authentication/services.py | 43 ++++----- authentication/urls.py | 6 +- 4 files changed, 204 insertions(+), 31 deletions(-) diff --git a/api/views.py b/api/views.py index adb1386c..00d80e09 100755 --- a/api/views.py +++ b/api/views.py @@ -232,7 +232,7 @@ def post(self, request): Source: https://stackoverflow.com/a/31813810 """ - if request.headers["Authorization"].split(" ")[0] == "Token": + if request.headers["Authorization"].split(" ")[0] == "Token" or request.headers["Authorization"].split(" ")[0] == "TOKEN": return POST_api_accounts_describe( token=request.META.get("HTTP_AUTHORIZATION") ) diff --git a/authentication/apis.py b/authentication/apis.py index f302943a..a239cd7b 100644 --- a/authentication/apis.py +++ b/authentication/apis.py @@ -2,18 +2,35 @@ import json from django.contrib.auth.models import User +from drf_yasg import openapi +from drf_yasg.utils import swagger_auto_schema from rest_framework import status, serializers +from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from authentication.selectors import check_user_email, get_user_info -from authentication.services import validate_token, create_bcodb, send_bcodb +from authentication.services import validate_token, create_bcodb, send_bcodb, validate_auth_service from authentication.models import Authentication class RegisterBcodbAPI(APIView): - """ + """Register BCODB + API View to register a new BCODB user. + + Methods: + post(request): Register a new BCODB user. + + Attributes: + InputSerializer: Serializer class for validating input data. """ class InputSerializer(serializers.Serializer): + """Serializer class for validating input data for registering a new BCODB user. + + Fields: + hostname (str): The URL of the BCODB portal. + email (str): The email address of the user to register. + token (str): The authentication token for the BCODB portal. + """ hostname= serializers.URLField() email = serializers.EmailField() token = serializers.CharField() @@ -26,8 +43,15 @@ class Meta: permission_classes = [] def post(self, request): + """Register a new BCODB user. + + Args: + request (Request): The request object containing the input data. + + Returns: + Response: A HTTP response indicating the result of the registration attempt. """ - """ + user_info = self.InputSerializer(data=request.data) user_info.is_valid(raise_exception=True) token = user_info.validated_data['token'] @@ -45,7 +69,159 @@ def post(self, request): data=data, request_info=user_info.validated_data ) if response.status_code == 200: - return Response(status=status.HTTP_201_CREATED, data={"message": "user account created"}) + return Response(status=status.HTTP_201_CREATED, data={"message": "user account created"}) + +class AuthenticationInputSerializer(serializers.Serializer): + auth_service = serializers.JSONField(validators=[validate_auth_service]) + + class Meta: + model = Authentication + fields = ['username', 'auth_service'] + + +class AddAuthenticationApi(APIView): + """ + Add Authentication Object + + ----------------------------- + + Adds an authentication dictionary to the list of auth_objects for a user + + ```JSON + { + "sub": "0000-0000-0000-0000", + "iss": "https://example.org" + } + ``` + """ + + permission_classes = [IsAuthenticated,] + + schema = openapi.Schema( + type=openapi.TYPE_OBJECT, + title="Add Authentication", + description="Adds an authentication objetc to the associated user", + required=["iss", "sub"], + properties={ + "iss": openapi.Schema( + type=openapi.TYPE_STRING, + description="The 'iss' (issuer) claim identifies the principal" + " that issued the JWT." + ), + "sub": openapi.Schema( + type=openapi.TYPE_STRING, + description="The 'sub' (subject) claim identifies the" + " principal that is the subject of the JWT.", + + ) + } + + ) + @swagger_auto_schema( + request_body=schema, + responses={ + 200: "Add authentication is successful.", + 201: "Authentication credentials were created and added.", + 400: "Bad request.", + 409: "That object already exists for this account.", + }, + tags=["Authentication"], + ) + def post(self, request): + """ + """ + result = validate_auth_service(request.data) + + if result is not 1: + return Response(status=status.HTTP_400_BAD_REQUEST, data=result) + try: + auth_object = Authentication.objects.get(username=request.user.username) + + if request.data in auth_object.auth_service: + return Response(status=status.HTTP_409_CONFLICT, data={"message": "That object already exists for this account."}) + auth_object.auth_service.append(request.data) + auth_object.save() + return Response(status=status.HTTP_200_OK, data={"message": "Authentication added to existing object"}) + + except Authentication.DoesNotExist: + auth_object = Authentication.objects.create( + username=request.user, + auth_service=[request.data] + ) + print('status=status.HTTP_201_CREATED') + return Response(status=status.HTTP_201_CREATED, data={"message": "Authentication object added to account"}) + + except Exception as err: + return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": err}) + +class RemoveAuthenticationApi(APIView): + """ + Removes Authentication Object + + ----------------------------- + + Removes an authentication dictionary to the list of auth_objects for a user + + ```JSON + { + "sub": "0000-0000-0000-0000", + "iss": "https://example.org" + } + ``` + """ + permission_classes = [IsAuthenticated,] + schema = openapi.Schema( + type=openapi.TYPE_OBJECT, + title="Remove Authentication", + description="Removess an authentication objetc to the associated user", + required=["iss", "sub"], + properties={ + "iss": openapi.Schema( + type=openapi.TYPE_STRING, + description="The 'iss' (issuer) claim identifies the principal" + " that issued the JWT." + ), + "sub": openapi.Schema( + type=openapi.TYPE_STRING, + description="The 'sub' (subject) claim identifies the" + " principal that is the subject of the JWT.", + + + ) + } + + ) + + @swagger_auto_schema( + request_body=schema, + responses={ + 200: "Remove authentication is successful.", + 400: "Bad request.", + 409: "That object already exists for this account.", + }, + tags=["Authentication"], + ) + def post(self, request): + """""" + result = validate_auth_service(request.data) + + if result is not 1: + return Response(status=status.HTTP_400_BAD_REQUEST, data=result) + try: + auth_object = Authentication.objects.get(username=request.user.username) + except Authentication.DoesNotExist: + return Response( + status=status.HTTP_409_CONFLICT, + data={"message": "That object does not exists."} + ) + if request.data not in auth_object.auth_service: + return Response( + status=status.HTTP_409_CONFLICT, + data={"message": "That object does not exists."} + ) + auth_object.auth_service.remove(request.data) + auth_object.save() + return Response(status=status.HTTP_200_OK, data={"message": "Authentication object removed."}) diff --git a/authentication/services.py b/authentication/services.py index 06def140..4907024f 100644 --- a/authentication/services.py +++ b/authentication/services.py @@ -3,6 +3,7 @@ import jwt import json import requests +import jsonschema from django.contrib.auth.models import User, Group from rest_framework import exceptions, status, serializers from rest_framework.response import Response @@ -15,28 +16,6 @@ from authentication.models import Authentication jwt_decode_handler = api_settings.JWT_DECODE_HANDLER -class AuthInputSerializer(serializers.ModelSerializer): - auth_service = serializers.JSONField() - - class Meta: - model = Authentication - fields = ('username', 'auth_service') - - def validate_auth_service(self, value): - for auth in value: - if 'sub' not in auth or 'iss' not in auth: - raise serializers.ValidationError('Each item in auth_service', - ' list must be in the form of {"sub": "###########", "iss":', - ' "http://example.org"}') - return value - - def create(self, validated_data): - auth_service = validated_data.pop('auth_service') - instance = super().create(validated_data) - instance.auth_service = auth_service - instance.save() - return instance - class CustomJSONWebTokenAuthentication(BaseAuthentication): def authenticate(self, request): @@ -57,7 +36,7 @@ def authenticate(self, request): return (user, token) - if type == 'Token': + if type == 'Token' or type == 'TOKEN': pass pass @@ -77,6 +56,22 @@ def authenticate_portal(payload: dict, token:str)-> User: else: exceptions.AuthenticationFailed(response.reason) +def validate_auth_service(value): + schema = { + "type": "object", + "required": ["iss", "sub"], + "additionalProperties": False, + "properties": { + "iss": {"type": "string", "description": "The 'iss' (issuer) claim identifies the principal that issued the JWT."}, + "sub": {"type": "string", "description": "The 'sub' (subject) claim identifies the principal that is the subject of the JWT."} + } + } + try: + jsonschema.validate(value, schema) + except jsonschema.ValidationError as error: + data = {"message": error.message} + return data + return 1 def authenticate_orcid(payload:dict, token:str)-> User: """Authenticate ORCID @@ -92,7 +87,7 @@ def authenticate_orcid(payload:dict, token:str)-> User: orcid_key = jwt.algorithms.RSAAlgorithm.from_jwk(orcid_jwk) try: - jwt.decode(token, key=orcid_key, algorithms=['RS256'], audience='APP-88DEA42BRILGEHKC') + jwt.decode(token, key=orcid_key, algorithms=['RS256'], audience=['APP-88DEA42BRILGEHKC', 'APP-ZQZ0BL62NV9SBWAX']) except Exception as exp: print('exp:', exp) raise exceptions.AuthenticationFailed(exp) diff --git a/authentication/urls.py b/authentication/urls.py index 7f769d05..5edd4331 100644 --- a/authentication/urls.py +++ b/authentication/urls.py @@ -1,8 +1,10 @@ # authentication/urls.py from django.urls import path -from authentication.apis import RegisterBcodbAPI +from authentication.apis import RegisterBcodbAPI, AddAuthenticationApi, RemoveAuthenticationApi urlpatterns = [ - path("auth/register/", RegisterBcodbAPI.as_view()) + path("auth/register/", RegisterBcodbAPI.as_view()), + path("auth/add/", AddAuthenticationApi.as_view()), + path("auth/remove/", RemoveAuthenticationApi.as_view()) ] \ No newline at end of file From 30d981cd0ce4e3dcec7ccdd58dd16775c412b404 Mon Sep 17 00:00:00 2001 From: hadleyking Date: Mon, 10 Apr 2023 10:09:46 -0400 Subject: [PATCH 05/11] Fix #154 Changes to be committed: modified: api/views.py --- api/views.py | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/api/views.py b/api/views.py index 00d80e09..f6e7fb79 100755 --- a/api/views.py +++ b/api/views.py @@ -1026,21 +1026,45 @@ class ApiObjectsSearch(APIView): -------------------- Search for available BCO objects that match criteria. + + `type` can be one of 3 different values => mine | prefix | bco_id + `search` should be an empty string if you are doing the mine search as that is for "My BCOs" + For prefix `search` should be the name of the prefix. + For `bco_id` it should be some substring that is present in the desired `bco_id` or SET of `bco_ids` + + Shell + ```shell + curl -X POST "http://localhost:8000/api/objects/search/" -H "accept: application/json" -H "Authorization: Token ${token}" -H "Content-Type: application/json" -d "{\"POST_api_objects_search\":[{\"type\": \"prefix\",\"search\": \"TEST\"}]}" + ``` + + JavaScript + ```javascript + axios.post("http://localhost:8000/api/objects/search/", { + "POST_api_objects_search":[ + { + "type": "prefix", + "search": "TEST" + } + ] + }, { + headers: { + "Authorization": "Token ${token}, + "Content-Type": "application/json" + } + }); + ``` """ - # authentication_classes = [] - # permission_classes = [] - # TODO: Need to get the schema that is being sent here from FE request_body = openapi.Schema( type=openapi.TYPE_OBJECT, - title="BCO Publication Schema", - description="Publish description.", + title="BCO Search Schema", + description="Search for BCOs", properties={ - "x": openapi.Schema( - type=openapi.TYPE_STRING, description="Description of X" + "type": openapi.Schema( + type=openapi.TYPE_STRING, description="Type of search to perform" ), - "y": openapi.Schema( - type=openapi.TYPE_STRING, description="Description of Y" + "search": openapi.Schema( + type=openapi.TYPE_STRING, description="Search value" ), }, ) @@ -1048,9 +1072,8 @@ class ApiObjectsSearch(APIView): @swagger_auto_schema( request_body=request_body, responses={ - 200: "BCO publication is successful.", - 400: "Bad request.", - 403: "Invalid token.", + 200: "Search successful.", + 404: "That prefix was not found on this server." }, tags=["BCO Management"], ) From 98ab64907936fa75a02ee463871429e0913edb4c Mon Sep 17 00:00:00 2001 From: hadleyking Date: Wed, 12 Apr 2023 13:31:21 -0400 Subject: [PATCH 06/11] Updates for RESTfull BCO URls in portal Changes to be committed: modified: api/scripts/method_specific/GET_draft_object_by_id.py modified: authentication/selectors.py modified: authentication/services.py --- api/scripts/method_specific/GET_draft_object_by_id.py | 10 +++++++--- authentication/selectors.py | 7 ++++++- authentication/services.py | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/api/scripts/method_specific/GET_draft_object_by_id.py b/api/scripts/method_specific/GET_draft_object_by_id.py index efaefa19..a63c7107 100755 --- a/api/scripts/method_specific/GET_draft_object_by_id.py +++ b/api/scripts/method_specific/GET_draft_object_by_id.py @@ -8,10 +8,10 @@ from api.models import BCO from api.scripts.utilities import UserUtils -from rest_framework import status +from rest_framework import status, authtoken from rest_framework.response import Response from guardian.shortcuts import get_objects_for_user - +from authentication.selectors import get_user_from_auth_token def get_draft_object_by_id(do_id, request): """Get a draft object @@ -40,7 +40,11 @@ def get_draft_object_by_id(do_id, request): status=status.HTTP_400_BAD_REQUEST, ) # Get the requestor's info. - user = UserUtils.UserUtils().user_from_request(request=request) + try: + user = UserUtils.UserUtils().user_from_request(request=request) + except authtoken.models.Token.DoesNotExist: + user = get_user_from_auth_token(request.META.get("HTTP_AUTHORIZATION").split(" ")[1]) + # import pdb; pdb.set_trace() user_perms = UserUtils.UserUtils().prefix_perms_for_user( flatten=True, user_object=user, specific_permission=["view"] ) diff --git a/authentication/selectors.py b/authentication/selectors.py index a5010876..47308d5e 100644 --- a/authentication/selectors.py +++ b/authentication/selectors.py @@ -13,7 +13,7 @@ def get_user_from_auth_token(token: str)-> User: if payload['iss'] == 'https://orcid.org' or payload['iss'] == 'https://sandbox.orcid.org': try: - return User.objects.get(username=Authentication.objects.get(auth_service__icontains=payload['iss']).username) + return User.objects.get(username=Authentication.objects.get(auth_service__icontains=payload['sub']).username) except User.DoesNotExist: return None if payload['iss'] == 'accounts.google.com': @@ -21,6 +21,11 @@ def get_user_from_auth_token(token: str)-> User: return User.objects.get(email=payload['email']) except User.DoesNotExist: return None + if payload['iss'] in ['http://localhost:8080', 'https://test.portal.biochemistry.gwu.edu/', 'https://biocomputeobject.org/']: + try: + return User.objects.get(email=payload['email']) + except User.DoesNotExist: + return None def check_user_email(email: str)-> bool: """Check for user diff --git a/authentication/services.py b/authentication/services.py index 4907024f..77e5b19e 100644 --- a/authentication/services.py +++ b/authentication/services.py @@ -92,7 +92,7 @@ def authenticate_orcid(payload:dict, token:str)-> User: print('exp:', exp) raise exceptions.AuthenticationFailed(exp) try: - user = User.objects.get(username=Authentication.objects.get(auth_service__icontains=payload['iss']).username) + user = User.objects.get(username=Authentication.objects.get(auth_service__icontains=payload['sub']).username) except (Authentication.DoesNotExist, User.DoesNotExist): return None return user From 7c8469ad8bf9348ecb351d2ceafa64cb58e6857f Mon Sep 17 00:00:00 2001 From: hadleyking Date: Thu, 13 Apr 2023 09:32:21 -0400 Subject: [PATCH 07/11] Update server.conf to be .gitignore Changes to be committed: modified: .gitignore new file: sever.conf.example --- .gitignore | 2 +- sever.conf.example | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 sever.conf.example diff --git a/.gitignore b/.gitignore index ebf80982..97d1ad4e 100755 --- a/.gitignore +++ b/.gitignore @@ -140,6 +140,6 @@ dmypy.json # bco_api/api/migrations/ .DS_Store* *.vscode/* - +server.conf # JetBrains IDEs .idea/ diff --git a/sever.conf.example b/sever.conf.example new file mode 100644 index 00000000..454a7100 --- /dev/null +++ b/sever.conf.example @@ -0,0 +1,74 @@ +# This is the main server configuration file for the BCO API. + +# --- Production and publishing flags --- # +# NOTE: Valid values are True or False (note the capitalization). +[PRODUCTION] +production=False + +# DB Version +[VERSION] +version=22.11 + +# NOTE: Valid values are True or False (note the capitalization). +# Is this a publish-only server? +[PUBLISHONLY] +publishonly=False + +# --- Security settings --- # + +# Create a key for an anonymous public user. +[KEYS] +anon=627626823549f787c3ec763ff687169206626149 + +[DATABASES] +# default settting +path=./db.sqlite3 +# path=/Users/hadleyking/GitHub/biocompute-objects/dev/data/db.sqlite3 + +# Which host names do you want to associate with the server? +# Note: the local hostname (i.e. 127.0.0.1) should come at the end. +[HOSTNAMES] +prod_names=test.portal.biochemistry.gwu.edu,127.0.0.1 +names=127.0.0.1:8000,127.0.0.1 + +# Give the human-readable hostnames +[HRHOSTNAME] +hrnames=BCO Server (Default) + +# The public hostname of the server (i.e. the one to make requests to) +[PUBLICHOSTNAME] +prod_name=https://test.portal.biochemistry.gwu.edu +name=http://127.0.0.1:8000 + +# Who gets to make requests? +[REQUESTS_FROM] +portal=https://test.portal.biochemistry.gwu.edu +local_development_portal=http://127.0.0.1:3000,http://localhost:3000 +public=true + +# --- Namings --- # +# How do you want to name your objects? +[OBJECT_NAMING] +prod_root_uri=https://test.portal.biochemistry.gwu.edu +root_uri=http://127.0.0.1:8000 +prod_uri_regex=root_uri/prefix_(\d+)/(\d+).(\d+) +uri_regex=root_uri/prefix_(\d+)/(\d+).(\d+) + +# --- Requests --- # +# Where are the request templates defined? +[REQUESTS] +folder=../api/request_definitions/ + +# --- Group and Prefix creation --- # +# To enable all users to create a prefix, group creation needs to be enabled. +# When a prefix is created it has assocaited groups created as well. If the +# user does not have permission to creat the needed groups the prefix creation +# will fail. +[GROUP_PREFIX] +allow_all_creation=True +allow_group_creation=True +allow_prefix_creation=True + +# Where are the validation templates defined? +# [VALIDATIONS] +# folder=../api/validation_definitions/ \ No newline at end of file From fb3018de81e4fa6c812f7dd85aa43a9e327b0378 Mon Sep 17 00:00:00 2001 From: hadleyking Date: Mon, 17 Apr 2023 13:09:01 -0400 Subject: [PATCH 08/11] Upadate to BCO search Changes to be committed: modified: authentication/selectors.py modified: authentication/services.py modified: search/apis.py modified: search/selectors.py --- authentication/selectors.py | 6 ++++++ authentication/services.py | 11 +++++++++- search/apis.py | 25 ++++++++++++++++------ search/selectors.py | 42 ++++++++++++++++++++++++++++++++----- 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/authentication/selectors.py b/authentication/selectors.py index 47308d5e..df2656cb 100644 --- a/authentication/selectors.py +++ b/authentication/selectors.py @@ -6,6 +6,12 @@ from authentication.models import Authentication from rest_framework.authtoken.models import Token + +def get_anon()-> User: + """Get AnonymosUser + """ + return User.objects.get(username="AnonymousUser") + def get_user_from_auth_token(token: str)-> User: """Get user from Auth Token """ diff --git a/authentication/services.py b/authentication/services.py index 77e5b19e..1d7c179e 100644 --- a/authentication/services.py +++ b/authentication/services.py @@ -12,16 +12,25 @@ from rest_framework_jwt.utils import jwt_get_secret_key from google.oauth2 import id_token from google.auth.transport import requests as g_requests -from authentication.selectors import check_user_email +from authentication.selectors import get_anon from authentication.models import Authentication + + jwt_decode_handler = api_settings.JWT_DECODE_HANDLER class CustomJSONWebTokenAuthentication(BaseAuthentication): def authenticate(self, request): + # import pdb; pdb.set_trace() if 'Authorization' in request.headers: type, token = request.headers['Authorization'].split(' ') + if type == 'Bearer': + if token == "null": + token = "627626823549f787c3ec763ff687169206626149" + user = get_anon() + + return (user, token) try: unverified_payload = jwt.decode(token, None, False) except Exception as exp: diff --git a/search/apis.py b/search/apis.py index 2d0bc779..41e6814e 100644 --- a/search/apis.py +++ b/search/apis.py @@ -3,9 +3,11 @@ from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema from rest_framework import status +from authentication.services import CustomJSONWebTokenAuthentication from rest_framework.response import Response +from rest_framework.permissions import AllowAny from rest_framework.views import APIView -from search.selectors import search_db +from search.selectors import search_db, controled_list from api.models import BCO from itertools import chain @@ -15,8 +17,9 @@ class SearchObjectsAPI(APIView): ------------------- Endpoint for use of query string based search. """ - authentication_classes = [] - permission_classes = [] + authentication_classes = [CustomJSONWebTokenAuthentication] + permission_classes = [AllowAny,] + auth = openapi.Parameter('test', openapi.IN_QUERY, description="test manual param", type=openapi.TYPE_BOOLEAN) @swagger_auto_schema( @@ -54,7 +57,6 @@ class SearchObjectsAPI(APIView): def get(self, request) -> Response: """GET search TODO: multiple values in the URL will only return the last one. - TODO: add authentication options """ return_values = [ "contents", @@ -67,11 +69,22 @@ def get(self, request) -> Response: "schema", "state", ] + search = self.request.GET - print(search) - result = BCO.objects.all() + print(request.user.username) + result = controled_list(request.user) + # import pdb; pdb.set_trace() + # result = BCO.objects.all() for query, value in search.items(): filter = f'{query}__icontains' result = search_db(filter, value, result) search_result = chain(result.values(*return_values)) return Response(status=status.HTTP_200_OK, data={search_result}) + + + # result = BCO.objects.filter(state="PUBLISHED") + # for query, value in search.items(): + # filter = f'{query}__icontains' + # result = search_db(filter, value, result) + # search_result = chain(result.values(*return_values)) + # return Response(status=status.HTTP_200_OK, data={search_result}) diff --git a/search/selectors.py b/search/selectors.py index 0925d161..7331dbfa 100644 --- a/search/selectors.py +++ b/search/selectors.py @@ -1,12 +1,44 @@ # search/selectors.py -from django.db.models import Q from api.models import BCO +from django.db.models import QuerySet from django.db.models.query import QuerySet +from django.contrib.auth.models import User +from guardian.shortcuts import get_objects_for_user +from itertools import chain +from api.scripts.utilities.UserUtils import UserUtils +return_values = [ + "contents", + "last_update", + "object_class", + "object_id", + "owner_group", + "owner_user", + "prefix", + "schema", + "state", + ] def search_db(filter:str, value:str, result:QuerySet)-> QuerySet: - """ + """Search DB + Search the BCODB """ new_result = result.filter(**{filter: value}) - print(filter, value) - print("new", len(new_result)) - return new_result \ No newline at end of file + return new_result + +def controled_list(user: User): + import pdb; pdb.set_trace() + prefix_list = [] + results_list = BCO.objects.none() + raw_prefixes = UserUtils().prefix_perms_for_user(user_object=user) + for prefix in raw_prefixes : + pre = prefix.split("_")[1] + if pre not in prefix_list and pre is not "prefix": + prefix_list.append(pre) + + for prefix in prefix_list: + if user.username == "AnonymousUser": + bco_list = BCO.objects.filter(prefix=prefix).values().exclude(state="DELETE").exclude(state="DRAFT") + results_list = results_list | bco_list + + return results_list + From 9af250836c8f12a39db38525d212cb10d36d63b9 Mon Sep 17 00:00:00 2001 From: hadleyking Date: Mon, 17 Apr 2023 14:31:02 -0400 Subject: [PATCH 09/11] Remove testing breaks Changes to be committed: modified: api/scripts/method_specific/GET_draft_object_by_id.py modified: api/scripts/utilities/UserUtils.py modified: api/tests/test_group_post_api.py modified: api/tests/test_model_user.py modified: authentication/services.py modified: search/apis.py modified: search/selectors.py --- api/scripts/method_specific/GET_draft_object_by_id.py | 1 - api/scripts/utilities/UserUtils.py | 1 - api/tests/test_group_post_api.py | 1 - api/tests/test_model_user.py | 1 - authentication/services.py | 3 +-- search/apis.py | 2 -- search/selectors.py | 1 - 7 files changed, 1 insertion(+), 9 deletions(-) diff --git a/api/scripts/method_specific/GET_draft_object_by_id.py b/api/scripts/method_specific/GET_draft_object_by_id.py index a63c7107..b90cb368 100755 --- a/api/scripts/method_specific/GET_draft_object_by_id.py +++ b/api/scripts/method_specific/GET_draft_object_by_id.py @@ -44,7 +44,6 @@ def get_draft_object_by_id(do_id, request): user = UserUtils.UserUtils().user_from_request(request=request) except authtoken.models.Token.DoesNotExist: user = get_user_from_auth_token(request.META.get("HTTP_AUTHORIZATION").split(" ")[1]) - # import pdb; pdb.set_trace() user_perms = UserUtils.UserUtils().prefix_perms_for_user( flatten=True, user_object=user, specific_permission=["view"] ) diff --git a/api/scripts/utilities/UserUtils.py b/api/scripts/utilities/UserUtils.py index c1ef5eec..4ff5b375 100755 --- a/api/scripts/utilities/UserUtils.py +++ b/api/scripts/utilities/UserUtils.py @@ -220,7 +220,6 @@ def prefix_perms_for_user( # if not flatten: # bco_specific['user']['bco'] = { } - # import pdb; pdb.set_trace() # for k, v in prefixed['groups']: # if 'bco' in prefixed['groups'][k]: # if flatten: diff --git a/api/tests/test_group_post_api.py b/api/tests/test_group_post_api.py index 61a404b0..f66f7efd 100644 --- a/api/tests/test_group_post_api.py +++ b/api/tests/test_group_post_api.py @@ -145,6 +145,5 @@ def test_post_api_groups_delete(self): response = view(request) # print("\ttest_post_api_groups_delete response: {}".format(response.data)) - # import pdb; pdb.set_trace() # Assert the status code is as expected. self.assertEqual(response.status_code, 200) diff --git a/api/tests/test_model_user.py b/api/tests/test_model_user.py index 62c9d424..d443854c 100644 --- a/api/tests/test_model_user.py +++ b/api/tests/test_model_user.py @@ -44,7 +44,6 @@ def test_user_creation(self): self.assertEqual(user.__token__(), self.token) self.assertEqual(user.__hostname__(), self.hostname) self.assertEqual(user.__temp_identifier__(), self.temp_identifier) - # import pdb; pdb.set_trace() def test_activate_user(self): """Activate new user diff --git a/authentication/services.py b/authentication/services.py index 1d7c179e..65efd482 100644 --- a/authentication/services.py +++ b/authentication/services.py @@ -21,7 +21,6 @@ class CustomJSONWebTokenAuthentication(BaseAuthentication): def authenticate(self, request): - # import pdb; pdb.set_trace() if 'Authorization' in request.headers: type, token = request.headers['Authorization'].split(' ') @@ -131,7 +130,7 @@ def validate_token(token: str, url: str)-> bool: """ headers = {"Content-type": "application/json; charset=UTF-8",} - import pdb; pdb.set_trace + response = requests.post( data=json.dumps({"token": token}, default=str), headers=headers, diff --git a/search/apis.py b/search/apis.py index 41e6814e..36e7f0d6 100644 --- a/search/apis.py +++ b/search/apis.py @@ -73,8 +73,6 @@ def get(self, request) -> Response: search = self.request.GET print(request.user.username) result = controled_list(request.user) - # import pdb; pdb.set_trace() - # result = BCO.objects.all() for query, value in search.items(): filter = f'{query}__icontains' result = search_db(filter, value, result) diff --git a/search/selectors.py b/search/selectors.py index 7331dbfa..e070a10f 100644 --- a/search/selectors.py +++ b/search/selectors.py @@ -26,7 +26,6 @@ def search_db(filter:str, value:str, result:QuerySet)-> QuerySet: return new_result def controled_list(user: User): - import pdb; pdb.set_trace() prefix_list = [] results_list = BCO.objects.none() raw_prefixes = UserUtils().prefix_perms_for_user(user_object=user) From 8ca1f0ac54c4347bfbb5dd2516d74088403fd374 Mon Sep 17 00:00:00 2001 From: hadleyking Date: Mon, 17 Apr 2023 14:32:10 -0400 Subject: [PATCH 10/11] Changes to be committed: modified: search/apis.py --- search/apis.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/search/apis.py b/search/apis.py index 36e7f0d6..718eae8c 100644 --- a/search/apis.py +++ b/search/apis.py @@ -79,10 +79,3 @@ def get(self, request) -> Response: search_result = chain(result.values(*return_values)) return Response(status=status.HTTP_200_OK, data={search_result}) - - # result = BCO.objects.filter(state="PUBLISHED") - # for query, value in search.items(): - # filter = f'{query}__icontains' - # result = search_db(filter, value, result) - # search_result = chain(result.values(*return_values)) - # return Response(status=status.HTTP_200_OK, data={search_result}) From 203c523459d248f338eaf1aa783ea2ddf94c455f Mon Sep 17 00:00:00 2001 From: hadleyking Date: Thu, 20 Apr 2023 08:04:23 -0400 Subject: [PATCH 11/11] Fix quick search Changes to be committed: modified: search/selectors.py --- search/selectors.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/search/selectors.py b/search/selectors.py index e070a10f..29180081 100644 --- a/search/selectors.py +++ b/search/selectors.py @@ -37,6 +37,8 @@ def controled_list(user: User): for prefix in prefix_list: if user.username == "AnonymousUser": bco_list = BCO.objects.filter(prefix=prefix).values().exclude(state="DELETE").exclude(state="DRAFT") + else: + bco_list = BCO.objects.filter(prefix=prefix).values().exclude(state="DELETE") results_list = results_list | bco_list return results_list