Loading client/Dockerfile +4 −1 Original line number Diff line number Diff line Loading @@ -21,7 +21,8 @@ COPY *.py \ vos_group \ vos_import \ vos_job \ vos_storage /usr/bin/vos_cli/ vos_storage \ vos_user /usr/bin/vos_cli/ RUN chmod -R 755 /usr/bin/vos_cli # Copy bash-completion scripts Loading @@ -45,7 +46,9 @@ WORKDIR /home/client/ RUN echo ". /usr/share/bash-completion/completions/vos_data" >> .bashrc && \ echo ". /usr/share/bash-completion/completions/vos_group" >> .bashrc && \ echo ". /usr/share/bash-completion/completions/vos_job" >> .bashrc && \ echo ". /usr/share/bash-completion/completions/vos_user" >> .bashrc && \ echo ". /usr/share/bash-completion/completions/vos_storage" >> .bashrc # Install python dependencies RUN pip3.9 install --no-cache-dir redis tabulate client/config/bash_completion/vos_user 0 → 100644 +20 −0 Original line number Diff line number Diff line # # This file is part of vospace-transfer-service # Copyright (C) 2021 Istituto Nazionale di Astrofisica # SPDX-License-Identifier: GPL-3.0-or-later # _vos_user() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="add del search" if [[ ${cur} == add || ${cur} == del || ${cur} == search || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } complete -F _vos_user vos_user client/config/vos_cli.conf +3 −0 Original line number Diff line number Diff line Loading @@ -26,3 +26,6 @@ rpc_queue = storage_queue [vos_group] rpc_queue = group_queue [vos_user] rpc_queue = user_queue client/vos_user 0 → 100644 +207 −0 Original line number Diff line number Diff line #!/usr/bin/env python # # This file is part of vospace-transfer-service # Copyright (C) 2021 Istituto Nazionale di Astrofisica # SPDX-License-Identifier: GPL-3.0-or-later # import sys from redis_rpc_client import RedisRPCClient from config import Config from tabulate import tabulate class VOSUser(RedisRPCClient): def __init__(self): config = Config("/etc/vos_cli/vos_cli.conf") params = config.loadSection("server") self.host = params["host"] self.port = params.getint("port") self.db = params.getint("db") params = config.loadSection("vos_user") self.rpcQueue = params["rpc_queue"] super(VOSUser, self).__init__(self.host, self.port, self.db, self.rpcQueue) def add(self): userId = None username = None email = None while not userId: try: userId = input("\nUser ID: ") except ValueError: print("Input type is not valid!") except EOFError: print("\nPlease, use CTRL+C to quit.") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") while not username: try: username = input("\nUsername: ") except ValueError: print("Input type is not valid!") except EOFError: print("\nPlease, use CTRL+C to quit.") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") while not email: # Add email validation here try: email = input("\nE-mail: ") except ValueError: print("Input type is not valid!") except EOFError: print("\nPlease, use CTRL+C to quit.") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") userRequest = { "requestType": "USER_ADD", "userId": userId, "username": username, "email": email } userResponse = self.call(userRequest) if "responseType" not in userResponse: sys.exit("FATAL: Malformed response, acknowledge expected.\n") elif userResponse["responseType"] == "USER_ADD_DONE": print("\nUser added successfully!\n") elif userResponse["responseType"] == "ERROR": errorCode = userResponse["errorCode"] errorMsg = userResponse["errorMsg"] sys.exit(f"\nError code: {errorCode}\nError message: {errorMsg}\n") else: sys.exit("\nFATAL: Unknown response type.\n") def delete(self): userRequest = { "requestType": "USER_DEL_REQ" } userResponse = self.call(userRequest) if "responseType" not in userResponse: sys.exit("FATAL: Malformed response, acknowledge expected.\n") elif userResponse["responseType"] == "USER_DEL_ACK": userList = userResponse["userList"] if not userList: sys.exit("\nNo username found.\n") userIdList = [] for user in userList: userIdList.append(user["user_id"]) userId = None while not userId in userIdList: try: userId = input("\nPlease, insert a valid user ID: ") except ValueError: print("Input type is not valid!") except EOFError: print("\nPlease, use CTRL+C to quit.") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") print("\n!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!") print("! This operation will remove the selected user only !") print("! from the database. !") print("! The user on the transfer node will not be removed, !") print("! you must do it manually. !") print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n") confirm = None while not confirm in ( "yes", "no" ): try: confirm = input("Are you sure to proceed? [yes/no]: ") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") except EOFError: print("\nPlease, use CTRL+C to quit.") if confirm == "yes": confirmRequest = { "requestType": "USER_DEL_CON", "userId": userId } confirmResponse = self.call(confirmRequest) if "responseType" not in confirmResponse: sys.exit("\nFATAL: Malformed response, confirmation expected.\n") elif confirmResponse["responseType"] == "USER_DEL_DONE": print("\nUser deleted successfully!\n") elif confirmResponse["responseType"] == "ERROR": errorCode = confirmResponse["errorCode"] errorMsg = confirmResponse["errorMsg"] sys.exit(f"\nError code: {errorCode}\nError message: {errorMsg}\n") else: sys.exit("\nFATAL: Unknown response type.\n") elif userResponse["responseType"] == "ERROR": errorCode = userResponse["errorCode"] errorMsg = userResponse["errorMsg"] sys.exit(f"Error code: {errorCode}\nError message: {errorMsg}\n") else: sys.exit("\nFATAL: Unknown response type.\n") def search(self, searchStr): userRequest = { "requestType": "USER_SEARCH", "searchStr": searchStr } userResponse = self.call(userRequest) if "responseType" not in userResponse: sys.exit("FATAL: Malformed response.\n") elif userResponse["responseType"] == "SEARCH_DONE": userSearch = userResponse["userSearch"] if userSearch: print("\n" + tabulate(userResponse["userSearch"], headers = "keys", tablefmt = "pretty") + "\n") else: sys.exit(f"\nThe search did not return any results.\n") elif userResponse["responseType"] == "ERROR": errorCode = userResponse["errorCode"] errorMsg = userResponse["errorMsg"] sys.exit(f"\nError code: {errorCode}\nError message: {errorMsg}\n") else: sys.exit("\nFATAL: Unknown response type.\n") def help(self): sys.exit(""" NAME vos_user SYNOPSYS vos_user COMMAND [ARGUMENT] DESCRIPTION Client tool to manage VOSpace users in the database. The client accepts only one (mandatory) command at a time. A list of supported commands is shown here below: add adds a user to the database del deletes a user from the database search performs a search on users and returns those having a match between the search string passed via command line and one of the following fields: 'user_id', 'user_name', 'e_mail' Adding a user to the database requires a user ID, a username and an e-mail address. A valid userID is required when deleting a user from the database. IMPORTANT NOTE: the VOSpace Transfer Service automatically populates the 'users' table in the database by previously quering the authentication system (RAP). So, please, use this client only if you need to handle situations involing users that for some reason are not recognized by the authentication system. """) # Create new VOSUser object vosUserCli = VOSUser() # Check the number of input args if len(sys.argv) == 2: script, cmd = sys.argv if cmd == "add": vosUserCli.add() elif cmd == "del": vosUserCli.delete() else: vosUserCli.help() elif len(sys.argv) == 3: script, cmd, arg = sys.argv if cmd == "search": vosUserCli.search(arg) else: vosUserCli.help() else: vosUserCli.help() transfer_service/cli_handler.py +3 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from group_rw_rpc_server import GroupRwRPCServer from import_rpc_server import ImportRPCServer from job_rpc_server import JobRPCServer from storage_rpc_server import StorageRPCServer from user_rpc_server import UserRPCServer class CliHandler(object): Loading @@ -28,6 +29,8 @@ class CliHandler(object): self.rpcServerList.append(DataRPCServer(self.host, self.port, self.db, rpcQueue)) elif srvType == "group_rw": self.rpcServerList.append(GroupRwRPCServer(self.host, self.port, self.db, rpcQueue)) elif srvType == "user": self.rpcServerList.append(UserRPCServer(self.host, self.port, self.db, rpcQueue)) elif srvType == "import": self.rpcServerList.append(ImportRPCServer(self.host, self.port, self.db, rpcQueue)) elif srvType == "storage": Loading Loading
client/Dockerfile +4 −1 Original line number Diff line number Diff line Loading @@ -21,7 +21,8 @@ COPY *.py \ vos_group \ vos_import \ vos_job \ vos_storage /usr/bin/vos_cli/ vos_storage \ vos_user /usr/bin/vos_cli/ RUN chmod -R 755 /usr/bin/vos_cli # Copy bash-completion scripts Loading @@ -45,7 +46,9 @@ WORKDIR /home/client/ RUN echo ". /usr/share/bash-completion/completions/vos_data" >> .bashrc && \ echo ". /usr/share/bash-completion/completions/vos_group" >> .bashrc && \ echo ". /usr/share/bash-completion/completions/vos_job" >> .bashrc && \ echo ". /usr/share/bash-completion/completions/vos_user" >> .bashrc && \ echo ". /usr/share/bash-completion/completions/vos_storage" >> .bashrc # Install python dependencies RUN pip3.9 install --no-cache-dir redis tabulate
client/config/bash_completion/vos_user 0 → 100644 +20 −0 Original line number Diff line number Diff line # # This file is part of vospace-transfer-service # Copyright (C) 2021 Istituto Nazionale di Astrofisica # SPDX-License-Identifier: GPL-3.0-or-later # _vos_user() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="add del search" if [[ ${cur} == add || ${cur} == del || ${cur} == search || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } complete -F _vos_user vos_user
client/config/vos_cli.conf +3 −0 Original line number Diff line number Diff line Loading @@ -26,3 +26,6 @@ rpc_queue = storage_queue [vos_group] rpc_queue = group_queue [vos_user] rpc_queue = user_queue
client/vos_user 0 → 100644 +207 −0 Original line number Diff line number Diff line #!/usr/bin/env python # # This file is part of vospace-transfer-service # Copyright (C) 2021 Istituto Nazionale di Astrofisica # SPDX-License-Identifier: GPL-3.0-or-later # import sys from redis_rpc_client import RedisRPCClient from config import Config from tabulate import tabulate class VOSUser(RedisRPCClient): def __init__(self): config = Config("/etc/vos_cli/vos_cli.conf") params = config.loadSection("server") self.host = params["host"] self.port = params.getint("port") self.db = params.getint("db") params = config.loadSection("vos_user") self.rpcQueue = params["rpc_queue"] super(VOSUser, self).__init__(self.host, self.port, self.db, self.rpcQueue) def add(self): userId = None username = None email = None while not userId: try: userId = input("\nUser ID: ") except ValueError: print("Input type is not valid!") except EOFError: print("\nPlease, use CTRL+C to quit.") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") while not username: try: username = input("\nUsername: ") except ValueError: print("Input type is not valid!") except EOFError: print("\nPlease, use CTRL+C to quit.") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") while not email: # Add email validation here try: email = input("\nE-mail: ") except ValueError: print("Input type is not valid!") except EOFError: print("\nPlease, use CTRL+C to quit.") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") userRequest = { "requestType": "USER_ADD", "userId": userId, "username": username, "email": email } userResponse = self.call(userRequest) if "responseType" not in userResponse: sys.exit("FATAL: Malformed response, acknowledge expected.\n") elif userResponse["responseType"] == "USER_ADD_DONE": print("\nUser added successfully!\n") elif userResponse["responseType"] == "ERROR": errorCode = userResponse["errorCode"] errorMsg = userResponse["errorMsg"] sys.exit(f"\nError code: {errorCode}\nError message: {errorMsg}\n") else: sys.exit("\nFATAL: Unknown response type.\n") def delete(self): userRequest = { "requestType": "USER_DEL_REQ" } userResponse = self.call(userRequest) if "responseType" not in userResponse: sys.exit("FATAL: Malformed response, acknowledge expected.\n") elif userResponse["responseType"] == "USER_DEL_ACK": userList = userResponse["userList"] if not userList: sys.exit("\nNo username found.\n") userIdList = [] for user in userList: userIdList.append(user["user_id"]) userId = None while not userId in userIdList: try: userId = input("\nPlease, insert a valid user ID: ") except ValueError: print("Input type is not valid!") except EOFError: print("\nPlease, use CTRL+C to quit.") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") print("\n!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!") print("! This operation will remove the selected user only !") print("! from the database. !") print("! The user on the transfer node will not be removed, !") print("! you must do it manually. !") print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n") confirm = None while not confirm in ( "yes", "no" ): try: confirm = input("Are you sure to proceed? [yes/no]: ") except KeyboardInterrupt: sys.exit("\nCTRL+C detected. Exiting...") except EOFError: print("\nPlease, use CTRL+C to quit.") if confirm == "yes": confirmRequest = { "requestType": "USER_DEL_CON", "userId": userId } confirmResponse = self.call(confirmRequest) if "responseType" not in confirmResponse: sys.exit("\nFATAL: Malformed response, confirmation expected.\n") elif confirmResponse["responseType"] == "USER_DEL_DONE": print("\nUser deleted successfully!\n") elif confirmResponse["responseType"] == "ERROR": errorCode = confirmResponse["errorCode"] errorMsg = confirmResponse["errorMsg"] sys.exit(f"\nError code: {errorCode}\nError message: {errorMsg}\n") else: sys.exit("\nFATAL: Unknown response type.\n") elif userResponse["responseType"] == "ERROR": errorCode = userResponse["errorCode"] errorMsg = userResponse["errorMsg"] sys.exit(f"Error code: {errorCode}\nError message: {errorMsg}\n") else: sys.exit("\nFATAL: Unknown response type.\n") def search(self, searchStr): userRequest = { "requestType": "USER_SEARCH", "searchStr": searchStr } userResponse = self.call(userRequest) if "responseType" not in userResponse: sys.exit("FATAL: Malformed response.\n") elif userResponse["responseType"] == "SEARCH_DONE": userSearch = userResponse["userSearch"] if userSearch: print("\n" + tabulate(userResponse["userSearch"], headers = "keys", tablefmt = "pretty") + "\n") else: sys.exit(f"\nThe search did not return any results.\n") elif userResponse["responseType"] == "ERROR": errorCode = userResponse["errorCode"] errorMsg = userResponse["errorMsg"] sys.exit(f"\nError code: {errorCode}\nError message: {errorMsg}\n") else: sys.exit("\nFATAL: Unknown response type.\n") def help(self): sys.exit(""" NAME vos_user SYNOPSYS vos_user COMMAND [ARGUMENT] DESCRIPTION Client tool to manage VOSpace users in the database. The client accepts only one (mandatory) command at a time. A list of supported commands is shown here below: add adds a user to the database del deletes a user from the database search performs a search on users and returns those having a match between the search string passed via command line and one of the following fields: 'user_id', 'user_name', 'e_mail' Adding a user to the database requires a user ID, a username and an e-mail address. A valid userID is required when deleting a user from the database. IMPORTANT NOTE: the VOSpace Transfer Service automatically populates the 'users' table in the database by previously quering the authentication system (RAP). So, please, use this client only if you need to handle situations involing users that for some reason are not recognized by the authentication system. """) # Create new VOSUser object vosUserCli = VOSUser() # Check the number of input args if len(sys.argv) == 2: script, cmd = sys.argv if cmd == "add": vosUserCli.add() elif cmd == "del": vosUserCli.delete() else: vosUserCli.help() elif len(sys.argv) == 3: script, cmd, arg = sys.argv if cmd == "search": vosUserCli.search(arg) else: vosUserCli.help() else: vosUserCli.help()
transfer_service/cli_handler.py +3 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from group_rw_rpc_server import GroupRwRPCServer from import_rpc_server import ImportRPCServer from job_rpc_server import JobRPCServer from storage_rpc_server import StorageRPCServer from user_rpc_server import UserRPCServer class CliHandler(object): Loading @@ -28,6 +29,8 @@ class CliHandler(object): self.rpcServerList.append(DataRPCServer(self.host, self.port, self.db, rpcQueue)) elif srvType == "group_rw": self.rpcServerList.append(GroupRwRPCServer(self.host, self.port, self.db, rpcQueue)) elif srvType == "user": self.rpcServerList.append(UserRPCServer(self.host, self.port, self.db, rpcQueue)) elif srvType == "import": self.rpcServerList.append(ImportRPCServer(self.host, self.port, self.db, rpcQueue)) elif srvType == "storage": Loading