X-Git-Url: https://gerrit.akraino.org/r/gitweb?p=ta%2Faccess-management.git;a=blobdiff_plain;f=src%2Faccess_management%2Frest-plugin%2Fusers.py;fp=src%2Faccess_management%2Frest-plugin%2Fusers.py;h=ec708c86a88fcd834557f751c5a11c96c7a5e51a;hp=0000000000000000000000000000000000000000;hb=d37b9ab19ff6f50b9c1746784623b3dd328ab525;hpb=0205adc63d3bba479a24db85d2d4bfdba0411876 diff --git a/src/access_management/rest-plugin/users.py b/src/access_management/rest-plugin/users.py new file mode 100644 index 0000000..ec708c8 --- /dev/null +++ b/src/access_management/rest-plugin/users.py @@ -0,0 +1,411 @@ +# Copyright 2019 Nokia + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import time +import access_management.db.amdb as amdb +from am_api_base import * +from keystoneauth1 import exceptions +from cmframework.apis import cmclient + + +class Users(AMApiBase): + + """ + User create operations + + .. :quickref: Users;User create operations + + .. http:post:: /am/v1/users + + **Start User create** + + **Example request**: + + .. sourcecode:: http + + POST am/v1/users HTTP/1.1 + Host: haproxyvip:61200 + Accept: application/json + { + "username": "user_1", + "password": "Passwd_1", + "email": "test@mail.com", + "project": "10f8fa2c6efe409d8207517128f03265", + "description": "desc" + } + + :> json string username: The created user name. + :> json string password: The user's password. + :> json string email: The user's e-mail. + :> json string project: ID of the project to be set as primary project for the user. + :> json string description: The user's description. + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + { + "code": 0, + "description": "", + "data": + { + "id": + } + } + + :> json int code: the status code + :> json string description: the error description, present if code is non zero + :> json object data: a dictionary with the created user's id + :> json string id: The created user's id. + + Users list operations + + .. :quickref: Users;Users list operations + + .. http:get:: /am/v1/users + + **Start Users list** + + **Example request**: + + .. sourcecode:: http + + GET am/v1/users HTTP/1.1 + Host: haproxyvip:61200 + Accept: application/json + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + { + "code": 0, + "description": "", + "data": + { + "0edf341a27544c349b7c37bb76ab25d1": + { + "enabled": true, + "id": "0edf341a27544c349b7c37bb76ab25d1", + "name": "cinder", + "password_expires_at": null + }, + "32e8859519f94b1ea80f61d53d17e74e": + { + "enabled": true, + "id": "32e8859519f94b1ea80f61d53d17e74e", + "name": "nova", + "password_expires_at": null + } + } + } + + :> json int code: the status code + :> json string description: the error description, present if code is non zero + :> json object data: The existing users. + :> json string enabled: The user's state. + :> json string id: The user's id. + :> json string name: The user's name. + :> json string password_expires_at: The user's password expiration date. + + User delete operations + + .. :quickref: Users;User delete operations + + .. http:delete:: /am/v1/users + + **Start User delete** + + **Example request**: + + .. sourcecode:: http + + DELETE am/v1/users HTTP/1.1 + Host: haproxyvip:61200 + Accept: application/json + { + "user": or + } + + :> json string user: The removed user's id or user name. + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + { + "code": 0, + "description": "User deleted!" + } + + :> json int code: the status code + :> json string description: the error description, present if code is non zero + """ + + endpoints = ['users'] + parser_arguments = ['username', + 'password', + 'email', + 'user', + 'project', + 'description'] + + def post(self): + self.logger.info("Received a user create request!") + args = self.parse_args() + + if args["email"] is not None: + if re.match("^[\.a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-z]+$", args["email"]) is None: + self.logger.error("E-mail validation failed!") + return AMApiBase.embed_data({}, 1, "E-mail validation failed!") + + if self.id_validator(args["username"]): + self.logger.error("{0} username is invalid, because cannot assign a valid uuid to it.".format(args["username"])) + return AMApiBase.embed_data({}, 1, "{0} username is invalid, because cannot assign a valid uuid to it.".format(args["username"])) + + if args["project"]: + projectidstate = self.id_validator(args["project"]) + if projectidstate == False: + self.logger.error("Project id validation failed") + return AMApiBase.embed_data({}, 1, "Project id validation failed") + + if re.match("^[a-zA-Z0-9_-]+$", args["username"]) is None: + self.logger.error("Username validation failed!") + return AMApiBase.embed_data({}, 1, "Username validation failed!") + + passstate = self.passwd_validator(args["password"]) + if passstate is not None: + self.logger.error(passstate) + return AMApiBase.embed_data({}, 1, passstate) + + state, result = self._create_user(args) + if state: + self.logger.info("User created!") + return AMApiBase.embed_data({"id": result}, 0, "") + else: + return AMApiBase.embed_data({}, 1, result) + + def get(self): + self.logger.info("Received a user list request!") + user_list = {} + try: + self.keystone = self.auth_keystone() + u_list = self.keystone.users.list() + except Exception as ex: + self.logger.error("{0}".format(ex)) + return False, "{0}".format(ex) + + for element in u_list: + user_list.update({element.id : element._info}) + + self.logger.info("The user list response done!") + return AMApiBase.embed_data(user_list, 0, "User list.") + + def delete(self): + self.logger.info("Received a user delete request!") + args = self.parse_args() + + state, user_info = self.get_uuid_and_name(args["user"]) + if state: + token_owner = self.get_uuid_from_token() + if user_info["id"] == token_owner: + self.logger.error("The {0} user tried to delete own account!".format(user_info["id"])) + return AMApiBase.embed_data({}, 1, "You cannot delete your own account!") + + state, message = self._delete_user(user_info) + + if state: + self.logger.info("User deleted!") + return AMApiBase.embed_data({}, 0, "User deleted!") + else: + self.logger.error(message) + return AMApiBase.embed_data({}, 1, message) + else: + self.logger.error(user_info) + return AMApiBase.embed_data({}, 1, user_info) + + def _delete_user(self, user_info): + state, name = self._delete_user_from_db(user_info) + if state: + self.logger.info("User removed from the db!") + try: + self.keystone.users.delete(user_info["id"]) + except exceptions.http.NotFound as ex: + self.logger.info("{0} user does not exist in the keystone!".format(user_info["name"])) + return True, "Done, but this user didn't exist in the keystone!" + except Exception as ex: + self.logger.error("{0}".format(ex)) + return False, "{0}".format(ex) + return True, "Done" + else: + return False, name + + def _delete_user_from_db(self, user_info): + state_open, message_open = self._open_db() + if state_open: + try: + roles = self.db.get_user_roles(user_info["id"]) + + for role in roles: + if self.db.is_chroot_role(role): + self.logger.debug("This user has a chroot role.") + for x in range(3): + self.remove_chroot_linux_role_handling(user_info["id"], "Chroot", "cloud.chroot") + time.sleep(2) + if self.check_chroot_linux_state(user_info["name"], "cloud.chroot", "absent"): + self.db.delete_user(user_info["id"]) + return True, user_info["name"] + + if role == "linux_user": + self.logger.debug("This user has a linux_user role!") + for x in range(3): + self.remove_chroot_linux_role_handling(user_info["id"], "Linux", "cloud.linuxuser") + time.sleep(2) + if self.check_chroot_linux_state(user_info["name"], "cloud.linuxuser", "absent"): + self.db.delete_user(user_info["id"]) + return True, user_info["name"] + + self.db.delete_user(user_info["id"]) + except amdb.NotAllowedOperation: + self.logger.error("Deleting service user is not allowed: {0}".format(user_info["name"])) + return False, "Deleting service user is not allowed: {0}".format(user_info["name"]) + except amdb.NotExist: + self.logger.info("The {0} user does not exist!".format(user_info["name"])) + return True, "" + except Exception as ex: + self.logger.error("Internal error: {0}".format(ex)) + return False, "Internal error: {0}".format(ex) + finally: + state_close, message_close = self._close_db() + if not state_close: + self._close_db() + return True, user_info["name"] + else: + return False, message_open + + def _create_user(self, args): + roles = [] + ks_member_roleid = self.get_role_id(defaults.KS_MEMBER_NAME) + if ks_member_roleid is None: + self.logger.error("Member user role not found!") + return False, "Member user role not found!" + else: + roles.append(ks_member_roleid) + basic_member_roleid = self.get_role_id(defaults.AM_MEMBER_NAME) + if basic_member_roleid is None: + self.logger.error("basic_member user role not found!") + return False, "basic_member user role not found!" + else: + roles.append(basic_member_roleid) + + um_proj_id = self.get_project_id(defaults.PROJECT_NAME) + if um_proj_id is None: + self.logger.error("The user management project is not found!") + return False, "The user management project is not found!" + + if args["email"] is None: + args["email"] = 'None' + if args["project"] is None: + args["project"] = um_proj_id + + try: + c_user_out = self.keystone.users.create(name=args["username"], password=args["password"], email=args["email"], default_project=args["project"], description=args["description"]) + except exceptions.http.Conflict as ex: + self.logger.error("{0}".format(ex)) + return False, "This user exists in the keystone!" + except Exception as ex: + self.logger.error("{0}".format(ex)) + return False, "{0}".format(ex) + + ID = c_user_out.id + state, message = self._add_basic_roles(um_proj_id, ID, roles) + if not state: + return False, message + if args["project"] != um_proj_id: + state, message = self._add_basic_roles(args["project"], ID, [ks_member_roleid]) + if not state: + return False, message + return self._create_user_in_db(ID, args) + + def _add_basic_roles(self, project, ID, roles): + for role in roles: + try: + self.keystone.roles.grant(role, user=ID, project=project) + except Exception: + try: + self.keystone.roles.grant(role, user=ID, project=project) + except Exception as ex: + self.logger.error("{0}".format(ex)) + self.keystone.users.delete(ID) + return False, "{0}".format(ex) + return True, "OK" + + def _create_user_in_db(self, ID, args): + state_open, message_open = self._open_db() + if state_open: + try: + self.db.create_user(ID, args["username"]) + self.db.add_user_role(ID, defaults.AM_MEMBER_NAME) + except amdb.AlreadyExist as ex1: + self.logger.error("User already exists in table!") + try: + self.keystone.users.delete(ID) + self.db.delete_user(ID) + except amdb.NotAllowedOperation as ex2: + self.logger.error("Internal error: Except1: {0}, Except2: {1}".format(ex1, ex2)) + return False, "Except1: {0}, Except2: {1}".format(ex1, ex2) + except Exception as ex3: + self.logger.error("Internal error: Except1: {0}, Except2: {1}".format(ex1, ex3)) + return False, "Except1: {0}, Except2: {1}".format(ex1, ex3) + return False, "User already exists!" + except Exception as ex: + self.logger.error("Internal error: {0}".format(ex)) + try: + self.keystone.users.delete(ID) + except exceptions.http.NotFound as ex: + self.logger.error("{0}".format(ex)) + return False, "This user does not exist in the keystone!" + except Exception as ex: + self.logger.error("{0}".format(ex)) + return False, "{0}".format(ex) + return False, "Internal error: {0}".format(ex) + finally: + state_close, message_close = self._close_db() + if not state_close: + self._close_db() + return True, ID + else: + return False, message_open + + def remove_chroot_linux_role_handling(self, user_id, user_type, list_name): + cmc = cmclient.CMClient() + user_list = cmc.get_property(list_name) + user_list = json.loads(user_list) + self.logger.debug("{0} user list before the change: {1}".format(user_type, json.dumps(user_list))) + if user_list is not None: + self.logger.debug("The {0} user list exists!".format(user_type)) + username, def_project = self.get_user_from_uuid(user_id) + self.logger.debug("User name: {0}".format(username)) + for val in user_list: + if val["name"] == username: + val["public_key"] = "" + val["state"] = "absent" + val["remove"] = "yes" + val["password"] = "" + break + self.logger.debug("{0} user list after the change: {1}".format(user_type, json.dumps(user_list))) + cmc.set_property(list_name, json.dumps(user_list))