#maj du 08/10/2024 v4
from resources.common_imports import *

#les variables globales minimum
from resources.config import ldap_admin, ldap_pass, ldap_root, ldap_host

class Ldap_user(Resource):
  
    def __init__(self):
        global ldap_admin, ldap_pass, ldap_root, ldap_host
        self.ldap_admin = ldap_admin
        self.ldap_pass = ldap_pass
        self.ldap_root = ldap_root
        self.ldap_host = f"ldap://{ldap_host}"

    def _connect_ldap(self):
        ldap_connection = ldap.initialize(self.ldap_host)
        ldap_connection.simple_bind_s("cn={},{}".format(self.ldap_admin, self.ldap_root), self.ldap_pass)
        return ldap_connection
    
    @classmethod
    def is_valid_field(cls, field):
        allowed_fields = ['numeroMembre','mailDeSecours', 'mailEnabled', 'nextcloudEnabled', 'mobilizonEnabled', 'agoraEnabled', 'userPassword', 'identifiantKaz', 'mailAlias', 'quota']
        return field in allowed_fields

    @jwt_required()
    def get(self, email):
        """
        Vérifier si un utilisateur avec cet email existe dans le LDAP soit comme mail principal soit comme alias
        ---
        tags:
          - Ldap        
        security:
          - Bearer: []        
        parameters:
          - in: path
            name: email
            type: string
            required: true                
        responses:
            200:
                description: Existe
            400:
                description: N'existe pas
            401:
                description: oops, email invalide
            402:
                description: oops, autre erreur
        
        """               
        try:
          
            if not validate_email(email):
                return "Adresse e-mail non valide", 400
              
            ldap_connection = self._connect_ldap()

            #result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, "(cn={})".format(email))
            
            # Créer une chaîne de filtre pour rechercher dans les champs "cn" et "mailAlias"
            filter_str = "(|(cn={})(mailAlias={}))".format(email, email)
            result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, filter_str)
  
            ldap_connection.unbind_s()

            if result:
                return True, 200
            else:
                return False, 400
              
        except EmailNotValidError as e:
            return str(e), 401

        except ldap.LDAPError as e:
            return str(e), 402


#*************************************************
    @jwt_required()
    def delete(self, email):
        """
        Supprimer un utilisateur du LDAP par son adresse e-mail
        ---
        tags:
          - Ldap        
        security:
          - Bearer: []        
        parameters:
          - in: path
            name: email
            type: string
            required: true                
        responses:
            200:
                description: Utilisateur supprimé avec succès
            404:
                description: Utilisateur non trouvé dans le LDAP
            400:
                description: Erreur lors de la suppression de l'utilisateur
        """
        try:
            if not validate_email(email):
                return "Adresse e-mail non valide", 400
            
            ldap_connection = self._connect_ldap()

            # Recherche de l'utilisateur
            result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, "(cn={})".format(email))
            
            if not result:
                return False, 404  # Utilisateur non trouvé

            # Récupération du DN de l'utilisateur
            dn = result[0][0]
            
            # Suppression de l'utilisateur
            ldap_connection.delete_s(dn)
            ldap_connection.unbind_s()

            return True, 200  # Utilisateur supprimé avec succès
              
        except ldap.NO_SUCH_OBJECT:
            return False, 404  # Utilisateur non trouvé
        except ldap.LDAPError as e:
            return str(e), 400  # Erreur lors de la suppression
        except EmailNotValidError as e:
            return str(e), 400


#*************************************************
    @jwt_required()
    def post(self, email):
        """
        Ajouter, supprimer ou modifier un champ pour l'utilisateur LDAP
        ---
        tags:
          - Ldap   
        security:
          - Bearer: []                
        parameters:
          - in: path
            name: email
            type: string
            required: true
          - in: query
            name: action
            type: string
            required: true
            enum: ['add', 'delete', 'modify']
          - in: body
            name: data
            required: true
            schema:
              type: object
              properties:
                field:
                  type: string
                  enum: ['numeroMembre', 'mailDeSecours', 'mailEnabled', 'nextcloudEnabled', 'mobilizonEnabled', 'agoraEnabled', 'userPassword', 'identifiantKaz', 'mailAlias', 'quota' ]
                  description: Le champ à ajouter, supprimer ou modifier (par exemple, mailDeSecours, mailAlias, etc.)
                value:
                  type: string        
                  description: La valeur à ajouter, supprimer ou modifier pour le champ spécifié
        responses:
            200:
                description: Opération réussie
            404:
                description: Utilisateur non trouvé dans le LDAP
            400:
                description: Erreur lors de l'opération
        """
        
        try:
            if not validate_email(email):
                return "Adresse e-mail non valide", 400
            
            action = request.args.get('action')
            field = request.json.get('field')
            value = request.json.get('value')
            
            if not action or not field or not value:
                return "Action, champ ou valeur manquant", 400
            
            if not self.is_valid_field(field):
                return "Champ non autorisé !", 400
          
            ldap_connection = self._connect_ldap()
            result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, "(cn={})".format(email))
            
            if not result:
                return False, 404
            
            dn = result[0][0]

            if field == 'userPassword' and (action == 'add' or action == 'modify'):
                  password_chiffre = sha512_crypt.hash(value)
                  value = "{{CRYPT}}{}".format(password_chiffre)
                  
            if action == 'add':
                mod_attrs = [(ldap.MOD_ADD, field, value.encode('utf-8'))]
                
            elif action == 'delete':
                mod_attrs = [(ldap.MOD_DELETE, field, value.encode('utf-8'))]
                
            elif action == 'modify':

              if field == 'quota':
                  mail_quota_value = value + 'G'
                  nextcloud_quota_value = value + " GB"
                  mod_attrs = [
                      (ldap.MOD_REPLACE, 'quota', value.encode('utf-8')),                    
                      (ldap.MOD_REPLACE, 'mailQuota', mail_quota_value.encode('utf-8')),
                      (ldap.MOD_REPLACE, 'nextcloudQuota', nextcloud_quota_value.encode('utf-8'))
                  ]
              else:
                  mod_attrs = [(ldap.MOD_REPLACE, field, value.encode('utf-8'))]
                              
            else:
                return "Action non valide", 400
            
            ldap_connection.modify_s(dn, mod_attrs)
            ldap_connection.unbind_s()

            return True, 200
              
        except ldap.NO_SUCH_OBJECT:
            return False, 404
        except ldap.LDAPError as e:
            return str(e), 400
        except EmailNotValidError as e:
            return str(e), 400

