text: #
  # crowd.py
  #
  # Author: Matteo Cerutti <matteo.cerutti@hotmail.co.uk>
  #
  
  import requests
  import json
  import random
  import string
  import logging
  
  
  class CrowdAPI:
      def __init__(self, **kwargs):
          if 'api_url' not in kwargs:
              raise ValueError("Crowd API URL must be given")
  
          self.api_url = kwargs['api_url']
  
          if 'app_name' not in kwargs:
              raise ValueError("Crowd API application name must be given")
  
          if 'app_password' not in kwargs:
              raise ValueError("Crowd API application password must be given")
  
          self.auth = (kwargs['app_name'], kwargs['app_password'])
  
          if 'verify_ssl' not in kwargs:
              self.verify_ssl = False
          else:
              self.verify_ssl = kwargs['verify_ssl']
  
          if 'timeout' not in kwargs:
              self.timeout = 10
          else:
              self.timeout = kwargs['timeout']
  
      def api_get(self, query):
          req = requests.get(self.api_url + query, headers={"Content-Type": "application/json",
                                                            "Accept": "application/json"}, auth=self.auth, verify=self.verify_ssl, timeout=self.timeout)
          return req
  
      def api_post(self, query, data):
          req = requests.post(self.api_url + query, headers={"Content-Type": "application/json", "Accept": "application/json"},
                              auth=self.auth, data=json.dumps(data), verify=self.verify_ssl, timeout=self.timeout)
          return req
      
      def api_put(self, query, data):
          req = requests.put(self.api_url + query, headers={"Content-Type": "application/json", "Accept": "application/json"},
                              auth=self.auth, data=json.dumps(data), verify=self.verify_ssl, timeout=self.timeout)
          return req
  
      def api_delete(self, query, data):
          req = requests.delete(self.api_url + query, headers={"Content-Type": "application/json", "Accept": "application/json"},
                                auth=self.auth, data=json.dumps(data), verify=self.verify_ssl, timeout=self.timeout)
          return req
  
      def get_user(self, **kwargs):
          if "username" not in kwargs:
              raise ValueError("Must pass username")
  
          req = self.api_get(
              "/user?username={}&expand=attributes".format(kwargs['username']))
          if req.status_code == 200:
              return {"status": True, "user": req.json()}
          if req.status_code == 404:
              return {"status": False, "user": None}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def get_user_attributes(self, **kwargs):
          if "username" not in kwargs:
              raise ValueError("Must pass username")
  
          req = self.api_get("/user/attribute?username={}".format(kwargs['username']))
          if req.status_code == 200:
              return {"status": True, "Attributes": req.content}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def get_user_groups(self, **kwargs):
          groups = []
  
          if "username" not in kwargs:
              raise ValueError("Must pass username")
  
          req = self.api_get("/user/group/direct?username={}&max-results={}".format(
              kwargs['username'], kwargs.get('max_results', 1000)))
          if req.status_code == 200:
              for group in req.json()['groups']:
                  groups.append(group['name'])
  
              return {"status": True, "groups": groups}
          if req.status_code == 404:
              return {"status": False, "groups": []}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def get_group_users(self, **kwargs):
          users = []
  
          if "groupname" not in kwargs:
              raise ValueError("Must pass username")
  
          req = self.api_get("/group/user/direct?groupname={}&max-results={}".format(
              kwargs['groupname'], kwargs.get('max_results', 1000)))
          if req.status_code == 200:
              for user in req.json()['users']:
                  users.append(user['name'])
  
              return {"status": True, "users": users}
          if req.status_code == 404:
              return {"status": False, "users": []}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def get_nested_group_users(self, **kwargs):
          users = []
  
          if "groupname" not in kwargs:
              raise ValueError("Must pass groupname")
  
          req = self.api_get(
              "/group/user/nested?groupname={}".format(kwargs['groupname']))
          if req.status_code == 200:
              for user in req.json()['users']:
                  users.append(user['name'])
  
              return {"status": True, "users": users}
          if req.status_code == 404:
              return {"status": False, "users": []}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def get_parent_groups(self, **kwargs):
          pgroups = []
  
          if "groupname" not in kwargs:
              raise ValueError("Must pass groupname")
  
          req = self.api_get(
              "/group/parent-group/direct?groupname={}".format(kwargs['groupname']))
          if req.status_code == 200:
              for pgroup in req.json()['groups']:
                  pgroups.append(pgroup['name'])
  
              return {"status": True, "pgroups": pgroups}
          if req.status_code == 404:
              return {"status": False, "pgroups": []}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def get_parent_groupsv2(self, **kwargs):
          groups = []
  
          if "groupname" not in kwargs:
              raise ValueError("Must pass groupname")
  
          req = self.api_get(
              "/group/parent-group/direct?groupname={}".format(kwargs['groupname']))
          if req.status_code == 200:
              for group in req.json()['groups']:
                  groups.append(group['name'])
  
              return {"status": True, "groups": groups}
          if req.status_code == 404:
              return {"status": False, "groups": []}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def get_all_groups(self, **kwargs):
          groups = []
  
          req = self.api_get(
              "/search?entity-type=group&max-results={}".format(kwargs.get('max_results', 1000)))
  
          if req.status_code == 200:
              for group in req.json()['groups']:
                  groups.append(group['name'])
  
              return {"status": True, "groups": groups}
          if req.status_code == 404:
              return {"status": False, "groups": []}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def search_group(self, **kwargs):
          groups = []
  
          if 'restriction' not in kwargs:
              raise ValueError("You need to define a certian restriction")
  
          req = self.api_get(
              "/search?entity-type=group&restriction={}&max-results={}".format(kwargs['restriction'], kwargs.get('max_results', 1000)))
  
          if req.status_code == 200:
              for group in req.json()['groups']:
                  groups.append(group['name'])
  
              return {"status": True, "groups": groups}
          if req.status_code == 404:
              return {"status": False, "groups": []}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def get_all_users(self, **kwargs):
          users = []
  
          req = self.api_get(
              "/search?entity-type=user&max-results={}".format(kwargs.get('max_results', 1000)))
          if req.status_code == 200:
              for user in req.json()['users']:
                  users.append(user['name'])
  
              return {"status": True, "users": users}
          if req.status_code == 404:
              return {"status": False, "users": []}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def search_user(self, **kwargs):
          users = []
  
          if 'restriction' not in kwargs:
              raise ValueError("You need to define a certian restriction")
  
          req = self.api_get(
              "/search?entity-type=user&restriction={}&max-results={}".format(kwargs['restriction'], kwargs.get('max_results', 1000)))
          if req.status_code == 200:
              for user in req.json()['users']:
                  users.append(user['name'])
  
              return {"status": True, "users": users}
          if req.status_code == 404:
              return {"status": False, "users": []}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
  
      def set_user_attribute(self, **kwargs):
          if "username" not in kwargs:
              raise ValueError("Must pass username")
  
          if "attribute_name" not in kwargs:
              raise ValueError("Must pass attribute_name")
  
          if "attribute_value" not in kwargs:
              raise ValueError("Must pass attribute_value")
          else:
              if not isinstance(kwargs['attribute_value'], list):
                  kwargs['attribute_value'] = [kwargs['attribute_value']]
  
          req = self.api_post("/user/attribute?username={}".format(kwargs['username']), {
                              "attributes": [{"name": kwargs['attribute_name'], "values": kwargs['attribute_value']}]})
          if req.status_code == 204:
              return {"status": True}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def set_user_activity(self, **kwargs):
          if "username" not in kwargs:
              raise ValueError("Must pass username")
  
          if "active" not in kwargs:
              raise ValueError("Must pass active (true/false)")
  
          # fetch current user document
          user_document = self.api_get(
              "/user?username={}".format(kwargs['username'])).json()
  
          # set to active true/false
          user_document["active"] = kwargs['active']
  
          # update user object
          req = self.api_put("/user?username={}".format(kwargs['username']), user_document )
          if req.status_code == 204:
              return {"status": True}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
  
      def create_user(self, **kwargs):
          user = {}
  
          for k, v in kwargs.items():
              user[k.replace('_', '-')] = v
  
          if 'password' not in kwargs:
              user['password'] = {}
              user['password']['value'] = ''.join(random.choice(
                  string.ascii_uppercase + string.digits) for _ in range(8))
              req_password_change = True
          else:
              req_password_change = False
  
          user['active'] = True
  
          req = self.api_post("/user", user)
          if req.status_code == 201:
              # user should change the password at their next login
              if req_password_change:
                  self.set_user_attribute(
                      username=user['name'], attribute_name="requiresPasswordChange", attribute_value=True)
                  return {"status": True, "password": user['password']['value']}
              else:
                  return {"status": True}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def create_group(self, **kwargs):
          req = self.api_post(
              "/group", {"name": kwargs['name'], "type": "GROUP", "description": kwargs['description'], "active": True})
          if req.status_code == 201:
              return {"status": True}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def delete_group(self, **kwargs):
  
          if "groupname" not in kwargs:
              raise ValueError("Must pass groupname")
  
          req = self.api_delete(
              "/group?groupname={}".format(kwargs['groupname']), data={})
          if req.status_code == 204:
              return {"status": True}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def add_user_to_group(self, **kwargs):
  
          if "username" not in kwargs:
              raise ValueError("Must pass username")
  
          if "groupname" not in kwargs:
              raise ValueError("Must pass groupname")
  
          req = self.api_post(
              "/user/group/direct?username={}".format(kwargs['username']), {"name": kwargs['groupname']})
          if req.status_code == 201:
              return {"status": True}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def remove_user_from_group(self, **kwargs):
  
          if "username" not in kwargs:
              raise ValueError("Must pass username")
  
          if "groupname" not in kwargs:
              raise ValueError("Must pass groupname")
  
          req = self.api_delete(
              "/user/group/direct?username={}&groupname={}".format(kwargs['username'], kwargs['groupname']), data={})
          if req.status_code == 204:
              return {"status": True}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  
      def get_group(self, **kwargs):
          req = self.api_get(
              "/group?groupname={}&expand=attributes".format(kwargs['name']))
          if req.status_code == 200:
              return {"status": True, "group": req.json()}
          else:
              return {"status": False, "code": req.status_code, "reason": req.content}
  