def renameAccount(name, newName): """ Permet de renommer un compte :param name: nom du compte à renommer :param newName: le nouveau nom du compte :param name: le nom du compte à renommer :param newName: le nouveau nom du compte :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail preSupprimé :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not utils.checkIsMailAddress(name) or not utils.checkIsMailAddress( newName): raise NameException("L'adresse mail n'est pas valide") data = {"name": name, "newname": newName} response = callMethod(services.extractDomain(name), "RenameAccount", data) if not utils.checkResponseStatus(response["status"]): raise ServiceException(response["status"], response["message"])
def zimbraPrefMailForwardingAddress(self, value): if isinstance(value, str) or value is None: if utils.checkIsMailAddress(value): self._zimbraPrefMailForwardingAddress = value else: raise NameException("L'adresse mail " + value + " n'est pas une adresse mail valide") else: raise TypeError
def setPassword(name, newPassword): if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") data={ "name": name, "password": newPassword } response = callMethod(services.extractDomain(name), "SetPassword", data) checkResponseStatus(response)
def removeAccountAlias(name, aliasToDelete): """ Méthode permettant de supprimer un alias d'un compte :param name: le nom du compte :param aliasToDelete: l'alias a supprimer :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail preSupprimé :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not utils.checkIsMailAddress(name) or not utils.checkIsMailAddress(aliasToDelete): raise NameException("L'adresse mail " + name +" ou "+aliasToDelete+" n'est pas valide") data = { "name": name, "alias": aliasToDelete } response = callMethod(services.extractDomain(name), "RemoveAccountAlias", data) checkResponseStatus(response)
def zimbraCalResContactEmail(self, value): if isinstance(value, str) or value is None: if utils.checkIsMailAddress(value): self._zimbraCalResContactEmail = value else: raise NameException("L'adresse mail " + value + " n'est pas une adresse mail valide") else: raise TypeError("zimbraCalResContactEmail")
def __init__(self, name=None): if name is not None and not isinstance(name, str): raise TypeError if name is not None and not utils.checkIsMailAddress(name): raise NameException("Adresse mail {} invalide".format(name)) GlobalModel.__init__(self, name) for a in Resource.ATTRIBUTES: setattr(self, '_{}'.format(a), None)
def modifyAccountAliases(name, listOfAliases): """ Méthode permettant de changer l'ensemble des alias d'un compte par ceux passés en paramètre :param name: le nom du compte :param listOfAliases: la liste des alias pour le compte :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail preSupprimé :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide :raises TypeError: Exception levée si le parametre listOfAliases n'est pas une liste """ if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") if not isinstance(listOfAliases, list): raise TypeError account = getAccount(name) #On vérifie que les adresses mail passées en paramètres sont des adresses valide for alias in listOfAliases: if not utils.checkIsMailAddress(alias): raise NameException("L'adresse mail " + alias + " n'est pas valide") #On parcour la liste passé en paramètre for alias in listOfAliases: if isinstance(account.zimbraMailAlias, list) or isinstance( account.zimbraMailAlias, str): #si la l'adresse mail n'est pas présente dans partage on la rajoute if alias not in account.zimbraMailAlias: addAccountAlias(name, alias) #si la liste partage est vide on rajoute l'adresse elif account.zimbraMailAlias is None: addAccountAlias(name, alias) if isinstance(account.zimbraMailAlias, list): #On parcours la liste des adresses partages for alias in account.zimbraMailAlias: #Si l'adresse n'est pas présente dans la liste passé en parametre on supprime l'adresse de partage if alias not in listOfAliases: removeAccountAlias(name, alias) #Si le compte n'a qu'un alias on test si il est présent ou pas dans la liste passé en paramètre elif isinstance(account.zimbraMailAlias, str): if account.zimbraMailAlias not in listOfAliases: removeAccountAlias(name, account.zimbraMailAlias)
def extractDomain(mailAddress): """ Méthode permettant d'extraire le domaine d'une adresse mail :param mailAddress: adresse mail pour extraire le domaine :return: le nom de domaine :raises NameException: Exception levée si l'adresse mail n'est pas valide """ if utils.checkIsMailAddress(mailAddress): return mailAddress.split("@")[1] else: raise NameException("L'adresse mail " + mailAddress + " n'est pas valide")
def __init__(self, name): if utils.checkIsMailAddress(name): GlobalModel.__init__(self, name) self._id = None self._admin = None self._businessCategory = None self._co = None self._company = None self._description = None self._displayName = None self._carLicense = None self._facsimileTelephoneNumber = None self._givenName = None self._homePhone = None self._initials = None self._l = None self._mavTransformation = None self._mavRedirection = None self._mobile = None self._pager = None self._postalCode = None self._quota = None self._sn = None self._st = None self._street = None self._telephoneNumber = None self._title = None self._used = None self._zimbraAccountStatus = None self._zimbraFeatureBriefcasesEnabled = None self._zimbraFeatureCalendarEnabled = None self._zimbraFeatureMailEnabled = None self._zimbraFeatureMailForwardingEnabled = None self._zimbraFeatureOptionsEnabled = None self._zimbraFeatureTasksEnabled = None self._zimbraHideInGal = None self._zimbraLastLogonTimestamp = None self._zimbraMailQuota = None self._zimbraNotes = None self._zimbraPasswordMustChange = None self._zimbraPrefMailForwardingAddress = None self._zimbraPrefMailLocalDeliveryDisabled = None self._zimbraMailAlias = None self._zimbraMailCanonicalAddress = None self._zimbraPrefFromDisplay = None self._zimbraCOSId = None self._zimbraZimletAvailableZimlets = None else: raise NameException("Le nom donné n'est pas une adresse mail")
def activateAccount(name): """ Méthode permettant de passer l'état d'un compte à activer :param name: le nom du compte à (ré)activer :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail preSupprimé :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") account = models.Account(name) account.zimbraAccountStatus = "active" modifyAccount(account)
def deleteAccount(name): """ Permet de supprimer un compte :param name: Nom du compte à supprimer :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail valide :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") data = {"name": name} response = callMethod(services.extractDomain(name), "DeleteAccount", data) if not utils.checkResponseStatus(response["status"]): raise ServiceException(response["status"], response["message"])
def closeAccount(name): """ Cette méthode déconnecte toutes les instances du compte et empêche la connexion à celui-ci. Le compte ne sera plus visible dans la GAL et les mails entrants seront rejetés :param name: le nom du compte à Désactiver :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail preSupprimé :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail n'est pas valide") setPassword(name, "valeurPourDeconnecterLesSessions") account = models.Account(name) account.zimbraAccountStatus = "closed" account.zimbraHideInGal = True modifyAccount(account)
def preDeleteAccount(name): """ Permet de mettre un compte dans un état de préSuppression Cette méthode désactive le compte puis le renomme (ajout d'un préfixe 'deleted_timestampactuel_name') :param name: nom du compte à préSupprimer :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail valide :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") closeAccount(name) newname = "readytodelete_" + utils.changeTimestampToDate(round( time())) + "_" + name renameAccount(name, newname) return newname
def _group_set_op(name_or_group, entries, f_name, op_name): """ Fonction interne utilisée pour effectuer les ajouts ou suppressions sur les listes de membres, d'alias et d'autorisations d'expédition d'un groupe. :param name_or_group: le nom du groupe à modifier, ou l'instance du modèle \ correspondante :param entries: l'entrée ou les entrées à ajouter ou supprimer :param f_name: le nom du champ tel qu'il doit être envoyé à l'API BSS; si \ le nom finit par '[]', toutes les informations seront envoyées \ en un seul appel :param op_name: le nom de la méthode distante à utiliser :raises TypeError: le groupe n'est ni un nom, ni une instance de modèle :raises NameException: l'adresse de groupe ou l'une des entrées est \ incorrecte :raises ServiceException: la requête vers l'API a echoué :raises DomainException: le domaine n'est pas valide """ if isinstance(entries, str): entries = [entries] else: entries = list(entries) if isinstance(name_or_group, Group): name = name_or_group.name else: if not isinstance(name_or_group, str): raise TypeError name = name_or_group for n in (name, *entries): if not utils.checkIsMailAddress(n): raise NameException("L'adresse mail {} n'est pas valide".format(n)) domain = services.extractDomain(name) if '[]' in f_name: entries = [entries] for entry in entries: data = { 'name': name, f_name: entry, } response = callMethod(domain, op_name, data) checkResponseStatus(response)
def lockAccount(name): """ Méthode permettant de passer l'état d'un compte à lock Cette état déconnecte toutes les instances du compte et empêche la connexion à celui-ci. Le compte sera toujours visible dans la GAL et les mails seront toujours acheminés vers cette boîte :param name: le nom du compte à verrouiller :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail preSupprimé :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") setPassword(name, "valeurPourDeconnecterLesSessions") account = models.Account(name) account.zimbraAccountStatus = "locked" modifyAccount(account)
def createGroup(name_or_group): """ Crée un groupe ou une liste de distribution en se basant sur une instance du modèle, ou simplement en utilisant un nom. Si une instance est utilisée et que des alias, des membres ou des autorisations d'expéditions sont présents dans l'instance, ils seront ajoutés au groupe sur Partage après sa création. :param name_or_group: le nom du groupe à créer, ou bien une instance du \ modèle contenant les informations initiales au sujet du groupe. :raises TypeError: si le paramètre n'est ni un nom ni une instance du modèle :raises NameError: si l'adresse du groupe à créer est invalide, ou si \ l'une des autres informations de ce type (alias, membres, \ autorisation) est incorrecte :raises ServiceException: la requête vers l'API a echoué :raises DomainException: le domaine n'est pas valide """ is_group = isinstance(name_or_group, Group) if is_group: if name_or_group.name is None: raise NameException("L'adresse mail n'est pas renseignée") data = name_or_group.to_bss() else: if not isinstance(name_or_group, str): raise TypeError data = {'name': name_or_group} if not utils.checkIsMailAddress(data['name']): raise NameException("L'adresse mail {} n'est pas valide".format(n)) domain = services.extractDomain(data['name']) response = callMethod(domain, 'CreateGroup', data) checkResponseStatus(response) if not is_group: return if name_or_group.has_aliases: addGroupAliases(name_or_group.name, name_or_group.aliases) if name_or_group.has_members: addGroupMembers(name_or_group.name, name_or_group.members) if name_or_group.has_senders: addGroupSenders(name_or_group.name, name_or_group.senders)
def addZimbraPrefCalendarForwardInvitesTo(self, value): if isinstance(value, str) or value is None: if self._zimbraPrefCalendarForwardInvitesTo is None: self._zimbraPrefCalendarForwardInvitesTo = [] if value not in self._zimbraPrefCalendarForwardInvitesTo: if utils.checkIsMailAddress(value): self._zimbraPrefCalendarForwardInvitesTo.append(value) else: raise TypeError("addZimbraPrefCalendarForwardInvitesTo") elif isinstance(value, collections.OrderedDict): if isinstance(value['zimbraPrefCalendarForwardInvitesTo'], list): if self._zimbraPrefCalendarForwardInvitesTo is None: self._zimbraPrefCalendarForwardInvitesTo = [] if value[ 'zimbraPrefCalendarForwardInvitesTo'] not in self._zimbraPrefCalendarForwardInvitesTo: self._zimbraPrefCalendarForwardInvitesTo += value[ 'zimbraPrefCalendarForwardInvitesTo'] else: raise TypeError("addZimbraPrefCalendarForwardInvitesTo")
def createResourceExt(resource: Resource): """ Méthode permettant de créer une ressource via l'API BSS en lui passant en paramètre les informations concernant un compte :param Resource resource: l'objet contenant les informations de la resource """ if not utils.checkIsMailAddress(resource.name): raise NameException("L'adresse mail " + resource.name + " n'est pas valide") data = resource.toData() domain = services.extractDomain(resource.name) response = callMethod(domain, 'CreateResource', data) checkResponseStatus(response) return resource
def createAccount(name,userPassword, cosId = None, account = None): """ Méthode permettant de créer un compte via l'API BSS en lui passant en paramètre l'empreinte du mot de passe (SSHA) et le cosId :param userPassword: l'empreine du mot de passe de l'utilisateur :param cosId: l'identifiant du cosId à appliquer pour le compte :param account: objet account contenant les informations à ajouter dans le compte (optionnel) :return: Le compte créé :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail valide :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not re.search(r'^\{\S+\}', userPassword): raise NameException("Le format de l'empreinte du mot de passe n'est pas correcte ; format attendu : {algo}empreinte") if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") # Les attributs issus de l'objet account data = {} if account is not None: data = account.toData() # Les attributs obligatoires data.update({ "name": name, "password": "", "userPassword": userPassword, "zimbraHideInGal": "FALSE" }) if cosId is not None: data["zimbraCOSId"]= cosId response = callMethod(services.extractDomain(name), "CreateAccount", data) checkResponseStatus(response) # if account is not None: # modifyAccount(account) return getAccount(name)
def getAccount(name): """ Méthode permettant de récupérer les informations d'un compte via l'API BSS :return: Le compte récupéré ou None si le compte n'existe pas :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail valide :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") data = {"name": name} response = callMethod(services.extractDomain(name), "GetAccount", data) if utils.checkResponseStatus(response["status"]): account = response["account"] return fillAccount(account) elif re.search(".*no such account.*", response["message"]): return None else: raise ServiceException(response["status"], response["message"])
def toData(self, checkName=True): """ Transforme les données d'une ressource' en un dictionnaire pouvant être utilisé avec l'API BSS, après avoir éventuellement vérifié l'adresse. :param bool checkName: vérifie l'adresse associée au compte :raises NameException: exception levée si le nom n'est pas une \ adresse mail valide :return: le dictionnaire contenant les informations au sujet du \ compte et pouvant être passé à l'API BSS. """ if self.name is None: raise NameException('Aucune adresse mail spécifiée.') if checkName and not utils.checkIsMailAddress(self.name): raise NameException("L'adresse mail " + self.name + " n'est pas valide") data = {} for attr in self.__dict__: attrValue = self.__getattribute__(attr) # On ne prend pas le préfixe '_' attrKey = attr[1:] if (self.__getattribute__(attr) is None): continue if isinstance(attrValue, list) or attrValue == 'DELETE_ARRAY': # On prévoit une valeur spéciale 'DELETE_ARRAY' pour effacer un attribut de type tableau if attrValue == 'DELETE_ARRAY': attrValue = '' attrKey = attrKey + '[]' if isinstance(attrValue, bool): attrValue = utils.changeBooleanToString(attrValue) data[attrKey] = attrValue return data
def RemoveRootShare(account): """ Cet appel permet de retirer un partage root d'une boites de service d'un ou plusieurs utilisateurs :param account: Adresse email du compte qui fournit le partage root :raises NameError: si l'adresse du partage à créer est invalide """ data = {} # On vérifie si le mail est valide if not utils.checkIsMailAddress(account): raise NameException( "L'adresse mail {} n'est pas valide".format(account)) else: # Préparation des attributs data.update({ "account": account, }) response = callMethod(services.extractDomain(account), "/account/RemoveRootShare", data) checkResponseStatus(response)
def fillAccount(accountResponse): """ Permet de remplir un objet compte depuis une réponse de l'API BSS :param accountResponse: l'objet account renvoyé par l'API :return: l'objet account créé :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail valide """ if not utils.checkIsMailAddress(accountResponse["name"]): raise NameException("L'adresse mail " + accountResponse["name"] + " n'est pas valide") retAccount = models.Account(accountResponse["name"]) accountKeys = accountResponse.keys() for attr in accountKeys: if accountResponse[attr] is not None: if isinstance(accountResponse[attr], str): if accountResponse[attr] == "TRUE" or accountResponse[ attr] == "FALSE": retAccount.__setattr__( "_" + attr, utils.changeStringToBoolean(accountResponse[attr])) else: retAccount.__setattr__("_" + attr, accountResponse[attr]) elif isinstance(accountResponse[attr], OrderedDict): if "type" in accountResponse[attr].keys(): if accountResponse[attr]["type"] == "integer": retAccount.__setattr__( "_" + attr, int(accountResponse[attr]["content"])) elif accountResponse[attr]["type"] == "array": if attr == "zimbraZimletAvailableZimlets": retAccount.__setattr__( "_" + attr, accountResponse[attr] ["zimbraZimletAvailableZimlet"]) elif attr == "zimbraMailAlias": retAccount.__setattr__( "_" + attr, accountResponse[attr]["zimbraMailAlias"]) return retAccount
def modifyPassword(name, newUserPassword): """ Pour modifier le mot de passe on n'accepte que l'empreinte du mot de passe. On commence par faire un SetPassword avec une valeur factice pour forcer la déconnexion des sessions en cours On passe ensuite via ModifyAccount l'empreinte du nouveau mot de passe :param newUserPassword: :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail preSupprimé :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not re.search(r'^\{\S+\}', newUserPassword): raise NameException( "Le format de l'empreinte du mot de passe n'est pas correcte ; format attendu : {algo}empreinte" ) if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") setPassword(name, "valeurPourDeconnecterLesSessions") data = {"name": name, "userPassword": newUserPassword} response = callMethod(services.extractDomain(name), "ModifyAccount", data) if not utils.checkResponseStatus(response["status"]): raise ServiceException(response["status"], response["message"])
def RemoveRootDelegate(account, right=["sendAs"]): """ Cet appel permet de retirer aux bénéficiaires d'un partage root le droit d'envoyer des email \ "en tant que" et/ou "de la part de" de la boite de service source. :param account: Adresse email du compte qui fournit le partage root :param rights: Les droit retirés pour l'envoi de mail:\ sendAs="en tant que", sendOnBehalfOf="de la part de" :raises NameError: si l'adresse du partage à créer est invalide """ data = {} # On vérifie si le mail est valide if not utils.checkIsMailAddress(account): raise NameException( "L'adresse mail {} n'est pas valide".format(account)) else: # Préparation des attributs data.update({"account": account, "right": right}) response = callMethod(services.extractDomain(account), "/account/RemoveRootDelegate", data) checkResponseStatus(response)
def preDeleteAccount(name): """ Permet de mettre un compte dans un état de préSuppression Cette méthode désactive le compte puis le renomme (ajout d'un préfixe 'deleted_timestampactuel_name') :param name: nom du compte à préSupprimer :raises ServiceException: Exception levée si la requête vers l'API à echoué. L'exception contient le code de l'erreur et le message :raises NameException: Exception levée si le nom n'est pas une adresse mail valide :raises DomainException: Exception levée si le domaine de l'adresse mail n'est pas un domaine valide """ if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail " + name + " n'est pas valide") closeAccount(name) newname = "readytodelete_"+utils.changeTimestampToDate(round(time()))+"_"+name renameAccount(name, newname) # On altère l'EPPN (champ carLicense) pour éviter un conflit en cas de création d'un compte avec même EPPN account = getAccount(newname) account2 = models.Account(newname) account2.carLicense = "DISABLED_" + account.carLicense modifyAccount(account2) return newname
def getResource(name): """ Lit les informations concernant une ressource. :param name: l'adresse mail de la resource :raises NameException: l'adresse de groupe spécifiée est incorrecte :raises ServiceException: la requête vers l'API a echoué :raises DomainException: le domaine n'est pas valide :return: la Ressource, sous la forme d'une instance du modèle, ou bien None \ si aucune resource ne correspond au nom spécifié """ if not utils.checkIsMailAddress(name): raise NameException("L'adresse mail {} n'est pas valide".format(name)) data = {'name': name} domain = services.extractDomain(name) response = callMethod(domain, 'GetResource', data) try: checkResponseStatus(response) except NotFoundException: return None resource = response['resource'] return Resource.from_bss(resource)
def getSendAsGroup(name_or_group): """ Lit la liste des utilisateurs autorisés à expédier du mail en utilisant l'adresse d'un groupe comme expéditeur. :param name_or_group: l'adresse d'un groupe ou l'instance correspondant au \ groupe pour lequel on veut lire la liste des autorisations. Si une \ instance est passée, son champ 'senders' sera mis à jour :raises NameException: l'adresse de groupe spécifiée est incorrecte :raises ServiceException: la requête vers l'API a echoué :raises DomainException: le domaine n'est pas valide :return: l'ensemble des utilisateurs autorisés, ou None si le groupe n'a \ pas été trouvé """ if isinstance(name_or_group, Group): name = name_or_group.name group = name_or_group else: name = name_or_group if not utils.checkIsMailAddress(name): raise NameException( "L'adresse mail {} n'est pas valide".format(name)) group = Group(name) data = {'name': name} domain = services.extractDomain(name) response = callMethod(domain, 'GetSendAsGroup', data) try: checkResponseStatus(response) except NotFoundException: return None return group.senders_from_bss(response)
def test_checkIsMailAddress_casFalseSansAdresseMaisAvecDomaine(): assert not checkIsMailAddress("@domain.fr")
def test_checkIsMailAddress_casFalseSansExtensionDeDomain(): assert not checkIsMailAddress("super.test@domain")