def test_autoresponse_json(self):

        """ Create a JSON-request and pass this to send_request expection a
        json response.
        """

        config = get_config()

        if config.getboolean("autoresponse_test", "enabled"):

            # Run only if enabled

            token = authenticate(
                config.get("autoresponse_test", "url"),
                config.get("autoresponse_test", "account"),
                config.get("autoresponse_test", "preauthkey")
            )

            self.assertNotEqual(
                token,
                None,
                "Cannot authenticate."
            )

            request = RequestJson()
            request.set_auth_token(token)
            request.add_request(
                "NoOpRequest",
                {

                },
                "urn:zimbraMail"
            )

            comm = Communication(config.get("autoresponse_test", "url"))

            response = comm.send_request(request)

            if response.is_fault():

                self.fail(
                    "Reponse failed: (%s) %s" % (
                        response.get_fault_code(),
                        response.get_fault_message()
                    )
                )

            self.assertEqual(
                response.response_type,
                "json",
                "Invalid response type %s" % response.response_type
            )
Ejemplo n.º 2
0
    def test_fault_non_existing_folder_batch_json(self):

        """ Request a non existing folder multiple times to get multiple
        faults
        """

        config = get_config()

        if config.getboolean('fault_test', 'enabled'):

            comm = Communication(config.get('fault_test', 'url'))

            token = auth.authenticate(config.get('fault_test', 'url'),
                                      config.get('fault_test', 'account'),
                                      config.get('fault_test', 'preauthkey'),
                                      config.get('fault_test', 'account_by'))

            request = RequestJson()

            request.set_auth_token(token)

            request.enable_batch()

            request.add_request(
                "GetFolderRequest",
                {
                    "folder": {
                        "path": config.get('fault_test', 'folder')
                    }
                },
                "urn:zimbraMail"
            )

            request.add_request(
                "GetFolderRequest",
                {
                    "folder": {
                        "path": config.get('fault_test', 'folder')
                    }
                },
                "urn:zimbraMail"
            )

            response = ResponseJson()

            comm.send_request(request, response)

            self.check_response(
                response
            )
