Skip to content

Commit

Permalink
Merge branch 'main' into wip/scenario-tanner
Browse files Browse the repository at this point in the history
  • Loading branch information
jnussbaum authored Oct 15, 2023
2 parents 4b9c31f + 7fb00c5 commit 944f5b1
Show file tree
Hide file tree
Showing 12 changed files with 98 additions and 44 deletions.
4 changes: 3 additions & 1 deletion dsp_permissions_scripts/ap/ap_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import requests

from dsp_permissions_scripts.ap.ap_model import Ap, ApValue
from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.utils.authentication import get_protocol
from dsp_permissions_scripts.utils.get_logger import get_logger
from dsp_permissions_scripts.utils.project import get_project_iri_by_shortcode
Expand Down Expand Up @@ -47,7 +48,8 @@ def _get_all_aps_of_project(
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/ap/{project_iri}"
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not get APs of project", response.text, response.status_code)
aps: list[dict[str, Any]] = response.json()["administrative_permissions"]
ap_objects = [create_ap_from_admin_route_object(ap) for ap in aps]
return ap_objects
Expand Down
2 changes: 0 additions & 2 deletions dsp_permissions_scripts/ap/ap_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ class ApValue(Enum):
ProjectAdminGroupRestrictedPermission = "ProjectAdminGroupRestrictedPermission"
# is allowed to change the permissions on all objects belonging to the project
ProjectAdminRightsAllPermission = "ProjectAdminRightsAllPermission"
# is allowed to administrate the project ontologies
ProjectAdminOntologyAllPermission = "ProjectAdminOntologyAllPermission"


class Ap(BaseModel):
Expand Down
18 changes: 13 additions & 5 deletions dsp_permissions_scripts/ap/ap_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
create_ap_from_admin_route_object,
)
from dsp_permissions_scripts.ap.ap_model import Ap
from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.utils.authentication import get_protocol
from dsp_permissions_scripts.utils.get_logger import get_logger, get_timestamp

