diff --git a/elab_users/__init__.py b/elab_users/__init__.py index e4e08de..e329ef4 100644 --- a/elab_users/__init__.py +++ b/elab_users/__init__.py @@ -36,7 +36,7 @@ def get_config(svn_dir=SVN_REPOS_PATH, authz=AUTHZ_FILE_NAME): return AuthzConfigParser.from_file(authz_path) -def list_users(username, svn_dir=SVN_REPOS_PATH, authz=AUTHZ_FILE_NAME): +def list_users(svn_dir=SVN_REPOS_PATH, authz=AUTHZ_FILE_NAME): """list all users""" config = get_config(svn_dir, authz) groups = config.group_users() @@ -52,7 +52,7 @@ def show_group_info(groupname, svn_dir=SVN_REPOS_PATH, authz=AUTHZ_FILE_NAME): groups = config.group_users() if groupname not in groups: sys.exit(f"Group '{groupname}' not found in authz file") - print("Users in group '{groupname}':") + print(f"Users in group '{groupname}':") for name in sorted(groups[groupname]): print(f" {name}") @@ -69,7 +69,7 @@ def add_new_user( config = get_config(svn_dir, authz) if username in config.elab_users: sys.exit(f"Username '{username}' already in use") - if username.lower() in {i.lower() for i in svn_dir.iterdir()}: + if username.lower() in {i.name.lower() for i in svn_dir.iterdir()}: sys.exit(f"Username '{username}' not allowed") user = config.add_journal_acl_for(username, group) user.create_new_repository(svn_dir, handler) @@ -104,6 +104,7 @@ def retire_user( config.move_user_to_alumni(username) config.write_to_file() user.delete_password(svn_dir / htpwd, handler=handler) + print(f"Moved user {username} to alumni") def change_password( @@ -129,7 +130,7 @@ def show_user_info(username, svn_dir=SVN_REPOS_PATH, authz=AUTHZ_FILE_NAME): sys.exit("User {username} not found.") user = config.elab_users[username] - print("User {user.name} is in group '{user.group}':") + print(f"User {user.name} is in group '{user.group}':") # print the write acls for a user if user.group == ADMINS: @@ -172,6 +173,7 @@ def main( authz=AUTHZ_FILE_NAME, htpwd=HTPWD_FILE_NAME, handler=subprocess, + cli_args=None, ): parser = argparse.ArgumentParser(prog="elab-users") parser.add_argument( @@ -182,30 +184,23 @@ def main( parser.add_argument( "name", nargs="?", help="user or group to perform the command on" ) - args = parser.parse_args() + args = parser.parse_args(cli_args) print(args.command) - print(args.username) + print(args.name) if not args.command: list_users(svn_dir, authz) - - if args.command.lower() not in COMMANDS: + elif args.command.lower() not in COMMANDS: show_user_info(args.command, svn_dir, authz) - - if args.command.lower() == "user": + elif args.command.lower() == "user": show_user_info(args.name, svn_dir, authz) - - if args.command.lower() == "groups": + elif args.command.lower() == "groups": show_group_info(args.name, svn_dir, authz) - - if args.command.lower() == "add": + elif args.command.lower() == "add": add_new_user(args.name, USERS, svn_dir, authz, htpwd, handler) - - if args.command.lower() == "restricted": + elif args.command.lower() == "restricted": add_new_user(args.name, RESTRICTED, svn_dir, authz, htpwd, handler) - - if args.command.lower() == "retire": + elif args.command.lower() == "retire": retire_user(args.name, svn_dir, authz, htpwd, handler) - - if args.command.lower() == "password": + elif args.command.lower() == "password": change_password(args.name, svn_dir, authz, htpwd, handler) diff --git a/test-data/authz b/test-data/authz index b862eec..920cdda 100644 --- a/test-data/authz +++ b/test-data/authz @@ -1,6 +1,6 @@ [groups] administrators = JuergenRuehe, OswaldPrucker -alumni = AlexeyKopyshev, AndreasBoenisch, AndreasEver, AnkeWoerz, AnneLoesche, ArthurMartens, ArulGeetha, CamillaOestevold, CanerKaganaslan, ChristianSchuh, ChristineBunte, CkPandiyarajan, CleoStannard, FanWu, GerhardBaaken, GinoRodriguez, GuillermoBenites, HeikeHaller, IrenaEipert, JacobBelardi, JenniferPfau, JoachimLauterwasser, JohannesBaader, KatrinMoosmann, KeLi, KerstinSchuh, KimberlySimancas, MarcoArmbruster, MariaVoehringer, MariaVohringer, MartinRendl, MartinVellinger, MartinaAuerswald, MatthiasLischka, MessRechner, MichaelaFrase, MiriamScheckenbach, MonicaPerez, NinoLomadze, Nongluck, OliverDornfeld, PeterZahn, PhilippDiefenthaler, PhilippWollermann, RebeccaBlell, RodrigoNavarro, SaraFuchs, SebastianBoehmer, SebastianSebald, SimonBodendorfer, SimonSchuster, SirasaYodmongkol, ThidaratWangkam, TobiasHeitzler, TobiasKoenig, TristanBourrel, UlrikeRiehle, ViVek, VinicioCarias, WolfgangEhm, YnSekhar, ZouStaarter +alumni = AlexeyKopyshev, AndreasBoenisch, AndreasEvers, AnkeWoerz, AnneLoesche, ArthurMartens, ArulGeetha, CamillaOestevold, CanerKaganaslan, ChristianSchuh, ChristineBunte, CkPandiyarajan, CleoStannard, FanWu, GerhardBaaken, GinoRodriguez, GuillermoBenites, HeikeHaller, IrenaEipert, JacobBelardi, JenniferPfau, JoachimLauterwasser, JohannesBaader, KatrinMoosmann, KeLi, KerstinSchuh, KimberlySimancas, MarcoArmbruster, MariaVoehringer, MariaVohringer, MartinRendl, MartinVellinger, MartinaAuerswald, MatthiasLischka, MessRechner, MichaelaFrase, MiriamScheckenbach, MonicaPerez, NinoLomadze, Nongluck, OliverDornfeld, PeterZahn, PhilippDiefenthaler, PhilippWollermann, RebeccaBlell, RodrigoNavarro, SaraFuchs, SebastianBoehmer, SebastianSebald, SimonBodendorfer, SimonSchuster, SirasaYodmongkol, ThidaratWangkam, TobiasHeitzler, TobiasKoenig, TristanBourrel, UlrikeRiehle, ViVek, VinicioCarias, WolfgangEhm, YnSekhar, ZouStaarter users = AlexanderDietz, AliciaMalekLuz, AndreasMader, AnnaSchuler, AnneBuderer, ChristophScheibelein, CrispinAmiriNaini, DanielaMoessner, DavidBoschert, DavidSchwaerzle, EstherRiga, FrankScherag, FranziskaDorner, GregorOsterwinter, HeidiPerez, HolgerFrey, JanNiklasSchoenberg, JonGreen, KarenLienkamp, KatyaSergeeva, LauraHerrera, MalwinaPajestka, MaraFlorea, MarcZinggeler, MarcelHoffmann, MarcelRothfelder, MartinKoerner, MartinSchoenstein, MatthiasMenzel, MelanieEichhorn, MichaelHenze, MonikaKurowska, MostafaMahmoud, NataliaSchatz, NicoleBirsner, NilsKorf, PengZou, PetraHettich, PhilipKotrade, RaduCristianMutihac, RomanErath, SamarKazan, SaschaEngel, SebastianBonaus, ShararehSahneh, SureshReddyBanda, ThananthornKanokwijitsilp, ThomasBrandstetter, TianyangZheng, VanessaWeiss, VitaliyKondrashov, WibkeHartleb, XiaoqiangHou, ZhuolingDeng restricted = BeniPrasser, JuliaSaar, SimonZunker, UrmilShah, YongZhou @@ -24,6 +24,13 @@ AlexanderDietz = rw @alumni = UrmilShah = r +[OswaldPrucker:/] +@administrators = rw +@users = r +@restricted = +@alumni = +OswaldPrucker = rw + [UrmilShah:/] @administrators = rw @users = r diff --git a/tests/test_elab_users.py b/tests/test_elab_users.py index 69e728f..bc7a43e 100644 --- a/tests/test_elab_users.py +++ b/tests/test_elab_users.py @@ -23,19 +23,364 @@ mistakes. import pytest +INFO_RESULT_AD = """ +User AlexanderDietz is in group 'users': + Write access is granted to: AlexanderDietz:/ + Read access is granted to (nearly) all journals. +Labjournal AlexanderDietz:/ + Write access granted to: @administrators, AlexanderDietz + Read access granted to: @users +""" + +INFO_RESULT_OP = """ +User OswaldPrucker is in group 'administrators': + Write access is granted to all journals. + Read access is granted to all journals. +Labjournal OswaldPrucker:/ + Write access granted to: @administrators, OswaldPrucker + Read access granted to: @users +""" + + +INFO_RESULT_AE = """ +User AndreasEvers is in group 'alumni': + Write access is NOT granted to any journals + Read access is NOT granted to any journals +Labjournal AndreasEvers:/ + Write access granted to: @administrators + Read access granted to: @users, UrmilShah +""" + + +INFO_RESULT_US = """ +User UrmilShah is in group 'restricted': + Write access is granted to: UrmilShah:/ + Read access is granted to: AndreasEvers:/ +Labjournal UrmilShah:/ + Write access granted to: @administrators, UrmilShah + Read access granted to: @users +""" + + +@pytest.mark.fun +def test_get_config(example_authz): + from elab_users import get_config + from elab_users.authz import AuthzConfigParser + + tmp_dir = example_authz.parent + parser = get_config(tmp_dir, "authz") + + assert isinstance(parser, AuthzConfigParser) + assert parser.elab_users != {} + + +@pytest.mark.fun +def test_get_config_missing_file(example_authz): + from elab_users import get_config + + tmp_dir = example_authz.parent + with pytest.raises(SystemExit): + get_config(tmp_dir, "not existent") + + +@pytest.mark.fun +def test_list_users(example_authz, capsys): + from elab_users import list_users + + list_users(example_authz.parent, example_authz.name) + captured = capsys.readouterr() + + admins = "Users in group 'administrators':\n JuergenRuehe\n" + assert admins in captured.out + users = "Users in group 'users':\n AlexanderDietz\n" + assert users in captured.out + restricted = "Users in group 'restricted':\n BeniPrasser\n" + assert restricted in captured.out + alumni = "Users in group 'alumni':\n AlexeyKopyshev\n" + assert alumni in captured.out + + +@pytest.mark.fun +def test_show_group_info(example_authz, capsys): + from elab_users import show_group_info + + show_group_info("alumni", example_authz.parent, example_authz.name) + captured = capsys.readouterr() + + alumni = "Users in group 'alumni':\n AlexeyKopyshev\n" + assert captured.out.startswith(alumni) + + +@pytest.mark.fun +def test_show_group_info_unknown_group(example_authz): + from elab_users import show_group_info + + with pytest.raises(SystemExit): + show_group_info("unknown", example_authz.parent, example_authz.name) + + +@pytest.mark.fun +def test_add_new_user(example_authz, example_htpasswd, stub_handler, capsys): + from elab_users import get_config, add_new_user + + add_new_user( + "JaneDoe", + "users", + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + ) + captured = capsys.readouterr() + + assert captured.out.startswith("New password for :") + assert "username: JaneDoe" in captured.out + url = "https://svn.cpi.imtek.uni-freiburg.de/JaneDoe" + assert f"url: {url}" in captured.out + + assert stub_handler.stack[0].args[:2] == ["svnadmin", "create"] + assert stub_handler.stack[0].args[2].endswith("/JaneDoe") + + config = get_config(example_authz.parent, example_authz.name) + user = config.elab_users["JaneDoe"] + assert user.group == "users" + assert user.read_acl == set() + assert user.write_acl == {"JaneDoe"} + + items = config.items("JaneDoe:/") + assert sorted(items) == [ + ("@administrators", "rw"), + ("@alumni", ""), + ("@restricted", ""), + ("@users", "r"), + ("JaneDoe", "rw"), + ] + + +@pytest.mark.fun +def test_add_new_user_error_on_existing_user( + example_authz, example_htpasswd, stub_handler +): + from elab_users import add_new_user + + with pytest.raises(SystemExit): + add_new_user( + "AlexanderDietz", + "users", + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + ) + + +@pytest.mark.fun +def test_add_new_user_error_on_forbidden_name( + example_authz, example_htpasswd, stub_handler +): + from elab_users import add_new_user -def test_example_unittest(): - """example unittest + with pytest.raises(SystemExit): + add_new_user( + "authz", + "users", + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + ) - will be run by 'make test' and 'make testall' but not 'make coverage' - """ - assert True + +@pytest.mark.fun +def test_retire_user(example_authz, example_htpasswd, stub_handler, capsys): + + from elab_users import get_config, retire_user + + retire_user( + "AlexanderDietz", + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + ) + captured = capsys.readouterr() + assert captured.out.startswith("Moved user AlexanderDietz to alumni") + + config = get_config(example_authz.parent, example_authz.name) + user = config.elab_users["AlexanderDietz"] + assert user.group == "alumni" + assert user.read_acl == set() + assert user.write_acl == set() + + assert stub_handler.stack[-1].args[:2] == ["htpasswd", "-D"] + + +@pytest.mark.fun +def test_retire_user_error_unknown_user( + example_authz, example_htpasswd, stub_handler +): + + from elab_users import retire_user + + with pytest.raises(SystemExit): + retire_user( + "Unknown", + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + ) + + +@pytest.mark.fun +def test_retire_user_error_already_alumni( + example_authz, example_htpasswd, stub_handler +): + + from elab_users import retire_user + + with pytest.raises(SystemExit): + retire_user( + "CamillaOestevold", + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + ) + + +@pytest.mark.fun +def test_retire_user_error_admin( + example_authz, example_htpasswd, stub_handler +): + + from elab_users import retire_user + + with pytest.raises(SystemExit): + retire_user( + "OswaldPrucker", + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + ) @pytest.mark.fun -def test_example_functional_test(): - """example unittest +def test_change_password( + example_authz, example_htpasswd, stub_handler, capsys +): + + from elab_users import change_password + + change_password( + "AlexanderDietz", + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + ) + captured = capsys.readouterr() + assert captured.out.startswith("New password for :") + + assert stub_handler.stack[-1].args[:2] == ["htpasswd", "-b"] + + +@pytest.mark.fun +def test_change_password_error_unknown_user( + example_authz, example_htpasswd, stub_handler +): + + from elab_users import change_password + + with pytest.raises(SystemExit): + change_password( + "Unknown", + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + ) + + +@pytest.mark.fun +@pytest.mark.parametrize( + "user, result", + [ + ("OswaldPrucker", INFO_RESULT_OP), + ("AlexanderDietz", INFO_RESULT_AD), + ("AndreasEvers", INFO_RESULT_AE), + ("UrmilShah", INFO_RESULT_US), + ], +) +def test_show_user_info(user, result, example_authz, capsys): + from elab_users import show_user_info + + show_user_info( + user, + example_authz.parent, + example_authz.name, + ) + captured = capsys.readouterr() + + assert captured.out.strip() == result.strip() + + +@pytest.mark.fun +def test_show_user_info_error_unknown_user(example_authz): + from elab_users import show_user_info + + with pytest.raises(SystemExit): + show_user_info( + "Unknown", + example_authz.parent, + example_authz.name, + ) + + +@pytest.mark.fun +@pytest.mark.parametrize( + "commands, result", + [ + ([], "Users in group 'restricted':"), + (["OswaldPrucker"], "granted to all journals"), + (["user", "OswaldPrucker"], "granted to all journals"), + (["groups", "alumni"], "Users in group 'alumni':"), + (["add", "JaneDoe"], "url:"), + (["restricted", "JaneDoe"], "url:"), + (["retire", "AlexanderDietz"], "to alumni"), + (["password", "AlexanderDietz"], "username: AlexanderDietz"), + ], +) +def test_main( + commands, result, example_authz, example_htpasswd, stub_handler, capsys +): + from elab_users import main + + main( + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + cli_args=commands, + ) + captured = capsys.readouterr() + + assert result in captured.out + + +@pytest.mark.fun +def test_main_help(example_authz, example_htpasswd, stub_handler, capsys): + from elab_users import main + + with pytest.raises(SystemExit): + main( + example_authz.parent, + example_authz.name, + example_htpasswd.name, + handler=stub_handler, + cli_args=["--help"], + ) + captured = capsys.readouterr() - will be by 'make coverage' and 'make testall' but not 'make test' - """ - assert True + assert "usage: elab-users [-h] [command] [name]" in captured.out