Ejemplo n.º 3
0
class ZimbraRequest(object):
  def __init__(self, admin_url, admin_user, admin_pass):
    self.admin_url = admin_url
    self.admin_user = admin_user
    self.admin_pass = admin_pass
    self.admin_account_by = 'name'
    self.request = None

    self.token = authenticate(
      self.admin_url, 
      self.admin_user,
      self.admin_pass,
      self.admin_account_by,
      admin_auth=True,
      request_type="json"
    )

    self.comm = Communication(self.admin_url)

  def getAllAdminAccounts(self, domain_name):
    self.cleanUp()
    self.request.add_request(
      request_name = 'GetAllAccountsRequest',
      request_dict = { 
        "domain": {
          "_content" : domain_name,
          "by": "name",
        },
        "a" : {
          "n" : "zimbraIsAdminAccount",
          "_content" : "TRUE"
        }
      },
      namespace = 'urn:zimbraAdmin'
    )

    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error getting all admin accounts. Error: %s' % response.get_fault_message(), 
        response = response
      )

    return response.get_response()

  def createDomain(self, domain_name, attrs=list()):
    """ Create a new domain
    :param domain_name: The name of the domain
    :param attrs: List of tuple attributes of domain (zmprov desc domain)
    """
    if not type(attrs) == list:
      raise TypeError('attrs must be a list')
    self.cleanUp()

    request_attrs = []
    for attr in attrs:
      zattr, value = attr
      request_attrs.append({
        'n' : zattr,
        '_content' : value
      })

    request_dict = { 'name' : domain_name }
    if request_attrs:
      request_dict['a'] = request_attrs

    self.request.add_request(
      request_name = 'CreateDomainRequest',
      request_dict = request_dict,
      namespace = 'urn:zimbraAdmin'
    )

    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error creating domain %s Error: %s' % (domain_name, response.get_fault_message()), 
        response = response
      )

    return response.get_response()

  def deleteDomain(self, domain_id):
    """ Delete a domain
    :param domain_id: The zimbraId of the domain
    """
    self.cleanUp()

    self.request.add_request(
      request_name = 'DeleteDomainRequest',
      request_dict = { 'id' : domain_id },
      namespace = 'urn:zimbraAdmin'
    )

    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error deleting domain %s Error: %s' % (domain_id, response.get_fault_message()), 
        response = response
      )

    return response.get_response()

  def getDomain(self, domain, attrs):
    """
    TODO
      - Implement list of attributes to get.
      - Raise exception when an error occours.
    """
    
    """
    Returns the attributes requested in a json format.
     "GetDomainResponse": {
      "domain": {
        "a": [
      {
         "_content": "externalLdapAutoComplete",
         "n": "zimbraGalAutoCompleteLdapFilter"
      },
      {
         "_content": "FALSE",
         "n": "zimbraAdminConsoleDNSCheckEnabled"
      },
      .
      .
      .
      https://files.zimbra.com/docs/soap_api/8.6.0/api-reference/zimbraAdmin/GetDomain.html
    """
    if not type(attrs) == list:
      raise TypeError('attrs must be a list')
    self.cleanUp()

    attrs =  ','.join(attrs)
    self.request.add_request(
      request_name = "GetDomainRequest",
      request_dict = {
        "attrs" : attrs,
        "domain": {
          "_content" : domain,
          "by": "name",
        },
      },
      namespace = "urn:zimbraAdmin"
    )
    
    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error getting domain %s Error: %s' % (domain, response.get_fault_message()), 
        response = response
      )
    
    return response.get_response()
    
  def modifyDomain(self, domain_id, attrs):
    """ Modify an attribute of a domain
    This method is idempotent, it will not change the result executing multiple times
    :param domain_id: The zimbraID of the domain
    :param attrs: A list of tuple containing the zimbra attribute with the corresponding value: [(zattr, value), ...]    
    """
    self.cleanUp()

    request_attrs = []
    for attr in attrs:
      zattr, value = attr
      request_attrs.append({
        'n' : zattr,
        '_content' : value
      })

    self.request.add_request(
      request_name = "ModifyDomainRequest",
      request_dict = {
        "id": {
          "_content": domain_id,
        },
        "a": request_attrs
      },
      namespace = "urn:zimbraAdmin"
    )
    
    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error modifying domain %s Error: %s' % (domain_id, response.get_fault_message()), 
        response = response
      )
    
    return response.get_response()

  def getAccount(self, account, attrs):
    """
    TODO
      - Implement a list of attributes to get.
      - Implement raise exception.
    """
    
    
    """
    Returns a json containing all attributes of an account or an exception:
    
    "GetAccountResponse": {
      "account": {
   "a": [
    {
       "_content": "FALSE",
       "n": "zimbraPrefCalendarReminderMobile"
    },
          {
        "_content": "TRUE",
        "n": "zimbraPrefIMLogChats"
    },
    .
    .
    .
    
    https://files.zimbra.com/docs/soap_api/8.6.0/api-reference/zimbraAdmin/GetAccount.html
    """
    if not type(attrs) == list:
      raise TypeError('attrs must be a list')

    attrs =  ','.join(attrs)
    self.cleanUp()
    
    self.request.add_request(
      "GetAccountRequest",
      {
        "attrs" : attrs,
        "account": {
          "_content": account,
          "by": "name",
        },
      },
      "urn:zimbraAdmin"
    )
    
    response = self.comm.send_request(self.request)
    
    if response.is_fault():
      raise ZimbraRequestError("Reponse failed: (%s) %s" % (response.get_fault_code(), response.get_fault_message()))
    return(response.get_response())

  def createAccount(self, account, password=None, attrs=list()):
    """ Create a new account into Zimbra system
    :param account: The target account
    :param password: The given for the account
    :param attrs: A list of tuple containing the zimbra attribute with the corresponding value: [(zattr, value), ...]
    """
    request_attrs = []
    for attr in attrs:
      zattr, value = attr
      request_attrs.append({
        'n' : zattr,
        '_content' : value
      })
    
    self.cleanUp()

    if not password:
      password = hmac.new(str(uuid.uuid4), str(uuid.uuid4()), hashlib.md5).hexdigest()
    
    self.request.add_request(
      request_name = "CreateAccountRequest",
      request_dict = {
        "name" : account,
        "password" : password,
        "a" : request_attrs
      },
      namespace = "urn:zimbraAdmin"
    )
      
    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error creating account %s. Error: %s' % (account, response.get_fault_message()), 
        response = response
      )

    return response.get_response()
       
  def cleanUp(self):
    """ Clean up after one step to leave a dedicated result for the other
     test cases.
    """
    self.setUp()

  def setUp(self):
    """
    Setup everything required to make a request to zimbra server.
    """
    
    self.request = RequestJson()
    self.request = self.comm.gen_request(token=self.token)

  def getDomainId(self, domain):
    """
    Returns the zimbraId of a domain. Useful to modify a domain with ModifyDomainRequest for instance.
    
    domain_id = self.getDomainId(inova.net)
    equal:
    domain_id = '4af850c7-7e44-452e-ad25-c70fda58f9bf'
    """
    
    self.cleanUp() 
    self.request.add_request(
      "GetDomainInfoRequest",
      {
        "domain": {
          "_content": domain,
          "by": "name",
        },
      },
      "urn:zimbraAdmin"
    )
      
    response = self.comm.send_request(self.request)
      
    if response.is_fault():
      raise ZimbraRequestError("Reponse failed: (%s) %s" % (response.get_fault_code(), response.get_fault_message()))
    
    return response.get_response()['GetDomainInfoResponse']['domain']['id']
    
  def getDomainQuotaUsage(self,domain):
    """
     Returns quota usage of all users of a specific domain
      {
      "GetQuotaUsageResponse": {
       "searchTotal": 1294,
       "account": [
        {
        "used": 0,
    "limit": 0,
    "name": "*****@*****.**",
    "id": "63b128d6-b7f2-466d-ac86-7b253e62a7ed"
     },
     {
    "used": 28,
    "limit": 26843545600,
    "name": "*****@*****.**",
    "id": "5b4832d1-b642-4778-ab7d-3056ebcefada"
     },
    .
    .
    .
      https://files.zimbra.com/docs/soap_api/8.6.0/api-reference/zimbraAdmin/GetQuotaUsage.html

    """
    self.cleanUp()

    self.request.add_request(
        "GetQuotaUsageRequest",
        {
            "domain": domain,
      "allServers": "1",
      "sortBy": "percentUsed",
      "sortAscending": "1",
        },
        "urn:zimbraAdmin"
    )

    response = self.comm.send_request(self.request)
    
    if response.is_fault():
      raise ZimbraRequestError("Reponse failed: (%s) %s" % (response.get_fault_code(), response.get_fault_message()))
    
    return(response.get_response())

  def getCos(self, cos_name):
    """ Get COS by it's name
    :param cos_name: The name of the COS
    """
    self.cleanUp()
    self.request.add_request(
      request_name = 'GetCosRequest',
      request_dict = {
        'cos' : {
          'by' : 'name',
          '_content' : cos_name
        }
      },
      namespace = 'urn:zimbraAdmin'
    )
    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error getting COS %s. Error: %s' % (cos_name, response.get_fault_message()), 
        response = response
      )

    return response.get_response()

  def createCos(self, cos_name, features=dict()):
    """ Create a new cos.
    :param cos_name: The name of the COS
    :param features: A dict representing the feature->value 
    """
    if type(features) is not dict:
      raise TypeError('Wrong type found for features, must be a dict.')

    features_req = []
    for feature, value in features.items():
      features_req.append({
        'n' : feature ,
        '_content' : value
      })

    self.cleanUp()
    self.request.add_request(
      request_name = 'CreateCosRequest',
      request_dict = { 
        'name' : { '_content' : cos_name },
        'a' : features_req
      },
      namespace = 'urn:zimbraAdmin'
    )

    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error creating COS %s Error: %s' % (cos_name, response.get_fault_message()), 
        response = response
      )

    return response.get_response()

  def modifyCos(self, zimbra_cos_id, features):
    """ Update a cos. 
    :param zimbra_cos_id: The zimbraID of the COS
    :param features: A dict representing the feature->value 
    """
    if type(features) is not dict:
      raise TypeError('Wrong type found for features, must be a dict')

    features_req = []
    for feature, value in features.items():
      features_req.append({
        'n' : feature,
        '_content' : value
      })

    self.cleanUp()
    self.request.add_request(
      request_name = 'ModifyCosRequest',
      request_dict = {
        'id' : {
          '_content' : zimbra_cos_id
        },
        'a' : features_req
      },
      namespace = 'urn:zimbraAdmin'
    )

    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error creating COS %s Error: %s' % (cos_name, response.get_fault_message()), 
        response = response
      )
    
    return response.get_response()

  def deleteCos(self, zimbra_cos_id):
    """ Delete a specific COS
    :param zimbra_cos_id: The zimbraID of the COS
    """
    self.cleanUp()
    self.request.add_request(
      request_name = 'DeleteCosRequest',
      request_dict = {
        'id' : { '_content' : zimbra_cos_id }
      },
      namespace = 'urn:zimbraAdmin'
    )
    response = self.comm.send_request(self.request)

    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error creating COS %s Error: %s' % (cos_name, response.get_fault_message()), 
        response = response
      )
      
    return response.get_response()

  def getComputeAggregateQuotaUsage(self):
    """ This method get all quota usage of all domains in a zimbra system. This may take a while depending how many domains and servers you have. Use wisely :P
    """

    self.cleanUp()

    self.request.add_request(
        "ComputeAggregateQuotaUsageRequest",
        {
        },
        "urn:zimbraAdmin"
    )

    response = self.comm.send_request(self.request)

    if response.is_fault():
      raise ZimbraRequestError("Reponse failed: (%s) %s" % (response.get_fault_code(), response.get_fault_message()))

    return(response.get_response())

  def createDistributionList(self, dlist, attrs=list()):
    """ This method create zimbra distribution list. A list of attributes may be given, we will handle it for you.
    :param dlist: The target distribution list
    :param attrs: List of tuple attributes of distribution list
    """
    if not type(attrs) == list:
      raise TypeError('attrs must be a list')

    request_attrs = []
    for attr in attrs:
      zattr, value = attr
      # If it's a list, then it's a multi-value attribute
      if type(value) == list:
        for multi_attr in value:
          request_attrs.append({
            'n' : zattr,
            '_content' : multi_attr
          })
      else:
        request_attrs.append({
          'n' : zattr,
          '_content' : value
        })
    self.cleanUp()
    
    self.request.add_request(
      request_name = "CreateDistributionListRequest",
      request_dict = {
        "name": dlist,
        "a": request_attrs
      },      
      namespace = "urn:zimbraAdmin"
    )
      
    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error creating DL %s Error: %s' % (dlist, response.get_fault_message()), 
        response = response
      )

    return response.get_response()

  def getDistributionList(self, dlist):
    """ Gets information about a distribution list.
    :param dlist: The target distribution list
    Obs: Tested with "a" attribute does not have effect on result
    """    
    self.cleanUp()
    self.request.add_request(
      request_name = "GetDistributionListRequest",
      request_dict = {
        "dl": {
          "_content": dlist,
          "by": "name",
        },
      },
      namespace = "urn:zimbraAdmin"
    )
            
    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error getting DL: %s Error: %s' % (dlist, response.get_fault_message()), 
        response = response
      )
    
    return response.get_response()

  def deleteDistributionList(self, dlist_zimbra_id):
    """ Deletes distribution list
    :param dlist_zimbra_id: Distribution List zimbraID
    """    
    self.cleanUp() 
    self.request.add_request(
      request_name = "DeleteDistributionListRequest",
      request_dict = { "id" : dlist_zimbra_id },
      namespace = "urn:zimbraAdmin"
    )      
    
    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error deleting DL: %s Error: %s' % (dlist, response.get_fault_message()), 
        response = response
      )
    return response.get_response()

  def addDistributionListMember(self, dlist_zimbra_id, members):
    """ This method adds members to a zimbra distribution list. A list of members must be sent.
    This method is idempotent, it will not change the result executing multiple times
    :param dlist_zimbra_id: The target distribution list zimbraId
    :param members: List containing the account members
    """
    if not type(members) == list:
      raise TypeError('members must be a list')

    zmembers = []
    for member in members:
      zmembers.append({'_content': member})
    self.cleanUp()
    
    self.request.add_request(
      request_name = "AddDistributionListMemberRequest",
      request_dict = {
        "id": dlist_zimbra_id,
        "dlm": zmembers
      },      
      namespace = "urn:zimbraAdmin"
    )
      
    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error adding members to dlist %s Error: %s' % (dlist_zimbra_id, response.get_fault_message()), 
        response = response
      )

    return response.get_response()

  def grantRight(self, target_name, target_type, grantee_name, grantee_type, right, deny=0):
    """ Grant a right on a target to an individual or group grantee.
    This method is idempotent, it will not change the result executing multiple times
    :param target_name: The target for applying the right (by name). E.g.: 'inova.net'. External docs: /target
    :param target_type: The type of the target. E.g.: 'domain'. External docs: /target@type
    :param grantee_name: Grantee selector. E.g.: 'grp', 'dlist'. External docs: /grantee
    :param grantee_type: The type of the grantee. E.g.: 'grp', 'dlist'. External docs: /grantee@type
    :param right: The name of the right. E.g.: getDomainQuotaUsage, domainAdminConsoleRights. External docs: /right
    :param deny: Either to deny or grant the permission. Default is 0. External docs: /right@deny
    
    Ref. Docs: https://files.zimbra.com/docs/soap_api/8.6.0/api-reference/zimbraAdmin/GrantRight.html    
    """
    self.cleanUp()
    self.request.add_request(
      request_name = "GrantRightRequest",
      request_dict = {
        "target": {
          "type": target_type,
          "by": "name",
          "_content": target_name
        },
        "grantee": {
          "type": grantee_type,
        "by": "name",
          "_content": grantee_name
        },  
        "right": {
        "_content": right,
        "deny": deny
        }
      },
      namespace = "urn:zimbraAdmin"
    )
    response = self.comm.send_request(self.request)
    if response.is_fault():
      raise ZimbraRequestError(
        message = 'Error adding grant to target_name %s Error: %s' % (target_name, response.get_fault_message()), 
        response = response
      )

    return response.get_response()
