Added seed code for access-management.
[ta/access-management.git] / src / access_management / rest-plugin / roles.py
diff --git a/src/access_management/rest-plugin/roles.py b/src/access_management/rest-plugin/roles.py
new file mode 100644 (file)
index 0000000..d1bea48
--- /dev/null
@@ -0,0 +1,343 @@
+# 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 access_management.db.amdb as amdb
+from am_api_base import *
+
+
+class Roles(AMApiBase):
+
+    """
+    Role create operations
+
+    .. :quickref: Roles;Role create operations
+
+    .. http:post:: /am/v1/roles
+
+    **Start Role create**
+
+    **Example request**:
+
+    .. sourcecode:: http
+
+        POST am/v1/roles HTTP/1.1
+        Host: haproxyvip:61200
+        Accept: application/json
+        {
+            "role_name": "test_role"
+            "desc": "This is a test role"
+        }
+
+    :> json string role_name: The created role name.
+    :> json string desc: A short description from the created role.
+
+    **Example response**:
+
+    .. sourcecode:: http
+
+        HTTP/1.1 200 OK
+        {
+            "code": 0,
+            "description": "Role created."
+        }
+
+    :> json int code: the status code
+    :> json string description: the error description, present if code is non zero
+
+    Role modify operations
+
+    .. :quickref: Roles;Role modify operations
+
+    .. http:put:: /am/v1/roles
+
+    **Start Role modify**
+
+    **Example request**:
+
+    .. sourcecode:: http
+
+        PUT am/v1/roles HTTP/1.1
+        Host: haproxyvip:61200
+        Accept: application/json
+        {
+            "role_name": "test_role"
+            "desc": "This is a test role"
+        }
+
+    :> json string role_name: The modified role name.
+    :> json string desc: A short description from the modified role.
+
+    **Example response**:
+
+    .. sourcecode:: http
+
+        HTTP/1.1 200 OK
+        {
+            "code": 0,
+            "description": "Role modified."
+        }
+
+    :> json int code: the status code
+    :> json string description: the error description, present if code is non zero
+
+    Role delete operations
+
+    .. :quickref: Roles;Role delete operations
+
+    .. http:delete:: /am/v1/roles
+
+    **Start Role delete**
+
+    **Example request**:
+
+    .. sourcecode:: http
+
+        DELETE am/v1/roles HTTP/1.1
+        Host: haproxyvip:61200
+        Accept: application/json
+        {
+            "role_name": "test_role"
+        }
+
+    :> json string role_name: The deleted role name.
+
+    **Example response**:
+
+    .. sourcecode:: http
+
+        HTTP/1.1 200 OK
+        {
+            "code": 0,
+            "description": "Role deleted."
+        }
+
+    :> json int code: the status code
+    :> json string description: the error description, present if code is non zero
+
+    Role list operations
+
+    .. :quickref: Roles;Role list operations
+
+    .. http:get:: /am/v1/roles
+
+    **Start Role list**
+
+    **Example request**:
+
+    .. sourcecode:: http
+
+        GET am/v1/roles HTTP/1.1
+        Host: haproxyvip:61200
+        Accept: application/json
+
+    **Example response**:
+
+    .. sourcecode:: http
+
+        HTTP/1.1 200 OK
+        {
+            "code": 0,
+            "description": "Role list."
+            "data":
+            {
+                "alarm_admin":
+                {
+                    "desc": "Alarm Administrator",
+                    "is_chroot": false,
+                    "is_service": true,
+                    "role_name": "alarm_admin"
+                },
+                "alarm_viewer":
+                {
+                    "desc": "Alarm Viewer",
+                    "is_chroot": false,
+                    "is_service": true,
+                    "role_name": "alarm_viewer"
+                }
+            }
+        }
+
+    :> 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 existing roles
+    :> json string role_name: The role name.
+    :> json string desc: The role description.
+    :> json string is_chroot: If this field is true, then this is a chroot user role.
+    :> json string is_service: If this field is true, then this is a service role and we created this role in deploymnet time.
+    """
+
+    endpoints = ['roles']
+    parser_arguments = ['role_name',
+                        'desc']
+
+    def post(self):
+        self.logger.info("Received a role create request!")
+        args = self.parse_args()
+        if args["desc"] is None:
+            args["desc"] = ""
+        state, result = self._role_create(args)
+
+        if state:
+            self.logger.info("The {0} role created!".format(args["role_name"]))
+            return AMApiBase.embed_data({}, 0, result)
+        else:
+            self.logger.error("The {0} role creation failed: {1}".format(args["role_name"], result))
+            return AMApiBase.construct_error_response(1, result)
+
+    def put(self):
+        self.logger.info("Received a role modify request!")
+        args = self.parse_args()
+        if args["desc"] is None:
+            args["desc"] = ""
+        state, result = self._role_modify(args)
+
+        if state:
+            self.logger.info("The {0} role modified!".format(args["role_name"]))
+            return AMApiBase.embed_data({}, 0, result)
+        else:
+            self.logger.error("The {0} role modify failed: {1}".format(args["role_name"], result))
+            return AMApiBase.construct_error_response(1, result)
+
+    def get(self):
+        self.logger.info("Received a role list request!")
+        state, roles = self._role_list()
+
+        if state:
+            self.logger.info("The role list response done!")
+            return AMApiBase.embed_data(roles, 0, "Role list.")
+        else:
+            self.logger.error("Role list creation failed: {0}".format(roles))
+            return AMApiBase.construct_error_response(1, roles)
+
+    def delete(self):
+        self.logger.info("Received a role delete request!")
+        args = self.parse_args()
+
+        state, message = self._role_delete(args)
+
+        if state:
+            self.logger.info("The {0} role deleted!".format(args["role_name"]))
+            return AMApiBase.embed_data({}, 0, message)
+        else:
+            self.logger.error("The {0} role deletion failed: {1}".format(args["role_name"], message))
+            return AMApiBase.construct_error_response(1, message)
+
+    def _role_modify(self, args):
+        state_open, message_open = self._open_db()
+        if state_open:
+            try:
+                self.db.set_role_param(args["role_name"], args["desc"])
+            except amdb.NotAllowedOperation:
+                self.logger.error("Modifying service role is not allowed: {0}".format(args["role_name"]))
+                return False, "Modifying service role is not allowed: {0}".format(args["role_name"])
+            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, "Role modified."
+        else:
+            return False, message_open
+
+    def _role_create(self, args):
+        state_open, message_open = self._open_db()
+        if state_open:
+            try:
+                self.db.create_role(args["role_name"], args["desc"])
+                try:
+                    self.keystone.roles.create(args["role_name"])
+                except Exception as ex:
+                    self.db.delete_role(args["role_name"])
+                    self.logger.error("Role {} already exists".format(args["role_name"]))
+                    return False, "Role {} already exists".format(args["role_name"])
+            except amdb.AlreadyExist:
+                self.logger.error("Role already exists in table: {0}".format(args["role_name"]))
+                return False, "Role already exists in table: {0}".format(args["role_name"])
+            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()
+        else:
+            return False, message_open
+        return True, "Role created."
+
+    def _role_list(self):
+        state_open, message_open = self._open_db()
+        if state_open:
+            try:
+                roles = self.db.get_all_roles()
+            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, roles
+        else:
+            return False, message_open
+
+    def _add_roles_back_to_users(self, role_name):
+        uuid_list = self.db.get_role_users(role_name)
+        for uuid in uuid_list:
+            username, def_project = self.get_user_from_uuid(uuid)
+            state, message = self.modify_role_in_keystone(role_name, uuid, "put", def_project)
+            if not state:
+                return False, "Role deletion failed, please try again!"
+        return False, "Role deletion failed, try again"
+
+    def _role_delete(self, args):
+        state_open, message_open = self._open_db()
+        if state_open:
+            try:
+                db_role = self.db.get_role(args["role_name"])
+                if not db_role._data["is_service"]:
+                    role_id = self.get_role_id(args["role_name"])
+                    if role_id is not None:
+                        try:
+                            self.keystone.roles.delete(role_id)
+                        except Exception as ex:
+                            self.logger.error("Some problem occured: {}".format(ex))
+                            return False, "Some problem occured: {}".format(ex)
+
+                        try:
+                            self.db.delete_role(args["role_name"])
+                        except Exception:
+                            try:
+                                self.keystone.roles.create(args["role_name"])
+                            except Exception:
+                                self.logger.error("Error during deleting role: {}".format(args["role_name"]))
+                                return False, "Error during deleting role: {}".format(args["role_name"])
+                            state, message = self._add_roles_back_to_users(args["role_name"])
+                            return state, message
+                else:
+                    raise amdb.NotAllowedOperation("")
+            except amdb.NotAllowedOperation:
+                self.logger.error("Deleting service role is not allowed: {0}".format(args["role_name"]))
+                return False, "Deleting service role is not allowed: {0}".format(args["role_name"])
+            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, "Role deleted."
+        else:
+            return False, message_open