#*************************************************
    @jwt_required()
    def put(self, email, **kwargs):
        """
        Créer une nouvelle entrée dans le LDAP pour un nouvel utilisateur. QUESTION: A QUOI SERVENT PRENOM/NOM/IDENT_KAZ DANS LE LDAP ? POURQUOI 3 QUOTA ?
        ---
        tags:
          - Ldap    
        security:
          - Bearer: []                
        parameters:
          - in: path
            name: email
            type: string
            required: true
          - in: body
            name: data
            required: true
            schema:
              type: object
              properties:
                prenom:
                  type: string
                  description: Prénom de l'utilisateur
                nom:
                  type: string
                  description: Nom de l'utilisateur
                password:
                  type: string
                  description: Mot de passe de l'utilisateur
                email_secours:
                  type: string
                  description: Adresse e-mail de secours
                quota:
                  type: string
                  description: Quota de l'utilisateur
        responses:
            200:
                description: Utilisateur ajouté avec succès
            400:
                description: Erreur lors de l'ajout de l'utilisateur
            406:
                description: Erreur utilisateur déjà existant
        
        """
        try:
                     
            if kwargs: # appel depuis une autre api
              email_secours = kwargs.get('email_secours')
              prenom = kwargs.get('prenom')
              nom = kwargs.get('nom')
              password = kwargs.get('password')
              quota = kwargs.get('quota')
              
            else: # appel depuis swagger 
              email_secours = request.json.get('email_secours')
              nom = request.json.get('nom')
              prenom = request.json.get('prenom')
              password = request.json.get('password')
              quota = request.json.get('quota')

            password_chiffre = sha512_crypt.hash(password)

            if not validate_email(email) or not validate_email(email_secours):
                return "Adresse e-mail ou secours non valide", 400
            
            #le user existe t-il déjà ?
            ldap_connection = self._connect_ldap()
            result = ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, "(cn={})".format(email))
            
            if result:
                return "User déjà existant", 406

            # Construire le DN
            dn = f"cn={email},ou=users,{ldap_root}"
            
            mod_attrs = [
              ('objectClass', [b'inetOrgPerson', b'PostfixBookMailAccount', b'nextcloudAccount', b'kaznaute']),
              ('sn', f'{prenom} {nom}'.encode('utf-8')),
              ('mail', email.encode('utf-8')),
              ('mailEnabled', b'TRUE'),
              ('mailGidNumber', b'5000'),
              ('mailHomeDirectory', f"/var/mail/{email.split('@')[1]}/{email.split('@')[0]}/".encode('utf-8')),
              ('mailQuota', f'{quota}G'.encode('utf-8')),
              ('mailStorageDirectory', f"maildir:/var/mail/{email.split('@')[1]}/{email.split('@')[0]}/".encode('utf-8')),
              ('mailUidNumber', b'5000'),
              ('mailDeSecours', email_secours.encode('utf-8')),
              ('identifiantKaz', f'{prenom.lower()}.{nom.lower()}'.encode('utf-8')),
              ('quota', str(quota).encode('utf-8')),
              ('nextcloudEnabled', b'TRUE'),
              ('nextcloudQuota', f'{quota} GB'.encode('utf-8')),
              ('mobilizonEnabled', b'TRUE'),
              ('agoraEnabled', b'TRUE'),
              ('userPassword', f'{{CRYPT}}{password_chiffre}'.encode('utf-8')),
              ('cn', email.encode('utf-8'))            
            ]
            
            ldap_connection.add_s(dn, mod_attrs)
            ldap_connection.unbind_s()
            
            return "Utilisateur créé dans le ldap", 200
          
        except ldap.LDAPError as e:
            return str(e), 400
        except EmailNotValidError as e:
            return str(e), 400

#*************************************************