Ejemplo n.º 4
0
class TestRequestJson(TestCase):
    """ Request tests
    """

    request = None
    """ The request to be tested against """
    def cleanUp(self):
        """ Clean up after one step to leave a dedicated result for the other
         test cases.
        """
        self.setUp()

    def setUp(self):
        self.request = RequestJson()

    def test_empty_request(self):
        """ Create an empty request and check the created xml
        """

        expected_result = '{"Body": {}, "Header": {"context": {"_jsns": ' \
                          '"urn:zimbra", "format": {"type": "js"}}}}'

        self.assertEqual(expected_result, self.request.get_request())

    def test_set_context_params_failtype(self):
        """ Add context parameters to the request and expect the method to
        send an exception
        """

        self.assertRaises(
            RequestHeaderContextException, self.request.set_context_params,
            {'invalidParam': {
                'invalidAttribute': 'invalidValue'
            }})

    def test_set_context_params(self):
        """ Add all currently accepted params and check the result
        """

        self.request.set_context_params({
            'authToken': {
                '_content': '1234567890abcdef'
            },
            'authTokenControl': {
                'voidOnExpired': '1'
            },
            'session': {
                'id': '1234567890abcdef',
                'seq': '1234567890',
                'type': 'admin'
            },
            'account': {
                'by': 'name',
                '_content': '*****@*****.**'
            },
            'change': {
                'token': '1234567890abcdef',
                'type': 'new'
            },
            'targetServer': {
                '_content': 'mailboxserver.zimbra.com'
            },
            'userAgent': {
                'name': 'Mozilla',
                'version': '1.0'
            },
            'via': {
                '_content': 'proxyserver.zimbra.com'
            }
        })

        expected_result = '{"Body": {}, "Header": {"context": {"authToken": {' \
                          '"_content": "1234567890abcdef"}, "account": {"by":' \
                          ' "name", "_content": "*****@*****.**"}, ' \
                          '"via": {"_content": "proxyserver.zimbra.com"}, ' \
                          '"targetServer": {"_content": "mailboxserver.zimbra' \
                          '.com"}, "format": {"type": "js"}, ' \
                          '"_jsns": "urn:zimbra", "session": {"type": ' \
                          '"admin", "id": "1234567890abcdef", ' \
                          '"seq": "1234567890"}, "authTokenControl": {' \
                          '"voidOnExpired": "1"}, "userAgent": {"version": "1' \
                          '.0", "name": "Mozilla"}, "change": {"token": ' \
                          '"1234567890abcdef", "type": "new"}}}}'

        self.assertEqual(expected_result, self.request.get_request())

        # Clean up after this test

        self.cleanUp()

    def test_enable_batch_default(self):
        """ Test enabling batch requests
        """

        # Check with default parameter

        self.request.enable_batch()

        expected_result = '{"Body": {"BatchRequest": {"onerror": "continue", ' \
                          '"_jsns": "urn:zimbra"}}, "Header": {"context": {' \
                          '"_jsns": "urn:zimbra", "format": {"type": "js"}}}}'

        self.assertEqual(expected_result, self.request.get_request())

        # Clean up

        self.cleanUp()

    def test_enable_batch_stop(self):
        """ Test enabling batch requests with additional parameter
        """

        self.request.enable_batch('stop')

        expected_result = '{"Body": {"BatchRequest": {"onerror": "stop", ' \
                          '"_jsns": "urn:zimbra"}}, "Header": {"context": {' \
                          '"_jsns": "urn:zimbra", "format": {"type": "js"}}}}'

        self.assertEqual(expected_result, self.request.get_request())

        # Clean up

        self.cleanUp()

    def test_batch_add_request(self):
        """ Test adding multiple request to a batch request
        """

        self.request.enable_batch()

        request_id = self.request.add_request('GetInfoRequest',
                                              {'sections': 'mbox,prefs'},
                                              "urn_zimbra")

        self.assertIsInstance(
            request_id,
            int,
            msg="Returned request_id for request 1 is not of type int, "
            "but of type %s" % (type(request_id)))

        self.assertEqual(
            1,
            request_id,
            msg="Returned request_id for request 1 is not 1, but %s" %
            (str(request_id)))

        expected_result = \
            '{"Body": {"BatchRequest": {"onerror": "continue", "_jsns": ' \
            '"urn:zimbra", "GetInfoRequest": {"_jsns": "urn_zimbra", ' \
            '"sections": "mbox,prefs", "requestId": 1}}}, "Header": {' \
            '"context": {"_jsns": "urn:zimbra", "format": {"type": "js"}}}}'

        self.assertEqual(expected_result, self.request.get_request())

        request_id = self.request.add_request('GetInfoRequest',
                                              {'sections': 'zimlets'},
                                              "urn:zimbra")

        self.assertIsInstance(
            request_id,
            int,
            msg="Returned request_id for request 2 is not of type int, "
            "but of type %s" % (type(request_id)))

        self.assertEqual(
            2,
            request_id,
            msg="Returned request_id for request 2 is not 2, but %s" %
            (str(request_id)))

        expected_result = \
            '{"Body": {"BatchRequest": {"onerror": "continue", "_jsns": ' \
            '"urn:zimbra", "GetInfoRequest": [{"_jsns": "urn_zimbra", ' \
            '"sections": "mbox,prefs", "requestId": 1}, {"_jsns": ' \
            '"urn:zimbra", "sections": "zimlets", "requestId": 2}]}}, ' \
            '"Header": {"context": {"_jsns": "urn:zimbra", "format": {' \
            '"type": "js"}}}}'

        self.assertEqual(expected_result, self.request.get_request())

        # Clean up

        self.setUp()

    def test_add_request(self):
        """ Test adding a request
        """

        request_id = self.request.add_request('GetInfoRequest',
                                              {'sections': 'mbox,prefs'},
                                              'urn:zimbra')

        self.assertIsNone(request_id,
                          msg="Returned request_id for request 1 is not none, "
                          "but %s" % (str(request_id)))

        expected_result = '{"Body": {"GetInfoRequest": {"_jsns": ' \
                          '"urn:zimbra", "sections": "mbox,prefs"}}, ' \
                          '"Header": {"context": {"_jsns": "urn:zimbra", ' \
                          '"format": {"type": "js"}}}}'

        self.assertEqual(expected_result, self.request.get_request())

        # Clean up

        self.setUp()

    def tearDown(self):
        self.request = None
