+ rc = self.db.del_one(self.roles_collection, {"_id": role_id})
+ self.db.del_list(self.tokens_collection, {"roles.id": role_id})
+ return rc
+
+ def update_role(self, role_info):
+ """
+ Update a role.
+
+ :param role_info: full role info.
+ :return: returns the role name and id.
+ :raises AuthconnOperationException: if user creation failed.
+ """
+ rid = role_info["_id"]
+ self.db.set_one(self.roles_collection, {"_id": rid}, role_info)
+ return {"_id": rid, "name": role_info["name"]}
+
+ def create_user(self, user_info):
+ """
+ Create a user.
+
+ :param user_info: full user info.
+ :return: returns the username and id of the user.
+ """
+ BaseTopic.format_on_new(user_info, make_public=False)
+ salt = uuid4().hex
+ user_info["_admin"]["salt"] = salt
+ user_info["_admin"]["user_status"] = "active"
+ present = time()
+ if not user_info["username"] == "admin":
+ if self.config.get("user_management"):
+ user_info["_admin"]["modified"] = present
+ user_info["_admin"]["password_expire_time"] = present
+ account_expire_time = present + 86400 * self.config.get(
+ "account_expire_days"
+ )
+ user_info["_admin"]["account_expire_time"] = account_expire_time
+
+ user_info["_admin"]["retry_count"] = 0
+ user_info["_admin"]["last_token_time"] = present
+ if "password" in user_info:
+ user_info["password"] = sha256(
+ user_info["password"].encode("utf-8") + salt.encode("utf-8")
+ ).hexdigest()
+ user_info["_admin"]["password_history"] = {salt: user_info["password"]}
+ # "projects" are not stored any more
+ if "projects" in user_info:
+ del user_info["projects"]
+ self.db.create(self.users_collection, user_info)
+ return {"username": user_info["username"], "_id": user_info["_id"]}
+
+ def update_user(self, user_info):
+ """
+ Change the user name and/or password.
+
+ :param user_info: user info modifications
+ """
+ uid = user_info["_id"]
+ old_pwd = user_info.get("old_password")
+ unlock = user_info.get("unlock")
+ renew = user_info.get("renew")
+ permission_id = user_info.get("system_admin_id")
+ now = time()
+
+ user_data = self.db.get_one(
+ self.users_collection, {BaseTopic.id_field("users", uid): uid}
+ )
+ if old_pwd:
+ salt = user_data["_admin"]["salt"]
+ shadow_password = sha256(
+ old_pwd.encode("utf-8") + salt.encode("utf-8")
+ ).hexdigest()
+ if shadow_password != user_data["password"]:
+ raise AuthconnConflictException(
+ "Incorrect password", http_code=HTTPStatus.CONFLICT
+ )
+ # Unlocking the user
+ if unlock:
+ system_user = None
+ unlock_state = False
+ if not permission_id:
+ raise AuthconnConflictException(
+ "system_admin_id is the required field to unlock the user",
+ http_code=HTTPStatus.CONFLICT,
+ )
+ else:
+ system_user = self.db.get_one(
+ self.users_collection,
+ {
+ BaseTopic.id_field(
+ self.users_collection, permission_id
+ ): permission_id
+ },
+ )
+ mapped_roles = system_user.get("project_role_mappings")
+ for role in mapped_roles:
+ role_id = role.get("role")
+ role_assigned = self.db.get_one(
+ self.roles_collection,
+ {BaseTopic.id_field(self.roles_collection, role_id): role_id},
+ )
+ if role_assigned.get("permissions")["admin"]:
+ if role_assigned.get("permissions")["default"]:
+ user_data["_admin"]["retry_count"] = 0
+ if now > user_data["_admin"]["account_expire_time"]:
+ user_data["_admin"]["user_status"] = "expired"
+ else:
+ user_data["_admin"]["user_status"] = "active"
+ unlock_state = True
+ break
+ if not unlock_state:
+ raise AuthconnConflictException(
+ "User '{}' does not have the privilege to unlock the user".format(
+ permission_id
+ ),
+ http_code=HTTPStatus.CONFLICT,
+ )
+ # Renewing the user
+ if renew:
+ system_user = None
+ renew_state = False
+ if not permission_id:
+ raise AuthconnConflictException(
+ "system_admin_id is the required field to renew the user",
+ http_code=HTTPStatus.CONFLICT,
+ )
+ else:
+ system_user = self.db.get_one(
+ self.users_collection,
+ {
+ BaseTopic.id_field(
+ self.users_collection, permission_id
+ ): permission_id
+ },
+ )
+ mapped_roles = system_user.get("project_role_mappings")
+ for role in mapped_roles:
+ role_id = role.get("role")
+ role_assigned = self.db.get_one(
+ self.roles_collection,
+ {BaseTopic.id_field(self.roles_collection, role_id): role_id},
+ )
+ if role_assigned.get("permissions")["admin"]:
+ if role_assigned.get("permissions")["default"]:
+ present = time()
+ account_expire = (
+ present + 86400 * self.config["account_expire_days"]
+ )
+ user_data["_admin"]["modified"] = present
+ user_data["_admin"]["account_expire_time"] = account_expire
+ if (
+ user_data["_admin"]["retry_count"]
+ >= self.config["max_pwd_attempt"]
+ ):
+ user_data["_admin"]["user_status"] = "locked"
+ else:
+ user_data["_admin"]["user_status"] = "active"
+ renew_state = True
+ break
+ if not renew_state:
+ raise AuthconnConflictException(
+ "User '{}' does not have the privilege to renew the user".format(
+ permission_id
+ ),
+ http_code=HTTPStatus.CONFLICT,
+ )
+ BaseTopic.format_on_edit(user_data, user_info)
+ # User Name
+ usnm = user_info.get("username")
+ if usnm:
+ user_data["username"] = usnm
+ # If password is given and is not already encripted
+ pswd = user_info.get("password")
+ if pswd and (
+ len(pswd) != 64 or not re.match("[a-fA-F0-9]*", pswd)
+ ): # TODO: Improve check?
+ cef_event(
+ self.cef_logger,
+ {
+ "name": "Change Password",
+ "sourceUserName": user_data["username"],
+ "message": "Changing Password for user, Outcome=Success",
+ "severity": "2",
+ },
+ )
+ self.logger.info("{}".format(self.cef_logger))
+ salt = uuid4().hex
+ if "_admin" not in user_data:
+ user_data["_admin"] = {}
+ if user_data.get("_admin").get("password_history"):
+ old_pwds = user_data.get("_admin").get("password_history")
+ else:
+ old_pwds = {}
+ for k, v in old_pwds.items():
+ shadow_password = sha256(
+ pswd.encode("utf-8") + k.encode("utf-8")
+ ).hexdigest()
+ if v == shadow_password:
+ raise AuthconnConflictException(
+ "Password is used before", http_code=HTTPStatus.CONFLICT
+ )
+ user_data["_admin"]["salt"] = salt
+ user_data["password"] = sha256(
+ pswd.encode("utf-8") + salt.encode("utf-8")
+ ).hexdigest()
+ if len(old_pwds) >= 3:
+ old_pwds.pop(list(old_pwds.keys())[0])
+ old_pwds.update({salt: user_data["password"]})
+ user_data["_admin"]["password_history"] = old_pwds
+ if not user_data["username"] == "admin":
+ if self.config.get("user_management"):
+ present = time()
+ if self.config.get("pwd_expire_days"):
+ expire = present + 86400 * self.config.get("pwd_expire_days")
+ user_data["_admin"]["modified"] = present
+ user_data["_admin"]["password_expire_time"] = expire
+ # Project-Role Mappings
+ # TODO: Check that user_info NEVER includes "project_role_mappings"
+ if "project_role_mappings" not in user_data:
+ user_data["project_role_mappings"] = []
+ for prm in user_info.get("add_project_role_mappings", []):
+ user_data["project_role_mappings"].append(prm)
+ for prm in user_info.get("remove_project_role_mappings", []):
+ for pidf in ["project", "project_name"]:
+ for ridf in ["role", "role_name"]:
+ try:
+ user_data["project_role_mappings"].remove(
+ {"role": prm[ridf], "project": prm[pidf]}
+ )
+ except KeyError:
+ pass
+ except ValueError:
+ pass
+ idf = BaseTopic.id_field("users", uid)
+ self.db.set_one(self.users_collection, {idf: uid}, user_data)
+ if user_info.get("remove_project_role_mappings"):
+ idf = "user_id" if idf == "_id" else idf
+ self.db.del_list(self.tokens_collection, {idf: uid})
+
+ def delete_user(self, user_id):
+ """
+ Delete user.
+
+ :param user_id: user identifier.
+ :raises AuthconnOperationException: if user deletion failed.
+ """
+ self.db.del_one(self.users_collection, {"_id": user_id})
+ self.db.del_list(self.tokens_collection, {"user_id": user_id})