Skip to content

Commit dd87e0a

Browse files
committed
add test for new CLI batch update permissions + requests code path of permissions from config
1 parent 3e10f70 commit dd87e0a

File tree

4 files changed

+194
-111
lines changed

4 files changed

+194
-111
lines changed

magpie/cli/batch_update_permissions.py

+10-19
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
"""
33
Magpie helper to create or delete a set of permissions.
44
5-
When parsing permissions to create, any underlying user, group, service or resource
6-
that are missing, but that can be resolved with reasonable defaults, will be dynamically
7-
created prior to setting the corresponding permission on it.
5+
When parsing permissions to create, any underlying user, group, or intermediate resource
6+
that are missing, but that can be resolved with reasonable defaults or with an explicit
7+
definition in the configuration file, will be dynamically created prior to setting the
8+
corresponding permission on it. All referenced services should exist beforehand. Consider
9+
using the 'register_providers' utility to register services beforehand as needed.
810
911
See https://pavics-magpie.readthedocs.io/en/latest/configuration.html#file-permissions-cfg for more details.
1012
"""
@@ -40,7 +42,7 @@ def make_parser():
4042
parser.add_argument("-P", "--password", "--magpie-admin-password", help=(
4143
"Admin password for magpie login (if omitted, will try using 'MAGPIE_ADMIN_PASSWORD' environment variable)."
4244
))
43-
parser.add_argument("-c", "--config", required=True, nargs="+", help=(
45+
parser.add_argument("-c", "--config", required=True, nargs="+", action="append", help=(
4446
"Path to a single configuration file or a directory containing configuration file that contains permissions. "
4547
"The option can be specified multiple times to provide multiple lookup directories or specific files to load. "
4648
"Configuration files must be in JSON or YAML format, with their respective extensions, or the '.cfg' extension."
@@ -56,22 +58,11 @@ def main(args=None, parser=None, namespace=None):
5658
ns = parser.parse_args(args=args, namespace=namespace)
5759
setup_logger_from_options(LOGGER, ns)
5860

59-
all_configs = []
60-
for cfg in ns.config:
61-
configs = get_all_configs(cfg, "permissions", allow_missing=True)
62-
all_configs.extend(configs)
63-
64-
if ns.verbose:
65-
LOGGER.info(
66-
"Resolved permissions to update:\n\n%s\n",
67-
yaml.safe_dump(all_configs, allow_unicode=True, encoding="utf-8", indent=4, sort_keys=False)
68-
)
69-
70-
if not all_configs or all(not cfg for cfg in all_configs):
71-
LOGGER.error("Could not find any permissions configuration under specified locations.")
72-
return ERROR_PARAMS
61+
all_configs = [cfg for cfg_args in ns.config for cfg in cfg_args]
7362
try:
74-
magpie_register_permissions_from_config(all_configs)
63+
for config in all_configs:
64+
LOGGER.info("Processing: [%s]", config)
65+
magpie_register_permissions_from_config(config)
7566
except Exception as exc:
7667
LOGGER.error("Failed permissions parsing and update from specified configurations [%s].", str(exc))
7768
return ERROR_EXEC

magpie/register.py

+65-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import subprocess # nosec
66
import time
77
from tempfile import NamedTemporaryFile
8-
from typing import TYPE_CHECKING
8+
from typing import TYPE_CHECKING, overload
99

1010
import requests
1111
import six
@@ -61,14 +61,16 @@
6161
CookiesOrSessionType,
6262
GroupsConfig,
6363
GroupsSettings,
64+
Literal,
6465
MultiConfigs,
6566
PermissionConfigItem,
6667
PermissionsConfig,
6768
ServicesConfig,
6869
ServicesSettings,
6970
Str,
7071
UsersConfig,
71-
UsersSettings
72+
UsersSettings,
73+
WebhooksConfig
7274
)
7375

7476

@@ -573,6 +575,36 @@ def _load_config(path_or_dict, section, allow_missing=False):
573575
CONFIG_KNOWN_EXTENSIONS = frozenset([".cfg", ".json", ".yml", ".yaml"])
574576

575577

578+
@overload
579+
def get_all_configs(path_or_dict, section, allow_missing=False):
580+
# type: (Union[Str, CombinedConfig], Literal["groups"], bool) -> GroupsConfig
581+
...
582+
583+
584+
@overload
585+
def get_all_configs(path_or_dict, section, allow_missing=False):
586+
# type: (Union[Str, CombinedConfig], Literal["users"], bool) -> UsersConfig
587+
...
588+
589+
590+
@overload
591+
def get_all_configs(path_or_dict, section, allow_missing=False):
592+
# type: (Union[Str, CombinedConfig], Literal["permissions"], bool) -> PermissionsConfig
593+
...
594+
595+
596+
@overload
597+
def get_all_configs(path_or_dict, section, allow_missing=False):
598+
# type: (Union[Str, CombinedConfig], Literal["services"], bool) -> ServicesConfig
599+
...
600+
601+
602+
@overload
603+
def get_all_configs(path_or_dict, section, allow_missing=False):
604+
# type: (Union[Str, CombinedConfig], Literal["webhooks"], bool) -> WebhooksConfig
605+
...
606+
607+
576608
def get_all_configs(path_or_dict, section, allow_missing=False):
577609
# type: (Union[Str, CombinedConfig], Str, bool) -> MultiConfigs
578610
"""
@@ -1020,19 +1052,36 @@ def magpie_register_permissions_from_config(
10201052
cookies_or_session = db_session
10211053

10221054
LOGGER.debug("Loading configurations.")
1023-
permissions = get_all_configs(permissions_config, "permissions") # type: List[PermissionsConfig]
1055+
if isinstance(permissions_config, list):
1056+
permissions = [permissions_config]
1057+
else:
1058+
permissions = get_all_configs(permissions_config, "permissions")
10241059
perms_cfg_count = len(permissions)
10251060
LOGGER.log(logging.INFO if perms_cfg_count else logging.WARNING,
10261061
"Found %s permissions configurations.", perms_cfg_count)
10271062
users_settings = groups_settings = None
10281063
if perms_cfg_count:
1029-
users = get_all_configs(permissions_config, "users", allow_missing=True) # type: List[UsersConfig]
1030-
groups = get_all_configs(permissions_config, "groups", allow_missing=True) # type: List[GroupsConfig]
1064+
if isinstance(permissions_config, str):
1065+
users = get_all_configs(permissions_config, "users", allow_missing=True)
1066+
else:
1067+
users = []
1068+
if isinstance(permissions_config, str):
1069+
groups = get_all_configs(permissions_config, "groups", allow_missing=True)
1070+
else:
1071+
groups = []
10311072
users_settings = _resolve_config_registry(users, "username") or {}
10321073
groups_settings = _resolve_config_registry(groups, "name") or {}
10331074
for i, perms in enumerate(permissions):
10341075
LOGGER.info("Processing permissions from configuration (%s/%s).", i + 1, perms_cfg_count)
1035-
_process_permissions(perms, magpie_url, cookies_or_session, users_settings, groups_settings, raise_errors)
1076+
_process_permissions(
1077+
perms,
1078+
magpie_url,
1079+
cookies_or_session,
1080+
users_settings,
1081+
groups_settings,
1082+
settings,
1083+
raise_errors,
1084+
)
10361085
LOGGER.info("All permissions processed.")
10371086

10381087

@@ -1061,16 +1110,23 @@ def _resolve_config_registry(config_files, key):
10611110
return config_map
10621111

10631112

1064-
def _process_permissions(permissions, magpie_url, cookies_or_session, users=None, groups=None, raise_errors=False):
1065-
# type: (PermissionsConfig, Str, Session, Optional[UsersSettings], Optional[GroupsSettings], bool) -> None
1113+
def _process_permissions(
1114+
permissions, # type: PermissionsConfig
1115+
magpie_url, # type: Str
1116+
cookies_or_session, # type: Session
1117+
users=None, # type: Optional[UsersSettings]
1118+
groups=None, # type: Optional[GroupsSettings]
1119+
settings=None, # type: Optional[AnySettingsContainer]
1120+
raise_errors=False, # type: bool
1121+
): # type: (...) -> None
10661122
"""
10671123
Processes a single `permissions` configuration.
10681124
"""
10691125
if not permissions:
10701126
LOGGER.warning("Permissions configuration are empty.")
10711127
return
10721128

1073-
anon_user = get_constant("MAGPIE_ANONYMOUS_USER")
1129+
anon_user = get_constant("MAGPIE_ANONYMOUS_USER", settings)
10741130
perm_count = len(permissions)
10751131
LOGGER.log(logging.INFO if perm_count else logging.WARNING,
10761132
"Found %s permissions to evaluate from configuration.", perm_count)

tests/test_magpie_cli.py

+58-24
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,24 @@
1212
import os
1313
import subprocess
1414
import tempfile
15+
from typing import TYPE_CHECKING
16+
from urllib.parse import urlparse
1517

1618
import mock
1719
import six
1820

1921
from magpie.cli import batch_update_permissions, batch_update_users, magpie_helper_cli
2022
from magpie.constants import get_constant
23+
from magpie.permissions import Permission, PermissionType
24+
from magpie.services import ServiceAPI, ServiceWPS
2125
from tests import runner, utils
2226

2327
if six.PY2:
2428
from backports import tempfile as tempfile2 # noqa # pylint: disable=E0611,no-name-in-module # Python 2
2529
else:
2630
tempfile2 = tempfile # pylint: disable=C0103,invalid-name
31+
if TYPE_CHECKING:
32+
from magpie.typedefs import JSON
2733

2834
KNOWN_HELPERS = [
2935
"batch_update_users",
@@ -120,34 +126,34 @@ def test_magpie_batch_update_permissions_command():
120126
"-P", test_admin_pwd,
121127
]
122128

123-
# cleanup in case of previous failure
129+
def mock_request(method, url, *args, **kwargs):
130+
_path = urlparse(url).path
131+
# because CLI utility does multiple login tests, we must force TestApp logout to forget session
132+
# otherwise, error is raised because of user session mismatch between previous login and new one requested
133+
if _path.startswith("/signin"):
134+
utils.check_or_try_logout_user(test_app)
135+
return utils.test_request(test_app, method, _path, *args, **kwargs)
136+
124137
_, test_admin_cookies = utils.check_or_try_login_user(test_app, username=test_admin_usr, password=test_admin_pwd)
138+
139+
# cleanup in case of previous run
125140
utils.TestSetup.delete_TestUser(test_app, override_user_name=test_usr, override_cookies=test_admin_cookies)
126141
utils.TestSetup.delete_TestGroup(test_app, override_group_name=test_grp, override_cookies=test_admin_cookies)
127142
utils.TestSetup.delete_TestService(test_app, override_service_name=test_api, override_cookies=test_admin_cookies)
128143
utils.TestSetup.delete_TestService(test_app, override_service_name=test_wps, override_cookies=test_admin_cookies)
129144

130-
def mock_request(*args, **kwargs):
131-
method, url, args = args[0], args[1], args[2:]
132-
path = url.replace(test_url, "")
133-
# because CLI utility does multiple login tests, we must force TestApp logout to forget session
134-
# otherwise, error is raised because of user session mismatch between previous login and new one requested
135-
if path.startswith("/signin"):
136-
utils.check_or_try_logout_user(test_app)
137-
return utils.test_request(test_app, method, path, *args, **kwargs)
145+
utils.TestSetup.create_TestService(test_app,
146+
override_service_name=test_api,
147+
override_service_type=ServiceAPI.service_type)
148+
utils.TestSetup.create_TestService(test_app,
149+
override_service_name=test_wps,
150+
override_service_type=ServiceWPS.service_type)
138151

139-
def run_command(operation_name, operation_args):
140-
with tempfile2.TemporaryDirectory() as tmpdir:
141-
with mock.patch("requests.Session.request", side_effect=mock_request):
142-
with mock.patch("requests.request", side_effect=mock_request):
143-
err_code = batch_update_permissions.main(operation_args)
144-
assert err_code == 0, "failed execution due to invalid arguments"
145-
assert len(os.listdir(tmpdir)) == 1, "utility should have produced 1 output file"
146-
file = os.path.join(tmpdir, os.listdir(tmpdir)[0])
147-
utils.check_val_is_in(operation_name, file)
148-
assert os.path.isfile(file)
149-
with open(file, mode="r", encoding="utf-8") as fd:
150-
file_text = fd.read()
152+
def run_command(__operation_name, operation_args):
153+
with mock.patch("requests.Session.request", side_effect=mock_request):
154+
with mock.patch("requests.request", side_effect=mock_request):
155+
err_code = batch_update_permissions.main(operation_args)
156+
assert err_code == 0, "failed execution due to invalid arguments"
151157

152158
with contextlib.ExitStack() as stack:
153159
tmp_file = stack.enter_context(tempfile.NamedTemporaryFile(mode="w", suffix=".json"))
@@ -156,8 +162,8 @@ def run_command(operation_name, operation_args):
156162
{
157163
"group": test_grp,
158164
"service": test_api,
159-
"resource": "/api/v1",
160-
"permission": "read",
165+
"resource": "/v1",
166+
"permission": Permission.READ.value,
161167
"action": "create",
162168
}
163169
]
@@ -173,7 +179,7 @@ def run_command(operation_name, operation_args):
173179
{
174180
"user": test_usr,
175181
"service": test_wps,
176-
"permission": "getcapabilities",
182+
"permission": Permission.GET_CAPABILITIES.value,
177183
"action": "create",
178184
}
179185
]
@@ -186,6 +192,34 @@ def run_command(operation_name, operation_args):
186192
test_args += ["--verbose"]
187193
run_command("batch_update_permissions", test_args)
188194

195+
path = f"/groups/{test_grp}/resources"
196+
resp = utils.test_request(test_app, "GET", path)
197+
body = utils.check_response_basic_info(resp)
198+
res_api = body["resources"][ServiceAPI.service_type][test_api] # type: JSON
199+
res_wps = body["resources"][ServiceWPS.service_type][test_wps] # type: JSON
200+
perms_res_api = res_api["permissions"]
201+
perms_res_wps = res_wps["permissions"]
202+
utils.check_val_equal(len(perms_res_wps), 0)
203+
utils.check_val_equal(len(perms_res_api), 0, msg="Permission should not be created directly on the service.")
204+
res_res_api = res_api["resources"]
205+
utils.check_val_equal(len(res_res_api), 1, msg="Resource should have been created dynamically.")
206+
perms_sub_api = res_res_api[list(res_res_api)[0]]["permissions"] # type: JSON
207+
utils.check_val_equal(len(perms_sub_api), 1)
208+
utils.check_val_equal(perms_sub_api[0]["name"], Permission.READ.value)
209+
utils.check_val_equal(perms_sub_api[0]["type"], PermissionType.INHERITED.value)
210+
211+
path = f"/users/{test_usr}/resources"
212+
resp = utils.test_request(test_app, "GET", path)
213+
body = utils.check_response_basic_info(resp)
214+
res_api = body["resources"][ServiceAPI.service_type][test_api]
215+
res_wps = body["resources"][ServiceWPS.service_type][test_wps]
216+
perms_res_api = res_api["permissions"]
217+
perms_res_wps = res_wps["permissions"]
218+
utils.check_val_equal(len(perms_res_api), 0)
219+
utils.check_val_equal(len(perms_res_wps), 1)
220+
utils.check_val_equal(perms_res_wps[0]["name"], Permission.GET_CAPABILITIES.value)
221+
utils.check_val_equal(perms_res_wps[0]["type"], PermissionType.DIRECT.value)
222+
189223

190224
@runner.MAGPIE_TEST_CLI
191225
@runner.MAGPIE_TEST_LOCAL

0 commit comments

Comments
 (0)