class TestRequestJson(TestCase):
    """ Request tests
    """

    request = None

    """ The request to be tested against """

    def cleanUp(self):
        """ Clean up after one step to leave a dedicated result for the other
         test cases.
        """
        self.setUp()

    def setUp(self):
        self.request = RequestJson()

    def test_empty_request(self):
        """ Create an empty request and check the created xml
        """

        expected_result = {
            "Body": {},
            "Header": {
                "context": {
                    "_jsns": "urn:zimbra",
                    "format": {
                        "type": "js"
                    }
                }
            }
        }

        self.assertEqual(
            expected_result,
            json.loads(self.request.get_request())
        )

    def test_set_context_params_failtype(self):
        """ Add context parameters to the request and expect the method to
        send an exception
        """

        self.assertRaises(
            RequestHeaderContextException,
            self.request.set_context_params,
            {
                'invalidParam': {
                    'invalidAttribute': 'invalidValue'
                }
            }
        )

    def test_set_context_params(self):
        """ Add all currently accepted params and check the result
        """

        self.request.set_context_params(
            {
                'authToken': {
                    '_content': '1234567890abcdef'
                },
                'authTokenControl': {
                    'voidOnExpired': '1'
                },
                'session': {
                    'id': '1234567890abcdef',
                    'seq': '1234567890',
                    'type': 'admin'
                },
                'account': {
                    'by': 'name',
                    '_content': '*****@*****.**'
                },
                'change': {
                    'token': '1234567890abcdef',
                    'type': 'new'
                },
                'targetServer': {
                    '_content': 'mailboxserver.zimbra.com'
                },
                'userAgent': {
                    'name': 'Mozilla',
                    'version': '1.0'
                },
                'via': {
                    '_content': 'proxyserver.zimbra.com'
                }
            }
        )

        expected_result = {
            "Body": {},
            "Header": {
                "context": {
                    "authToken": {
                        "_content": "1234567890abcdef"
                    },
                    "account": {
                        "by": "name",
                        "_content": "*****@*****.**"
                    },
                    "via": {
                        "_content": "proxyserver.zimbra.com"
                    },
                    "targetServer": {
                        "_content": "mailboxserver.zimbra.com"
                    },
                    "format": {
                        "type": "js"
                    },
                    "_jsns": "urn:zimbra",
                    "session": {
                        "type": "admin",
                        "id": "1234567890abcdef",
                        "seq": "1234567890"
                    },
                    "authTokenControl": {
                        "voidOnExpired": "1"
                    },
                    "userAgent": {
                        "version": "1.0",
                        "name": "Mozilla"
                    },
                    "change": {
                        "token": "1234567890abcdef",
                        "type": "new"
                    }
                }
            }
        }

        self.assertEqual(
            expected_result,
            json.loads(self.request.get_request())
        )

        # Clean up after this test

        self.cleanUp()

    def test_enable_batch_default(self):

        """ Test enabling batch requests
        """

        # Check with default parameter

        self.request.enable_batch()

        expected_result = {
            "Body": {
                "BatchRequest": {
                    "onerror": "continue",
                    "_jsns": "urn:zimbra"
                }
            },
            "Header": {
                "context": {
                    "_jsns": "urn:zimbra",
                    "format": {
                        "type": "js"
                    }
                }
            }
        }

        self.assertEqual(
            expected_result,
            json.loads(self.request.get_request())
        )

        # Clean up

        self.cleanUp()

    def test_enable_batch_stop(self):

        """ Test enabling batch requests with additional parameter
        """

        self.request.enable_batch('stop')

        expected_result = {
            "Body": {
                "BatchRequest": {
                    "onerror": "stop",
                    "_jsns": "urn:zimbra"
                }
            },
            "Header": {
                "context": {
                    "_jsns": "urn:zimbra",
                    "format": {
                        "type": "js"
                    }
                }
            }
        }

        self.assertEqual(
            expected_result,
            json.loads(self.request.get_request())
        )

        # Clean up

        self.cleanUp()

    def test_batch_add_request(self):

        """ Test adding multiple request to a batch request
        """

        self.request.enable_batch()

        request_id = self.request.add_request(
            'GetInfoRequest',
            {
                'sections': 'mbox,prefs'
            },
            "urn_zimbra"
        )

        self.assertIsInstance(
            request_id,
            int,
            msg="Returned request_id for request 1 is not of type int, "
                "but of type %s" % (
                    type(request_id)
                )
        )

        self.assertEqual(
            1,
            request_id,
            msg="Returned request_id for request 1 is not 1, but %s" % (
                str(request_id)
            )
        )

        expected_result = {
            "Body": {
                "BatchRequest": {
                    "onerror": "continue",
                    "_jsns": "urn:zimbra",
                    "GetInfoRequest": {
                        "_jsns": "urn_zimbra",
                        "sections": "mbox,prefs",
                        "requestId": 1
                    }
                }
            },
            "Header": {
                "context": {
                    "_jsns": "urn:zimbra",
                    "format": {
                        "type": "js"
                    }
                }
            }
        }

        self.assertEqual(
            expected_result,
            json.loads(self.request.get_request())
        )

        request_id = self.request.add_request(
            'GetInfoRequest',
            {
                'sections': 'zimlets'
            },
            "urn:zimbra"
        )

        self.assertIsInstance(
            request_id,
            int,
            msg="Returned request_id for request 2 is not of type int, "
                "but of type %s" % (
                    type(request_id)
                )
        )

        self.assertEqual(
            2,
            request_id,
            msg="Returned request_id for request 2 is not 2, but %s" % (
                str(request_id)
            )
        )

        expected_result = {
            "Body": {
                "BatchRequest": {
                    "onerror": "continue",
                    "_jsns": "urn:zimbra",
                    "GetInfoRequest": [
                        {
                            "_jsns": "urn_zimbra",
                            "sections": "mbox,prefs",
                            "requestId": 1
                        },
                        {
                            "_jsns": "urn:zimbra",
                            "sections": "zimlets",
                            "requestId": 2
                        }
                    ]
                }
            },
            "Header": {
                "context": {
                    "_jsns": "urn:zimbra",
                    "format": {
                        "type": "js"
                    }
                }
            }
        }

        self.assertEqual(
            expected_result,
            json.loads(self.request.get_request())
        )

        # Clean up

        self.setUp()

    def test_add_request(self):

        """ Test adding a request
        """

        request_id = self.request.add_request(
            'GetInfoRequest',
            {
                'sections': 'mbox,prefs'
            },
            'urn:zimbra'
        )

        self.assertIsNone(
            request_id,
            msg="Returned request_id for request 1 is not none, "
                "but %s" % (
                    str(request_id)
                )
        )

        expected_result = {
            "Body": {
                "GetInfoRequest": {
                    "_jsns": "urn:zimbra",
                    "sections": "mbox,prefs"
                }
            },
            "Header": {
                "context": {
                    "_jsns": "urn:zimbra",
                    "format": {
                        "type": "js"
                    }
                }
            }
        }

        self.assertEqual(
            expected_result,
            json.loads(self.request.get_request())
        )

        # Clean up

        self.setUp()

    def tearDown(self):
        self.request = None
