diff --git a/conftest.py b/conftest.py index e32de784c17bb99575ed9469fe93c9c1241d088c..0f82f60eaaa25aebb5ea4c6810f3d0c9faeefe6c 100644 --- a/conftest.py +++ b/conftest.py @@ -1,3 +1,4 @@ +# vim: tw=100 foldmethod=indent # pylint: disable=global-statement import pytest @@ -5,31 +6,34 @@ import pytest from ldf_adapter import config as globalConfig, UserInfo from ldf_adapter import backend as backend_module -TEST_BACKENDS = ['bwidm', 'local_unix'] +TEST_BACKENDS = ["bwidm", "local_unix"] # this is from: # https://docs.pytest.org/en/stable/example/parametrize.html#deferring-the-setup-of-parametrized-resources def pytest_generate_tests(metafunc): - if 'backend' in metafunc.fixturenames: - metafunc.parametrize('backend', TEST_BACKENDS, indirect=True) + if "backend" in metafunc.fixturenames: + metafunc.parametrize("backend", TEST_BACKENDS, indirect=True) + + +test_primary_group = "test_primary_group" -test_primary_group = 'test_primary_group' @pytest.fixture def backend(monkeypatch, request): - monkeypatch.setattr(backend_module, '__backend__', request.param) + monkeypatch.setattr(backend_module, "__backend__", request.param) return backend_module + @pytest.fixture def answers(): - return { - } + return {} + @pytest.fixture def user_data(): return { - 'user' : { - 'userinfo': { + "user": { + "userinfo": { "displayName": "Hardt, Marcus (SCC)", "eduperson_entitlement": [ "urn:geant:kit.edu:group:DFN-SLCS#example.org", @@ -39,13 +43,10 @@ def user_data(): "urn:geant:kit.edu:group:bwUniCluster#example.org", "urn:geant:kit.edu:group:bwsyncnshare#example.org", "urn:geant:kit.edu:group:bwsyncnshare-idm#example.org", - "urn:geant:kit.edu:group:gruppenverwalter#example.org" + "urn:geant:kit.edu:group:gruppenverwalter#example.org", ], "eduperson_principal_name": "lo0018@kit.edu", - "eduperson_scoped_affiliation": [ - "employee@kit.edu", - "member@kit.edu" - ], + "eduperson_scoped_affiliation": ["employee@kit.edu", "member@kit.edu"], "email": "marcus.hardt@kit.edu", "family_name": "Hardt", "givenName": "Marcus", @@ -66,12 +67,10 @@ def user_data(): "/bwUniCluster", "/bwsyncnshare", "/bwsyncnshare-idm", - "/gruppenverwalter" + "/gruppenverwalter", ], } }, - 'answers': { - 'primary_group': 'gruppenverwalter' - }, - 'credentials': {}, + "answers": {"primary_group": "gruppenverwalter"}, + "credentials": {}, } diff --git a/feudal_globalconfig/globalconfig.py b/feudal_globalconfig/globalconfig.py index 1471e7c692db06b2aa83267d17a7ac968442a210..c10ef9257f7643be1c7e680ee0d1173fe8c163bd 100644 --- a/feudal_globalconfig/globalconfig.py +++ b/feudal_globalconfig/globalconfig.py @@ -1,4 +1,4 @@ -'''Capture global config''' +"""Capture global config""" if not "config" in globals(): config = {} diff --git a/ldf_adapter/backend/local_unix.py b/ldf_adapter/backend/local_unix.py index 104c5e43af50690c860dc3c788407dd17a17b21e..99278b2b22b0efd9368627db734b2d30cf63b540 100644 --- a/ldf_adapter/backend/local_unix.py +++ b/ldf_adapter/backend/local_unix.py @@ -631,10 +631,11 @@ class Group(generic.Group): return {group[ID_FIELD]: group for group in groups} + def make_shadow_compatible(orig_word) -> str: """Make shadow compatible, using a configured function""" mode = CONFIG.backend.local_unix.shadow_compatibility_function - logger.error(F"MODE: {mode}") + logger.error(f"MODE: {mode}") if mode == "v044": logger.warning("v044") return make_shadow_compatible_v044(orig_word) @@ -712,7 +713,7 @@ def make_shadow_compatible_default(orig_word) -> str: if excess_chars > 0: if len(word.split("_")) == 1: # no '_' found: word = "__" + word[excess_chars + 2 :] - logger.warning(F"shortened {orig_word} to {word}") + logger.warning(f"shortened {orig_word} to {word}") elif len(word.split("_")) > 1: # at least one '_' found: fragments = word.split("_") diff --git a/setup.py b/setup.py index f8e1ff343fec595134f548bfcafa95bce55fabc0..230a2741feb8ed2ec39bbb5964387951a363af4f 100755 --- a/setup.py +++ b/setup.py @@ -3,6 +3,6 @@ import setuptools setuptools.setup( - setup_requires=['pbr>=1.8'], + setup_requires=["pbr>=1.8"], pbr=True, ) diff --git a/subiss-to-unix.py b/subiss-to-unix.py index 978ceefdb8163086fc3a2e9af1dded5959eeb8d1..eabd849f2c8cce1bdd58d32b05bfee67b623e810 100755 --- a/subiss-to-unix.py +++ b/subiss-to-unix.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# pylint +# pylint # vim: tw=100 # pylint: disable=bad-continuation, invalid-name, superfluous-parens # pylint: disable=bad-whitespace, mixed-indentation @@ -23,8 +23,8 @@ CONFIG = ConfigParser(interpolation=ExtendedInterpolation()) CONFIG.optionxform = lambda option: option # Logging -logformat='[%(levelname)s] [%(filename)s:%(funcName)s:%(lineno)d] %(message)s' -logging.basicConfig(level=os.environ.get("LOG", "WARN"), format = logformat) +logformat = "[%(levelname)s] [%(filename)s:%(funcName)s:%(lineno)d] %(message)s" +logging.basicConfig(level=os.environ.get("LOG", "WARN"), format=logformat) logger = logging.getLogger(__name__) # Functions @@ -36,16 +36,16 @@ def load_config(): """ files = [] try: - files += [ Path(args.pathconf_file) ] + files += [Path(args.pathconf_file)] except: pass logger.info("reading config") files += [ - Path('./subiss-to-unix.conf'), - Path(Path.home(),'.config','subiss-to-unix.conf'), - Path('/etc/subiss-to-unix.conf') + Path("./subiss-to-unix.conf"), + Path(Path.home(), ".config", "subiss-to-unix.conf"), + Path("/etc/subiss-to-unix.conf"), ] for f in files: @@ -54,33 +54,43 @@ def load_config(): CONFIG.read(f) break + def remove_quotes(data): return data.lstrip('"').lstrip("'").rstrip('"').rstrip("'") + def parseOptions(): - '''Parse the commandline options''' + """Parse the commandline options""" path_of_executable = os.path.realpath(sys.argv[0]) folder_of_executable = os.path.split(path_of_executable)[0] full_name_of_executable = os.path.split(path_of_executable)[1] - name_of_executable = full_name_of_executable.rstrip('.py') + name_of_executable = full_name_of_executable.rstrip(".py") parser = argparse.ArgumentParser() - parser.add_argument('--rest_user', '-u', help='username for LDF rest interface') - parser.add_argument('--rest_passwd', '-p', help='passwdname for LDF rest interface') - parser.add_argument('--iss' , action="append") - parser.add_argument('--bwidmOrgId' , default="hdf") - parser.add_argument('--base_url' , default="https://bwidm-test.scc.kit.edu/rest/") - parser.add_argument('--verify_tls' , default=True , action="store_false" , help='disable verify') - parser.add_argument('--issTranslateExpression', default='{"unity-hdf": "unity.helmholtz-data-federation.de/oauth2", "kit": "https://oidc.scc.kit.edu/auth/realms/kit"}') - parser.add_argument(dest='sub_iss' , help='Content of $REMOTE_USER. For testing use "test-offline" and "test-id"') - parser.add_argument('--verbose', '-v' , default=False , action="store_true" ) - parser.add_argument('--debug', '-d' , default=False , action="store_true" ) + parser.add_argument("--rest_user", "-u", help="username for LDF rest interface") + parser.add_argument("--rest_passwd", "-p", help="passwdname for LDF rest interface") + parser.add_argument("--iss", action="append") + parser.add_argument("--bwidmOrgId", default="hdf") + parser.add_argument("--base_url", default="https://bwidm-test.scc.kit.edu/rest/") + parser.add_argument( + "--verify_tls", default=True, action="store_false", help="disable verify" + ) + parser.add_argument( + "--issTranslateExpression", + default='{"unity-hdf": "unity.helmholtz-data-federation.de/oauth2", "kit": "https://oidc.scc.kit.edu/auth/realms/kit"}', + ) + parser.add_argument( + dest="sub_iss", + help='Content of $REMOTE_USER. For testing use "test-offline" and "test-id"', + ) + parser.add_argument("--verbose", "-v", default=False, action="store_true") + parser.add_argument("--debug", "-d", default=False, action="store_true") args = parser.parse_args() # sanitize some args: - args.base_url = args.base_url.rstrip('/') + args.base_url = args.base_url.rstrip("/") args.bwidmOrgId = remove_quotes(args.bwidmOrgId) # ensure translation will work as JSON @@ -88,75 +98,89 @@ def parseOptions(): args.issTranslateExpressionJSON = json.loads(args.issTranslateExpression) try: for key in args.issTranslateExpressionJSON.keys(): - args.issTranslateExpressionJSON[key] = remove_quotes(args.issTranslateExpressionJSON[key]) + args.issTranslateExpressionJSON[key] = remove_quotes( + args.issTranslateExpressionJSON[key] + ) except: - sys.stderr.write('FATAL: issTranslateExpression needs to be a one line json object that lists keys and values: \n') - sys.stderr.write('{"unity-hdf": "unity.helmholtz-data-federation.de/oauth2", "test": "https://test.com"}') - sys.stderr.write('Instead you provided "%s"\n' % str(args.issTranslateExpression)) + sys.stderr.write( + "FATAL: issTranslateExpression needs to be a one line json object that lists keys and values: \n" + ) + sys.stderr.write( + '{"unity-hdf": "unity.helmholtz-data-federation.de/oauth2", "test": "https://test.com"}' + ) + sys.stderr.write( + 'Instead you provided "%s"\n' % str(args.issTranslateExpression) + ) raise except: raise return args + args = parseOptions() load_config() -if args.sub_iss == 'test-offline': - sys.stdout.write('hdf_marcus\n') - exit(0) +if args.sub_iss == "test-offline": + sys.stdout.write("hdf_marcus\n") + exit(0) -if args.sub_iss == 'test-id': +if args.sub_iss == "test-id": args.sub_iss = "6c611e2a-2c1c-487f-9948-c058a36c8f0e@https://login.helmholtz-data-federation.de/oauth2" sys.stderr.write("using test id: %s\n" % args.sub_iss) -if args.sub_iss == 'test-marcus': +if args.sub_iss == "test-marcus": args.sub_iss = "6c611e2a-2c1c-487f-9948-c058a36c8f0e@https://login.helmholtz-data-federation.de/oauth2" sys.stderr.write("using test id: %s\n" % args.sub_iss) -if args.sub_iss == 'test-borja-old': +if args.sub_iss == "test-borja-old": args.sub_iss = "d9f4d895-6051-4717-883e-4b2676ad0d0d@https://login.helmholtz-data-federation.de/oauth2" sys.stderr.write("using test id: %s\n" % args.sub_iss) -if args.sub_iss == 'test-borja-new': +if args.sub_iss == "test-borja-new": args.sub_iss = "309ed509-c56a-4894-b163-5993bd08cbc2@https://login.helmholtz-data-federation.de/oauth2" sys.stderr.write("using test id: %s\n" % args.sub_iss) externalId = args.sub_iss -vals = args.sub_iss.split('@') -sub = '@'.join(vals[0:-1]) # First few components are sub, may contain '@' -iss = vals[-1] # Last component is issuer may NOT contain '@' -externalId = ul.quote_plus(sub) + \ - '@n' + \ - ul.quote_plus(iss) - -url = args.base_url + '/external-user/find/externalId/' + ul.quote_plus(str(externalId)) -rest_user = CONFIG['main'].get('rest_user', 'xxx') -rest_pass = CONFIG['main'].get('rest_pass', 'xxx') -base_url = CONFIG['main'].get('base_url', '') +vals = args.sub_iss.split("@") +sub = "@".join(vals[0:-1]) # First few components are sub, may contain '@' +iss = vals[-1] # Last component is issuer may NOT contain '@' +externalId = ul.quote_plus(sub) + "@n" + ul.quote_plus(iss) + +url = args.base_url + "/external-user/find/externalId/" + ul.quote_plus(str(externalId)) +rest_user = CONFIG["main"].get("rest_user", "xxx") +rest_pass = CONFIG["main"].get("rest_pass", "xxx") +base_url = CONFIG["main"].get("base_url", "") if args.verbose: - logger.debug(F"URL: {url}") + logger.debug(f"URL: {url}") -resp = requests.get (url, verify=args.verify_tls, auth=(rest_user, rest_pass)) +resp = requests.get(url, verify=args.verify_tls, auth=(rest_user, rest_pass)) if resp.status_code != 200: - sys.stderr.write('Error %d reading from remote: \n%s\n'% (resp.status_code, str(resp.text))) + sys.stderr.write( + "Error %d reading from remote: \n%s\n" % (resp.status_code, str(resp.text)) + ) exit(1) -resp_json=resp.json() +resp_json = resp.json() try: - username = resp_json['attributeStore']['urn:oid:0.9.2342.19200300.100.1.1'] - bwIdmOrgId = resp_json['attributeStore']['http://bwidm.de/bwidmOrgId'] + username = resp_json["attributeStore"]["urn:oid:0.9.2342.19200300.100.1.1"] + bwIdmOrgId = resp_json["attributeStore"]["http://bwidm.de/bwidmOrgId"] - sys.stdout.write('%s_%s\n' % (bwIdmOrgId, username)) + sys.stdout.write("%s_%s\n" % (bwIdmOrgId, username)) except KeyError as e: if args.verbose: # sys.stderr.write('Error interpreting remote json object: Key Error: %s not found\n' % str(e)) - sys.stderr.write('Error: I could not find the username in the database. Most likely the user is not registered for this service\n') - sys.stderr.write('This is the json data received\n') - sys.stderr.write(json.dumps(resp_json, sort_keys=True, indent=4, separators=(',', ': '))) - sys.stderr.write('\n') + sys.stderr.write( + "Error: I could not find the username in the database. Most likely the user is not registered for this service\n" + ) + sys.stderr.write("This is the json data received\n") + sys.stderr.write( + json.dumps(resp_json, sort_keys=True, indent=4, separators=(",", ": ")) + ) + sys.stderr.write("\n") if args.debug: - sys.stderr.write('This is the json data received\n') - sys.stderr.write(json.dumps(resp_json, sort_keys=True, indent=4, separators=(',', ': '))) - sys.stderr.write('\n') - + sys.stderr.write("This is the json data received\n") + sys.stderr.write( + json.dumps(resp_json, sort_keys=True, indent=4, separators=(",", ": ")) + ) + sys.stderr.write("\n") diff --git a/tests/backend/local_unix.py b/tests/backend/local_unix.py index 0d664a8d325b1cc26f3f5d21e80034d080732ffd..ff226be0a78c73e713abe7b73c5fc973e630d233 100644 --- a/tests/backend/local_unix.py +++ b/tests/backend/local_unix.py @@ -430,7 +430,10 @@ def test_make_shadow_compatible_length(raw): """a shadow-compatible name must be at most 32 characters long""" assert len(ldf_adapter.backend.local_unix.make_shadow_compatible(raw)) <= 32 -@pytest.mark.parametrize("raw", [x[0] for x in INPUT_SHADOW_COMPATIBLE + INPUT_SHADOW_COMPATIBLE_V044]) + +@pytest.mark.parametrize( + "raw", [x[0] for x in INPUT_SHADOW_COMPATIBLE + INPUT_SHADOW_COMPATIBLE_V044] +) def test_make_shadow_compatible_length_v044(raw): assert len(ldf_adapter.backend.local_unix.make_shadow_compatible_v044(raw)) <= 32 @@ -443,14 +446,15 @@ def test_make_shadow_compatible_allowed_chars(raw): word = ldf_adapter.backend.local_unix.make_shadow_compatible(raw) assert regex.match(r"[a-z_]", word[0]) and regex.match(r"[-0-9_a-z]", word) -@pytest.mark.parametrize("raw", [x[0] for x in INPUT_SHADOW_COMPATIBLE + INPUT_SHADOW_COMPATIBLE_V044]) + +@pytest.mark.parametrize( + "raw", [x[0] for x in INPUT_SHADOW_COMPATIBLE + INPUT_SHADOW_COMPATIBLE_V044] +) def test_make_shadow_compatible_allowed_chars_v044(raw): word = ldf_adapter.backend.local_unix.make_shadow_compatible_v044(raw) assert regex.match(r"[a-z_]", word[0]) and regex.match(r"[-0-9_a-z]", word) - - @pytest.mark.parametrize("raw,cooked", INPUT_SHADOW_COMPATIBLE) def test_make_shadow_compatible(monkeypatch, raw, cooked): """expected behaviour: @@ -476,6 +480,7 @@ def test_make_shadow_compatible(monkeypatch, raw, cooked): ) assert ldf_adapter.backend.local_unix.make_shadow_compatible(raw) == cooked + @pytest.mark.parametrize("raw,cooked", INPUT_SHADOW_COMPATIBLE_V044) def test_make_shadow_compatible_v044(monkeypatch, raw, cooked): monkeypatch.setattr( @@ -483,6 +488,8 @@ def test_make_shadow_compatible_v044(monkeypatch, raw, cooked): "v044", ) assert ldf_adapter.backend.local_unix.make_shadow_compatible(raw) == cooked + + @pytest.mark.parametrize("raw,cooked", INPUT_SHADOW_COMPATIBLE_PUNCH4NFDI) def test_make_shadow_compatible_punch(monkeypatch, raw, cooked): monkeypatch.setattr( @@ -507,6 +514,8 @@ def test_make_shadow_compatible_fail(raw): """ with pytest.raises(ValueError): ldf_adapter.backend.local_unix.make_shadow_compatible(raw) + + @pytest.mark.parametrize("raw", INPUT_SHADOW_COMPATIBLE_FAIL_V044) def test_make_shadow_compatible_fail_v044(raw): with pytest.raises(ValueError):