Expand All @@ -34,7 +35,8 @@ def _delete_single_ap(
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/{ap_iri}"
response = requests.delete(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError(f"Could not delete Administrative Permission {ap.iri}", response.text, response.status_code)
logger.info(f"Deleted Administrative Permission {ap.iri} on host {host}")


Expand All @@ -52,7 +54,13 @@ def _update_ap(
url = f"{protocol}://{host}/admin/permissions/{iri}/hasPermissions"
payload = {"hasPermissions": create_admin_route_object_from_ap(ap)["hasPermissions"]}
response = requests.put(url, headers=headers, json=payload, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError(
message=f"Could not update Administrative Permission {ap.iri}",
response_text=response.text,
status_code=response.status_code,
payload=payload,
)
ap_updated: dict[str, Any] = response.json()["administrative_permission"]
ap_object_updated = create_ap_from_admin_route_object(ap_updated)
return ap_object_updated
Expand Down Expand Up @@ -90,9 +98,9 @@ def apply_updated_aps_on_server(
token=token,
)
_log_and_print_ap_update(ap=new_ap)
except Exception: # pylint: disable=broad-exception-caught
logger.error(f"ERROR while updating Administrative Permission {ap.iri}", exc_info=True)
warnings.warn(f"ERROR while updating Administrative Permission {ap.iri}")
except ApiError as err:
logger.error(err)
warnings.warn(err.message)

print(f"{get_timestamp()}: All APs have been updated.")

Expand Down
4 changes: 3 additions & 1 deletion dsp_permissions_scripts/doap/doap_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import requests

from dsp_permissions_scripts.doap.doap_model import Doap, DoapTarget, DoapTargetType
from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.utils.authentication import get_protocol
from dsp_permissions_scripts.utils.get_logger import get_logger
from dsp_permissions_scripts.utils.project import get_project_iri_by_shortcode
Expand Down Expand Up @@ -47,7 +48,8 @@ def _get_all_doaps_of_project(
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/permissions/doap/{project_iri}"
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError(f"Error while getting DOAPs of project {project_iri}", response.text, response.status_code)
doaps: list[dict[str, Any]] = response.json()["default_object_access_permissions"]
doap_objects = [create_doap_from_admin_route_response(doap) for doap in doaps]
return doap_objects
Expand Down
10 changes: 6 additions & 4 deletions dsp_permissions_scripts/doap/doap_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from dsp_permissions_scripts.doap.doap_get import create_doap_from_admin_route_response
from dsp_permissions_scripts.doap.doap_model import Doap
from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.models.scope import PermissionScope
from dsp_permissions_scripts.utils.authentication import get_protocol
from dsp_permissions_scripts.utils.get_logger import get_logger, get_timestamp
Expand All @@ -30,7 +31,8 @@ def _update_doap_scope(
url = f"{protocol}://{host}/admin/permissions/{iri}/hasPermissions"
payload = {"hasPermissions": create_admin_route_object_from_scope(scope)}
response = requests.put(url, headers=headers, json=payload, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError( f"Could not update scope of DOAP {doap_iri}", response.text, response.status_code, payload)
new_doap = create_doap_from_admin_route_response(response.json()["default_object_access_permission"])
return new_doap

Expand Down Expand Up @@ -68,8 +70,8 @@ def apply_updated_doaps_on_server(
token=token,
)
_log_and_print_doap_update(doap=new_doap)
except Exception: # pylint: disable=broad-exception-caught
logger.error(f"ERROR while updating DOAP {d.doap_iri}", exc_info=True)
warnings.warn(f"ERROR while updating DOAP {d.doap_iri}")
except ApiError as err:
logger.error(err)
warnings.warn(err.message)

print(f"{get_timestamp()}: All DOAPs have been updated.")
16 changes: 16 additions & 0 deletions dsp_permissions_scripts/models/api_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pprint
from dataclasses import dataclass, field
from typing import Any


@dataclass(frozen=True)
class ApiError(Exception):
"""Exception raised when an error occurs while calling DSP-API."""

message: str
response_text: str | None = None
status_code: int | None = None
payload: dict[str, Any] = field(default_factory=dict)

def __str__(self) -> str:
return pprint.pformat(vars(self))
5 changes: 5 additions & 0 deletions dsp_permissions_scripts/models/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,8 @@ def remove(
D={builtin_groups.CREATOR, builtin_groups.PROJECT_MEMBER},
V={builtin_groups.UNKNOWN_USER, builtin_groups.KNOWN_USER},
)

PRIVATE = PermissionScope.create(
CR={builtin_groups.PROJECT_ADMIN, builtin_groups.CREATOR},
V={builtin_groups.PROJECT_MEMBER},
)
29 changes: 18 additions & 11 deletions dsp_permissions_scripts/oap/oap_get_set.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# pylint: disable=too-many-arguments

import json
import warnings
from typing import Any
from urllib.parse import quote_plus

import requests

from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.models.scope import PermissionScope
from dsp_permissions_scripts.models.value import ValueUpdate
from dsp_permissions_scripts.oap.oap_model import Oap
Expand Down Expand Up @@ -50,7 +50,8 @@ def _get_resource(
url = f"{protocol}://{host}/v2/resources/{iri}"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError( f"Error while getting resource {resource_iri}", response.text, response.status_code)
data: dict[str, Any] = response.json()
return data

Expand Down Expand Up @@ -109,11 +110,11 @@ def _update_permissions_for_value(
msg = f"Permissions of resource {resource_iri}, value {value.value_iri} are already up to date"
logger.warning(msg)
elif response.status_code != 200:
logger.error(
f"Error while updating permissions of resource {resource_iri}, value {value.value_iri}. "
f"Response status code: {response.status_code}. "
f"Response text: {response.text}. "
f"Payload: {json.dumps(payload, indent=4)}"
raise ApiError(
message=f"Error while updating permissions of resource {resource_iri}, value {value.value_iri}",
response_text=response.text,
status_code=response.status_code,
payload=payload
)
else:
logger.info(f"Updated permissions of resource {resource_iri}, value {value.value_iri}")
Expand Down Expand Up @@ -143,7 +144,13 @@ def _update_permissions_for_resource(
url = f"{protocol}://{host}/v2/resources"
headers = {"Authorization": f"Bearer {token}"}
response = requests.put(url, headers=headers, json=payload, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError(
message=f"ERROR while updating permissions of resource {resource_iri}",
response_text=response.text,
status_code=response.status_code,
payload=payload,
)
logger.info(f"Updated permissions of resource {resource_iri}")


Expand Down Expand Up @@ -214,9 +221,9 @@ def apply_updated_oaps_on_server(
host=host,
token=token,
)
except Exception: # pylint: disable=broad-exception-caught
logger.error(f"ERROR while updating permissions of resource {resource_oap.object_iri}", exc_info=True)
warnings.warn(f"ERROR while updating permissions of resource {resource_oap.object_iri}")
except ApiError as err:
logger.error(err)
warnings.warn(err.message)
failed_res_iris.append(resource_oap.object_iri)
logger.info(f"Updated permissions of resource {resource_oap.object_iri} and its values.")

Expand Down
10 changes: 5 additions & 5 deletions dsp_permissions_scripts/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ def modify_aps(aps: list[Ap]) -> list[Ap]:
"""Adapt this sample to your needs."""
modified_aps = []
for ap in aps:
if ap.forGroup == builtin_groups.PROJECT_MEMBER:
if ApValue.ProjectAdminOntologyAllPermission not in ap.hasPermissions:
ap.add_permission(ApValue.ProjectAdminOntologyAllPermission)
if ap.forGroup == builtin_groups.UNKNOWN_USER:
if ApValue.ProjectAdminGroupAllPermission not in ap.hasPermissions:
ap.add_permission(ApValue.ProjectAdminGroupAllPermission)
modified_aps.append(ap)
return modified_aps

Expand All @@ -33,7 +33,7 @@ def modify_doaps(doaps: list[Doap]) -> list[Doap]:
"""Adapt this sample to your needs."""
modified_doaps = []
for doap in doaps:
if doap.target.group in [builtin_groups.PROJECT_MEMBER, builtin_groups.PROJECT_ADMIN]:
if doap.target.group == builtin_groups.UNKNOWN_USER:
doap.scope = PUBLIC
modified_doaps.append(doap)
return modified_doaps
Expand Down Expand Up @@ -69,7 +69,7 @@ def update_aps(
host=host,
token=token,
existing_aps=project_aps,
forGroup=builtin_groups.PROJECT_MEMBER,
forGroup=builtin_groups.UNKNOWN_USER,
)
modified_aps = modify_aps(remaining_aps)
apply_updated_aps_on_server(
Expand Down
5 changes: 4 additions & 1 deletion dsp_permissions_scripts/utils/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import requests

from dsp_permissions_scripts.models.api_error import ApiError


def _get_token(host: str, email: str, pw: str) -> str:
"""
Expand All @@ -10,7 +12,8 @@ def _get_token(host: str, email: str, pw: str) -> str:
protocol = get_protocol(host)
url = f"{protocol}://{host}/v2/authentication"
response = requests.post(url, json={"email": email, "password": pw}, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not login", response.text, response.status_code)
token: str = response.json()["token"]
return token

Expand Down
37 changes: 24 additions & 13 deletions dsp_permissions_scripts/utils/project.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import warnings
from urllib.parse import quote_plus

import requests

from dsp_permissions_scripts.models.api_error import ApiError
from dsp_permissions_scripts.oap.oap_model import Oap
from dsp_permissions_scripts.utils.authentication import get_protocol
from dsp_permissions_scripts.utils.get_logger import get_logger, get_timestamp
Expand Down Expand Up @@ -43,7 +45,8 @@ def _get_onto_iris_of_project(
url = f"{protocol}://{host}/v2/ontologies/metadata"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not get onto IRIs", response.text, response.status_code)
all_ontologies = response.json().get("@graph")
project_onto_iris = [o["@id"] for o in all_ontologies if o["knora-api:attachedToProject"]["@id"] == project_iri]
return project_onto_iris
Expand All @@ -58,7 +61,8 @@ def _get_class_iris_of_onto(
url = f"{protocol}://{host}/v2/ontologies/allentities/{quote_plus(onto_iri)}"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not get class IRIs", response.text, response.status_code)
all_entities = response.json()["@graph"]
context = response.json()["@context"]
class_ids = [c["@id"] for c in all_entities if c.get("knora-api:isResourceClass")]
Expand All @@ -81,15 +85,20 @@ def _get_all_resource_oaps_of_resclass(
more = True
while more:
logger.info(f"Getting page {page}...")
more, iris = _get_next_page(
protocol=protocol,
host=host,
resclass_iri=resclass_iri,
page=page,
headers=headers,
)
resources.extend(iris)
page += 1
try:
more, iris = _get_next_page(
protocol=protocol,
host=host,
resclass_iri=resclass_iri,
page=page,
headers=headers,
)
resources.extend(iris)
page += 1
except ApiError as err:
logger.error(f"{err}\nStop getting more pages, return what has been retrieved so far.")
warnings.warn(f"{err.message}\nStop getting more pages, return what has been retrieved so far.")
more = False
print(f"{get_timestamp()}: Retrieved {len(resources)} resource OAPs of class {resclass_iri}.")
logger.info(f"Retrieved {len(resources)} resource OAPs of class {resclass_iri}.")
return resources
Expand All @@ -113,7 +122,8 @@ def _get_next_page(
"""
url = f"{protocol}://{host}/v2/resources?resourceClass={quote_plus(resclass_iri)}&page={page}"
response = requests.get(url, headers=headers, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Could not get next page", response.text, response.status_code)
result = response.json()
if "@graph" in result:
# result contains several resources: return them, then continue with next page
Expand All @@ -138,7 +148,8 @@ def get_project_iri_by_shortcode(shortcode: str, host: str) -> str:
protocol = get_protocol(host)
url = f"{protocol}://{host}/admin/projects/shortcode/{shortcode}"
response = requests.get(url, timeout=5)
assert response.status_code == 200, f"Status {response.status_code}. Error message from DSP-API: {response.text}"
if response.status_code != 200:
raise ApiError("Cannot retrieve project IRI", response.text, response.status_code)
iri: str = response.json()["project"]["id"]
return iri

Expand Down
2 changes: 1 addition & 1 deletion tests/test_ap.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ def test_remove_permission(self):

def test_remove_permission_not_exists(self):
with self.assertRaises(ValueError):
self.ap.remove_permission(ApValue.ProjectAdminOntologyAllPermission)
self.ap.remove_permission(ApValue.ProjectAdminAllPermission)

0 comments on commit 944f5b1

Please sign in to comment.