Ejemplo n.º 6
0
class ZimbraRequest(object):
    def __init__(self, admin_url, admin_user, admin_pass):
        self.admin_url = admin_url
        self.admin_user = admin_user
        self.admin_pass = admin_pass
        self.admin_account_by = 'name'
        self.request = None

        self.token = authenticate(self.admin_url,
                                  self.admin_user,
                                  self.admin_pass,
                                  self.admin_account_by,
                                  admin_auth=True,
                                  request_type="json")

        self.comm = Communication(self.admin_url)

    def getAllAdminAccounts(self, domain_name):
        self.cleanUp()
        self.request.add_request(request_name='GetAllAccountsRequest',
                                 request_dict={
                                     "domain": {
                                         "_content": domain_name,
                                         "by": "name",
                                     },
                                     "a": {
                                         "n": "zimbraIsAdminAccount",
                                         "_content": "TRUE"
                                     }
                                 },
                                 namespace='urn:zimbraAdmin')

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error getting all admin accounts. Error: %s' %
                response.get_fault_message(),
                response=response)

        return response.get_response()

    def createDomain(self, domain_name, attrs=list()):
        """ Create a new domain
    :param domain_name: The name of the domain
    :param attrs: List of tuple attributes of domain (zmprov desc domain)
    """
        if not type(attrs) == list:
            raise TypeError('attrs must be a list')
        self.cleanUp()

        request_attrs = []
        for attr in attrs:
            zattr, value = attr
            request_attrs.append({'n': zattr, '_content': value})

        request_dict = {'name': domain_name}
        if request_attrs:
            request_dict['a'] = request_attrs

        self.request.add_request(request_name='CreateDomainRequest',
                                 request_dict=request_dict,
                                 namespace='urn:zimbraAdmin')

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error creating domain %s Error: %s' %
                (domain_name, response.get_fault_message()),
                response=response)

        return response.get_response()

    def deleteDomain(self, domain_id):
        """ Delete a domain
    :param domain_id: The zimbraId of the domain
    """
        self.cleanUp()

        self.request.add_request(request_name='DeleteDomainRequest',
                                 request_dict={'id': domain_id},
                                 namespace='urn:zimbraAdmin')

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error deleting domain %s Error: %s' %
                (domain_id, response.get_fault_message()),
                response=response)

        return response.get_response()

    def getDomain(self, domain, attrs):
        """
    TODO
      - Implement list of attributes to get.
      - Raise exception when an error occours.
    """
        """
    Returns the attributes requested in a json format.
     "GetDomainResponse": {
      "domain": {
        "a": [
      {
         "_content": "externalLdapAutoComplete",
         "n": "zimbraGalAutoCompleteLdapFilter"
      },
      {
         "_content": "FALSE",
         "n": "zimbraAdminConsoleDNSCheckEnabled"
      },
      .
      .
      .
      https://files.zimbra.com/docs/soap_api/8.6.0/api-reference/zimbraAdmin/GetDomain.html
    """
        if not type(attrs) == list:
            raise TypeError('attrs must be a list')
        self.cleanUp()

        attrs = ','.join(attrs)
        self.request.add_request(request_name="GetDomainRequest",
                                 request_dict={
                                     "attrs": attrs,
                                     "domain": {
                                         "_content": domain,
                                         "by": "name",
                                     },
                                 },
                                 namespace="urn:zimbraAdmin")

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error getting domain %s Error: %s' %
                (domain, response.get_fault_message()),
                response=response)

        return response.get_response()

    def modifyDomain(self, domain_id, attrs):
        """ Modify an attribute of a domain
    This method is idempotent, it will not change the result executing multiple times
    :param domain_id: The zimbraID of the domain
    :param attrs: A list of tuple containing the zimbra attribute with the corresponding value: [(zattr, value), ...]    
    """
        self.cleanUp()

        request_attrs = []
        for attr in attrs:
            zattr, value = attr
            request_attrs.append({'n': zattr, '_content': value})

        self.request.add_request(request_name="ModifyDomainRequest",
                                 request_dict={
                                     "id": {
                                         "_content": domain_id,
                                     },
                                     "a": request_attrs
                                 },
                                 namespace="urn:zimbraAdmin")

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error modifying domain %s Error: %s' %
                (domain_id, response.get_fault_message()),
                response=response)

        return response.get_response()

    def getAccount(self, account, attrs):
        """
    TODO
      - Implement a list of attributes to get.
      - Implement raise exception.
    """
        """
    Returns a json containing all attributes of an account or an exception:
    
    "GetAccountResponse": {
      "account": {
   "a": [
    {
       "_content": "FALSE",
       "n": "zimbraPrefCalendarReminderMobile"
    },
          {
        "_content": "TRUE",
        "n": "zimbraPrefIMLogChats"
    },
    .
    .
    .
    
    https://files.zimbra.com/docs/soap_api/8.6.0/api-reference/zimbraAdmin/GetAccount.html
    """
        if not type(attrs) == list:
            raise TypeError('attrs must be a list')

        attrs = ','.join(attrs)
        self.cleanUp()

        self.request.add_request("GetAccountRequest", {
            "attrs": attrs,
            "account": {
                "_content": account,
                "by": "name",
            },
        }, "urn:zimbraAdmin")

        response = self.comm.send_request(self.request)

        if response.is_fault():
            raise ZimbraRequestError(
                "Reponse failed: (%s) %s" %
                (response.get_fault_code(), response.get_fault_message()))
        return (response.get_response())

    def createAccount(self, account, password=None, attrs=list()):
        """ Create a new account into Zimbra system
    :param account: The target account
    :param password: The given for the account
    :param attrs: A list of tuple containing the zimbra attribute with the corresponding value: [(zattr, value), ...]
    """
        request_attrs = []
        for attr in attrs:
            zattr, value = attr
            request_attrs.append({'n': zattr, '_content': value})

        self.cleanUp()

        if not password:
            password = hmac.new(str(uuid.uuid4), str(uuid.uuid4()),
                                hashlib.md5).hexdigest()

        self.request.add_request(request_name="CreateAccountRequest",
                                 request_dict={
                                     "name": account,
                                     "password": password,
                                     "a": request_attrs
                                 },
                                 namespace="urn:zimbraAdmin")

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error creating account %s. Error: %s' %
                (account, response.get_fault_message()),
                response=response)

        return response.get_response()

    def cleanUp(self):
        """ Clean up after one step to leave a dedicated result for the other
     test cases.
    """
        self.setUp()

    def setUp(self):
        """
    Setup everything required to make a request to zimbra server.
    """

        self.request = RequestJson()
        self.request = self.comm.gen_request(token=self.token)

    def getDomainId(self, domain):
        """
    Returns the zimbraId of a domain. Useful to modify a domain with ModifyDomainRequest for instance.
    
    domain_id = self.getDomainId(inova.net)
    equal:
    domain_id = '4af850c7-7e44-452e-ad25-c70fda58f9bf'
    """

        self.cleanUp()
        self.request.add_request("GetDomainInfoRequest", {
            "domain": {
                "_content": domain,
                "by": "name",
            },
        }, "urn:zimbraAdmin")

        response = self.comm.send_request(self.request)

        if response.is_fault():
            raise ZimbraRequestError(
                "Reponse failed: (%s) %s" %
                (response.get_fault_code(), response.get_fault_message()))

        return response.get_response()['GetDomainInfoResponse']['domain']['id']

    def getDomainQuotaUsage(self, domain):
        """
     Returns quota usage of all users of a specific domain
      {
      "GetQuotaUsageResponse": {
       "searchTotal": 1294,
       "account": [
        {
        "used": 0,
    "limit": 0,
    "name": "*****@*****.**",
    "id": "63b128d6-b7f2-466d-ac86-7b253e62a7ed"
     },
     {
    "used": 28,
    "limit": 26843545600,
    "name": "*****@*****.**",
    "id": "5b4832d1-b642-4778-ab7d-3056ebcefada"
     },
    .
    .
    .
      https://files.zimbra.com/docs/soap_api/8.6.0/api-reference/zimbraAdmin/GetQuotaUsage.html

    """
        self.cleanUp()

        self.request.add_request(
            "GetQuotaUsageRequest", {
                "domain": domain,
                "allServers": "1",
                "sortBy": "percentUsed",
                "sortAscending": "1",
            }, "urn:zimbraAdmin")

        response = self.comm.send_request(self.request)

        if response.is_fault():
            raise ZimbraRequestError(
                "Reponse failed: (%s) %s" %
                (response.get_fault_code(), response.get_fault_message()))

        return (response.get_response())

    def getCos(self, cos_name):
        """ Get COS by it's name
    :param cos_name: The name of the COS
    """
        self.cleanUp()
        self.request.add_request(
            request_name='GetCosRequest',
            request_dict={'cos': {
                'by': 'name',
                '_content': cos_name
            }},
            namespace='urn:zimbraAdmin')
        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error getting COS %s. Error: %s' %
                (cos_name, response.get_fault_message()),
                response=response)

        return response.get_response()

    def createCos(self, cos_name, features=dict()):
        """ Create a new cos.
    :param cos_name: The name of the COS
    :param features: A dict representing the feature->value 
    """
        if type(features) is not dict:
            raise TypeError('Wrong type found for features, must be a dict.')

        features_req = []
        for feature, value in features.items():
            features_req.append({'n': feature, '_content': value})

        self.cleanUp()
        self.request.add_request(request_name='CreateCosRequest',
                                 request_dict={
                                     'name': {
                                         '_content': cos_name
                                     },
                                     'a': features_req
                                 },
                                 namespace='urn:zimbraAdmin')

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error creating COS %s Error: %s' %
                (cos_name, response.get_fault_message()),
                response=response)

        return response.get_response()

    def modifyCos(self, zimbra_cos_id, features):
        """ Update a cos. 
    :param zimbra_cos_id: The zimbraID of the COS
    :param features: A dict representing the feature->value 
    """
        if type(features) is not dict:
            raise TypeError('Wrong type found for features, must be a dict')

        features_req = []
        for feature, value in features.items():
            features_req.append({'n': feature, '_content': value})

        self.cleanUp()
        self.request.add_request(request_name='ModifyCosRequest',
                                 request_dict={
                                     'id': {
                                         '_content': zimbra_cos_id
                                     },
                                     'a': features_req
                                 },
                                 namespace='urn:zimbraAdmin')

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error creating COS %s Error: %s' %
                (cos_name, response.get_fault_message()),
                response=response)

        return response.get_response()

    def deleteCos(self, zimbra_cos_id):
        """ Delete a specific COS
    :param zimbra_cos_id: The zimbraID of the COS
    """
        self.cleanUp()
        self.request.add_request(
            request_name='DeleteCosRequest',
            request_dict={'id': {
                '_content': zimbra_cos_id
            }},
            namespace='urn:zimbraAdmin')
        response = self.comm.send_request(self.request)

        if response.is_fault():
            raise ZimbraRequestError(
                message='Error creating COS %s Error: %s' %
                (cos_name, response.get_fault_message()),
                response=response)

        return response.get_response()

    def getComputeAggregateQuotaUsage(self):
        """ This method get all quota usage of all domains in a zimbra system. This may take a while depending how many domains and servers you have. Use wisely :P
    """

        self.cleanUp()

        self.request.add_request("ComputeAggregateQuotaUsageRequest", {},
                                 "urn:zimbraAdmin")

        response = self.comm.send_request(self.request)

        if response.is_fault():
            raise ZimbraRequestError(
                "Reponse failed: (%s) %s" %
                (response.get_fault_code(), response.get_fault_message()))

        return (response.get_response())

    def createDistributionList(self, dlist, attrs=list()):
        """ This method create zimbra distribution list. A list of attributes may be given, we will handle it for you.
    :param dlist: The target distribution list
    :param attrs: List of tuple attributes of distribution list
    """
        if not type(attrs) == list:
            raise TypeError('attrs must be a list')

        request_attrs = []
        for attr in attrs:
            zattr, value = attr
            # If it's a list, then it's a multi-value attribute
            if type(value) == list:
                for multi_attr in value:
                    request_attrs.append({'n': zattr, '_content': multi_attr})
            else:
                request_attrs.append({'n': zattr, '_content': value})
        self.cleanUp()

        self.request.add_request(request_name="CreateDistributionListRequest",
                                 request_dict={
                                     "name": dlist,
                                     "a": request_attrs
                                 },
                                 namespace="urn:zimbraAdmin")

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(message='Error creating DL %s Error: %s' %
                                     (dlist, response.get_fault_message()),
                                     response=response)

        return response.get_response()

    def getDistributionList(self, dlist):
        """ Gets information about a distribution list.
    :param dlist: The target distribution list
    Obs: Tested with "a" attribute does not have effect on result
    """
        self.cleanUp()
        self.request.add_request(request_name="GetDistributionListRequest",
                                 request_dict={
                                     "dl": {
                                         "_content": dlist,
                                         "by": "name",
                                     },
                                 },
                                 namespace="urn:zimbraAdmin")

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(message='Error getting DL: %s Error: %s' %
                                     (dlist, response.get_fault_message()),
                                     response=response)

        return response.get_response()

    def deleteDistributionList(self, dlist_zimbra_id):
        """ Deletes distribution list
    :param dlist_zimbra_id: Distribution List zimbraID
    """
        self.cleanUp()
        self.request.add_request(request_name="DeleteDistributionListRequest",
                                 request_dict={"id": dlist_zimbra_id},
                                 namespace="urn:zimbraAdmin")

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error deleting DL: %s Error: %s' %
                (dlist, response.get_fault_message()),
                response=response)
        return response.get_response()

    def addDistributionListMember(self, dlist_zimbra_id, members):
        """ This method adds members to a zimbra distribution list. A list of members must be sent.
    This method is idempotent, it will not change the result executing multiple times
    :param dlist_zimbra_id: The target distribution list zimbraId
    :param members: List containing the account members
    """
        if not type(members) == list:
            raise TypeError('members must be a list')

        zmembers = []
        for member in members:
            zmembers.append({'_content': member})
        self.cleanUp()

        self.request.add_request(
            request_name="AddDistributionListMemberRequest",
            request_dict={
                "id": dlist_zimbra_id,
                "dlm": zmembers
            },
            namespace="urn:zimbraAdmin")

        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error adding members to dlist %s Error: %s' %
                (dlist_zimbra_id, response.get_fault_message()),
                response=response)

        return response.get_response()

    def grantRight(self,
                   target_name,
                   target_type,
                   grantee_name,
                   grantee_type,
                   right,
                   deny=0):
        """ Grant a right on a target to an individual or group grantee.
    This method is idempotent, it will not change the result executing multiple times
    :param target_name: The target for applying the right (by name). E.g.: 'inova.net'. External docs: /target
    :param target_type: The type of the target. E.g.: 'domain'. External docs: /target@type
    :param grantee_name: Grantee selector. E.g.: 'grp', 'dlist'. External docs: /grantee
    :param grantee_type: The type of the grantee. E.g.: 'grp', 'dlist'. External docs: /grantee@type
    :param right: The name of the right. E.g.: getDomainQuotaUsage, domainAdminConsoleRights. External docs: /right
    :param deny: Either to deny or grant the permission. Default is 0. External docs: /right@deny
    
    Ref. Docs: https://files.zimbra.com/docs/soap_api/8.6.0/api-reference/zimbraAdmin/GrantRight.html    
    """
        self.cleanUp()
        self.request.add_request(request_name="GrantRightRequest",
                                 request_dict={
                                     "target": {
                                         "type": target_type,
                                         "by": "name",
                                         "_content": target_name
                                     },
                                     "grantee": {
                                         "type": grantee_type,
                                         "by": "name",
                                         "_content": grantee_name
                                     },
                                     "right": {
                                         "_content": right,
                                         "deny": deny
                                     }
                                 },
                                 namespace="urn:zimbraAdmin")
        response = self.comm.send_request(self.request)
        if response.is_fault():
            raise ZimbraRequestError(
                message='Error adding grant to target_name %s Error: %s' %
                (target_name, response.get_fault_message()),
                response=response)

        return response.get_response()