Exemple #1
0
    def test_fault_non_existing_folder_genrequest_invalidurl(self):
        """ Request a non existing folder, so we get a fitting fault
        """

        config = get_config()

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

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

            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 = comm.gen_request(token=token)

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

            # A 404 error should by raised as an exception.

            self.assertRaises(
                Exception,
                comm.send_request,
                request
            )
    def test_genrequest_default(self):
        """ Create a request only using the Communication-object
        """

        config = get_config()

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

            # Run only if enabled

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

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

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

            request = comm.gen_request(token=token)

            request.add_request("NoOpRequest", {}, "urn:zimbraMail")

            response = comm.send_request(request)

            if response.is_fault():

                self.fail(
                    "Reponse failed: (%s) %s" %
                    (response.get_fault_code(), response.get_fault_message()))
Exemple #3
0
def getDlData(name, url, token):
    comm = Communication(url)
    info_request = comm.gen_request(token=token)
    info_request.add_request('GetDistributionListRequest',
                             {'dl': {
                                 '_content': name,
                                 'by': 'name'
                             }}, 'urn:zimbraAdmin')
    info_response = comm.send_request(info_request)
    data = {}
    data['members'] = []
    if not info_response.is_fault():
        zimbra_data = info_response.get_response(
        )['GetDistributionListResponse']
        data['settings'] = dict.zimbra_to_python(zimbra_data['dl']['a'],
                                                 key_attribute='n',
                                                 content_attribute='_content')
        if 'dlm' in zimbra_data['dl']:
            for i in zimbra_data['dl']['dlm']:
                data['members'].append(i['_content'])

        data['id'] = zimbra_data['dl']['id']
        data['name'] = zimbra_data['dl']['name']
        return data
    else:
        print(info_response.get_fault_message())
        sys.exit(1)
Exemple #4
0
    def test_fault_non_existing_folder_genrequest_xml(self):
        """ Request a non existing folder, so we get a fitting fault
        """

        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 = comm.gen_request(request_type="xml", token=token)

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

            response = comm.send_request(request)

            self.check_response(
                response
            )
Exemple #5
0
    def __init__(self, server_host, server_port, *args, **kwargs):
        loc = 'https://%s:%s/%s' % (server_host, server_port, self.LOCATION)
        self.com = Communication(loc)
        self._server_host = server_host
        self._server_port = server_port

        self._session = ZimbraAPISession(self)
Exemple #6
0
    def request(self, name, data, urn):

        comm = Communication(self.url)
        account_request = RequestXml()
        account_request.set_auth_token(self.getToken())
        account_request.add_request(name, data, urn)
        account_response = ResponseXml()
        comm.send_request(account_request, account_response)
        return account_response.get_response()
Exemple #7
0
class ZimbraAbstractClient(object):
    """ Factorized abstract code for SOAP API access.

    Provides common ground for zimbraAdmin and zimbraAccount.
    """
    def __init__(self, server_host, server_port, *args, **kwargs):
        loc = 'https://%s:%s/%s' % (server_host, server_port, self.LOCATION)
        self.com = Communication(loc)
        self._server_host = server_host
        self._server_port = server_port

        self._session = ZimbraAPISession(self)

    def request(self, name, content={}, namespace=None):
        """ Do a SOAP request and returns the result.

        Simple wrapper arround pythonzimbra functions
        :param name: ex: 'Auth' for performing an 'AuthRequest'
        :param content: a dict formatted pythonzimbra-style for request
        :param namespace: (optional), the namespace, if different from the
                          client's

        :returns: a dict with response
        """
        if not namespace:
            namespace = self.NAMESPACE

        req_name = name+'Request'
        resp_name = name+'Response'
        req = auth_request = pythonzimbra.request_xml.RequestXml()
        resp = pythonzimbra.response_xml.ResponseXml()

        if self._session.is_logged_in():
            req.set_auth_token(self._session.authToken)

        req.add_request(req_name, content, namespace)
        try:
            self.com.send_request(req, resp)
        except urllib2.HTTPError, e:
            if resp :
                raise ZimbraSoapServerError(e.req, e.resp)
            else:
                raise

        try:
            resp_content = resp.get_response()
            return resp_content[resp_name]
        except KeyError:
            if resp_content.has_key('Fault'):
                raise ZimbraSoapServerError(req, resp)
            raise ZimbraSoapUnexpectedResponse(
                req, resp, 'Cannot find {} in response "{}"'.format(
                    resp_name, resp.get_response()))

        return resp_content
 def run_request(self, creds, req, req_args={}):
     token = self.get_token(
         creds['url'], creds['user'], creds['pwd'], creds['admin_auth'])
     comm = Communication(creds['url'])
     zmreq = comm.gen_request(token=token, request_type="xml")
     zmreq.add_request(req, req_args, creds['urn'])
     resp = comm.send_request(zmreq)
     if resp.is_fault():
         print(resp.get_fault_code())
     ret = resp.get_response()
     return json.loads(ret['response']['content'])
Exemple #9
0
 def connect(self):
     url = os.environ['ZIMBRA_URL']
     account = os.environ['ZIMBRA_ACCOUNT']
     password = os.environ['ZIMBRA_PASSWORD']
     try:
         self.comm = Communication(url)
         self.token = auth.authenticate(url, account, password, admin_auth=True)
     except:
         log.exception("locals=%s", locals())
     if not self.token:
         raise Exception("Authentication failed: locals=%s", locals())
Exemple #10
0
    def connect(self):
        try:
            self.comm = Communication(self.url)

            self.__token = auth.authenticate(self.url, self.usr, self.__password, admin_auth=True)
            self.request = self.comm.gen_request(token=self.__token, set_batch=True)

            return 'Conectado'

        except urllib.error.URLError as E:
        # catastrophic error. bail.
            return f'Erro na conexão: {E}'
    def test_autoresponse_xml(self):

        """ Create an XML-request and pass this to send_request expection a
        xml 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 = RequestXml()
            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,
                "xml",
                "Invalid response type %s" % response.response_type
            )
    def test_genrequest_xml(self):

        """ Create a request only using the Communication-object
        """

        config = get_config()

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

            # Run only if enabled

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

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

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

            request = comm.gen_request(request_type="xml", token=token)

            request.add_request(
                "NoOpRequest",
                {

                },
                "urn:zimbraMail"
            )

            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,
                "xml",
                "Invalid response type %s" % response.response_type
            )
Exemple #13
0
    def test_genrequest_fail(self):

        """ Create a request only using the Communication-object
        """

        config = get_config()

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

            # Run only if enabled

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

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

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

            self.assertRaises(
                UnknownRequestType,
                comm.gen_request,
                request_type="INVALID",
                token=token
            )

            request = comm.gen_request(token=token)

            request.add_request(
                "NoOpRequest",
                {

                },
                "urn:zimbraMail"
            )

            # Deliberately break the request

            request.request_type = "INVALID"

            self.assertRaises(
                UnknownRequestType,
                comm.send_request,
                request
            )
Exemple #14
0
    def test_genrequest_check_response_xml(self):

        """ Create a request only using the Communication-object, send it and
            check the response
        """

        config = get_config()

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

            # Run only if enabled

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

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

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

            request = comm.gen_request(request_type="xml", token=token)

            request.add_request(
                "GetInfoRequest",
                {
                },
                "urn:zimbraAccount"
            )

            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.get_response()["GetInfoResponse"]["name"],
                config.get("genrequest_test", "account"),
                "Request returned unexpected response"
            )
Exemple #15
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
            )
    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)
Exemple #17
0
    def __init__(self, server_host, server_port, *args, **kwargs):
        loc = 'https://%s:%s/%s' % (server_host, server_port, self.LOCATION)
        self.com = Communication(loc)
        self._server_host = server_host
        self._server_port = server_port

        self._session = ZimbraAPISession(self)
Exemple #18
0
def delMembers(dlId, member, url, token):
    if options.verbose:
        print('deleting member: ', member)
    comm = Communication(url)
    info_request = comm.gen_request(token=token)
    info_request.add_request('RemoveDistributionListMemberRequest', {
        'id': dlId,
        'dlm': {
            '_content': member
        }
    }, 'urn:zimbraAdmin')
    info_response = comm.send_request(info_request)
    if not info_response.is_fault():
        zimbra_data = info_response.get_response(
        )['RemoveDistributionListMemberResponse']
    else:
        print(info_response.get_fault_message())
        sys.exit(1)
    def __init__( self , cfg ):
        """
        Initialise l'accès à l'API en lisant les paramètres de connexion dans la
        configuration et en créant l'instance de communication.
        """
        self.url_ = cfg.get( 'bss' , 'zimbra-url' ,
                'https://webmail.partage.renater.fr/service/soap' )
        self.domain_ = cfg.get( 'bss' , 'domain' )
        self.dkey_ = cfg.get( 'bss' , 'token' )
        self.fake_it_ = cfg.has_flag( 'bss' , 'simulate' )
        self.user_ = None
        self.token_ = None

        # On lit le timeout depuis la configuration, s'il est défini. Sinon,
        # on utilise 10s par défaut.
        timeout_cfg = cfg.get( 'bss' , 'zimbra-timeout' , '10' )
        try:
            timeout_cfg = int( timeout_cfg )
        except ValueError as e:
            Logging( 'zimbra' ).error( 'Paramètre zimbra-timeout invalide' )
            raise FatalError( 'Erreur de configuration' , e )
        from pythonzimbra.communication import Communication
        self.comm_ = Communication( self.url_ , timeout = timeout_cfg )
  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)
Exemple #21
0
class ZimbraAbstractClient(object):
    """ Factorized abstract code for SOAP API access.

    Provides common ground for zimbraAdmin, zimbraAccount and zimbraMail.
    """
    def __init__(self, server_host, server_port, *args, **kwargs):
        loc = 'https://%s:%s/%s' % (server_host, server_port, self.LOCATION)
        self.com = Communication(loc)
        self._server_host = server_host
        self._server_port = server_port

        self._session = ZimbraAPISession(self)

    def request(self, name, content={}, namespace=None):
        """ Do a SOAP request and returns the result.

        Simple wrapper arround pythonzimbra functions
        :param name: ex: 'Auth' for performing an 'AuthRequest'
        :param content: a dict formatted pythonzimbra-style for request
        :param namespace: (optional), the namespace, if different from the
                          client's

        :returns: a dict with response
        """
        if not namespace:
            namespace = self.NAMESPACE

        req_name = name+'Request'
        resp_name = name+'Response'
        req = pythonzimbra.request_xml.RequestXml()
        resp = pythonzimbra.response_xml.ResponseXml()

        if self._session.is_logged_in():
            req.set_auth_token(self._session.authToken)

        req.add_request(req_name, content, namespace)
        try:
            self.com.send_request(req, resp)
        except HTTPError as e:
            if resp:
                raise ZimbraSoapServerError(e.req, e.resp)
            else:
                raise

        try:
            resp_content = resp.get_response()
            return resp_content[resp_name]
        except KeyError:
            if 'Fault' in resp_content:
                raise ZimbraSoapServerError(req, resp)
            raise ZimbraSoapUnexpectedResponse(
                req, resp, 'Cannot find {} in response "{}"'.format(
                    resp_name, resp.get_response()))

        return resp_content

    def request_single(self, name, content={}):
        """ Simple wrapper arround request to extract a single response

        :returns: the first tag in the response body
        """
        resp = self.request(name, content)

        # We stop on the first non-attribute (attributes are unicode/str)
        # If it's a list, we only return the first one.

        for i in resp.values():
            if type(i) == list:
                return i[0]
            elif type(i) == dict:
                return i

        return None

    def request_list(self, name, content={}):
        """ Simple wrapper arround request to extract a list of response

        :returns: the list of tags with same name or empty list
        """
        resp = self.request(name, content)

        # We stop on the first non-attribute (attributes are unicode/str)
        # If it's a list, we only return the first one.

        for i in resp.values():
            if type(i) == list:
                return i
            elif type(i) == dict:
                return [i]

        return []

    def login(self, user, password):
        self._session.login(user, password)

    def login_with_authToken(self, authToken, lifetime=None):
        self._session.import_session(authToken)
        if lifetime:
            self._session.set_end_date(int(lifetime))

    def get_logged_in_by(self, login, parent_zc, duration=0):
        """Use another client to get logged in via preauth mechanism by an
        already logged in admin.

        It required the domain of the admin user to have preAuthKey
        The preauth key cannot be created by API, do it with zmprov :
            zmprov gdpak <domain>
        """
        domain_name = zobjects.Account(name=login).get_domain()
        preauth_key = parent_zc.get_domain(domain_name)['zimbraPreAuthKey']

        rc = self.REST_PREAUTH(
            self._server_host, parent_zc._server_port, preauth_key=preauth_key)

        authToken = rc.get_preauth_token(login)

        self.login_with_authToken(authToken)

    def delegated_login(self, login, admin_zc, duration=0):
        """Use another client to get logged in via delegated_auth mechanism by an
        already logged in admin.

        :param login: the user login (or email) you want to log as
        :param admin_zc: An already logged-in admin client
        :type admin_zc: ZimbraAdminClient
        """
        # a duration of zero is interpretted literaly by the API...
        selector = zobjects.Account(name=login).to_selector()
        delegate_args = {'account': selector}
        if duration:
            delegate_args['duration': duration]
        resp = admin_zc.request('DelegateAuth', delegate_args)

        lifetime = resp['lifetime']
        authToken = resp['authToken']

        self.login_account = login
        self.login_with_authToken(authToken, lifetime)

    def is_session_valid(self):
        # some classes may need to overload it
        return self._session.is_session_valid()

    def get_host(self):
        return self._server_host
    # Calculate point in times for appointment expansion

    expand_start = datetime.datetime.now() - datetime.timedelta(options.start)
    expand_end = datetime.datetime.now() + datetime.timedelta(options.end)

    # Calculate the epoch-timestamps for Zimbra (in milliseconds)

    expand_start_epoch = calendar.timegm(expand_start.utctimetuple()) * 1000
    expand_end_epoch = calendar.timegm(expand_end.utctimetuple()) * 1000

    # Build up Zimbra communication

    url = "https://%s:7071/service/admin/soap" % server_name
    user_url = "https://%s/service/soap" % server_name

    comm = Communication(url)

    user_comm = Communication(user_url)

    token = auth.authenticate(url, admin_account, admin_password, admin_auth=True)

    if token is None:

        logging.error("Cannot login into zimbra with the supplied credentials.")

        exit(1)

    # Search for groupcal dictionaries

    search_request = comm.gen_request(token=token)
Exemple #23
0
def authenticate(url,
                 account,
                 key,
                 by='name',
                 expires=0,
                 timestamp=None,
                 timeout=None,
                 request_type="xml",
                 admin_auth=False,
                 use_password=False,
                 raise_on_error=False):
    """ Authenticate to the Zimbra server

    :param url: URL of Zimbra SOAP service
    :param account: The account to be authenticated against
    :param key: The preauth key of the domain of the account or a password (if
      admin_auth or use_password is True)
    :param by: If the account is specified as a name, an ID or a
      ForeignPrincipal
    :param expires: When the token expires (or 0 for default expiration)
    :param timestamp: When the token was requested (None for "now")
    :param timeout: Timeout for the communication with the server. Defaults
      to the urllib2-default
    :param request_type: Which type of request to use ("xml" (default) or
      "json")
    :param admin_auth: This request should authenticate and generate an admin
      token. The "key"-parameter therefore holds the admin password (implies
      use_password)
    :param use_password: The "key"-parameter holds a password. Do a password-
      based user authentication.
    :param raise_on_error: Should I raise an exception when an authentication
      error occurs or just return None?
    :return: The authentication token or None
    :rtype: str or None or unicode
    """

    if timestamp is None:
        timestamp = int(time.time()) * 1000

    pak = ""
    if not admin_auth:
        pak = preauth.create_preauth(account, key, by, expires, timestamp)

    if request_type == 'xml':

        auth_request = RequestXml()

    else:

        auth_request = RequestJson()

    request_data = {'account': {'by': by, '_content': account}}

    ns = "urn:zimbraAccount"

    if admin_auth:

        ns = "urn:zimbraAdmin"

        request_data['password'] = key

    elif use_password:

        request_data['password'] = {"_content": key}

    else:

        request_data['preauth'] = {
            'timestamp': timestamp,
            'expires': expires,
            '_content': pak
        }

    auth_request.add_request('AuthRequest', request_data, ns)

    server = Communication(url, timeout)

    if request_type == 'xml':

        response = ResponseXml()

    else:

        response = ResponseJson()

    server.send_request(auth_request, response)

    if response.is_fault():

        if raise_on_error:

            raise AuthenticationFailed(
                "Cannot authenticate user: (%s) %s" %
                (response.get_fault_code(), response.get_fault_message()))

        return None

    return response.get_response()['AuthResponse']['authToken']['_content']
Exemple #24
0
                    help='webmail URL',
                    required=True)
parser.add_argument('-S',
                    '--send-mail',
                    dest='send_mail',
                    nargs='+',
                    metavar=('email', 'smtp-server'),
                    help='Add this if you want to send output by email')

argslist = parser.parse_args()

url = "https://" + argslist.hostname + ":7071/service/admin/soap"
admin = argslist.admin
password = argslist.password
domain = argslist.domain
comm = Communication(url)
sendm = argslist.send_mail


def multipleReplace(text, wordDict):
    for key in wordDict:
        text = text.replace(key, wordDict[key])
    return text


def zimbraAttributes():
    return [
        'displayName', 'description', 'zimbraAccountStatus', 'zimbraCOSId',
        'zimbraLastLogonTimestamp'
    ]
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()
Exemple #26
0
    def test_genrequest_check_response_batch_xml(self):

        """ Create a batch-request only using the Communication-object
        """

        config = get_config()

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

            # Run only if enabled

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

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

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

            request = comm.gen_request(
                request_type="xml",
                token=token,
                set_batch=True
            )

            self.assertEqual(
                type(request),
                RequestXml,
                "Generated request wasn't an json-request, which should be "
                "the default."
            )

            request.add_request(
                "NoOpRequest",
                {

                },
                "urn:zimbraMail"
            )

            request.add_request(
                "GetInfoRequest",
                {
                },
                "urn:zimbraAccount"
            )

            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.get_response(2)["GetInfoResponse"]["name"],
                config.get("genrequest_test", "account"),
                "Request returned unexpected response"
            )
    def run_admin_test(self, request_type):

        """ Actually do the work
        """

        config = get_config()

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

            # Run only if enabled

            token = authenticate(
                config.get("admin_request_test", "admin_url"),
                config.get("admin_request_test", "admin_account"),
                config.get("admin_request_test", "admin_password"),
                config.get("admin_request_test", "admin_account_by"),
                admin_auth=True,
                request_type=request_type
            )

            if token is None:

                self.fail("Authentication with the configured settings "
                          "was not successful")

            # Create an account

            comm = Communication(config.get("admin_request_test", "admin_url"))

            if request_type == "xml":

                request = RequestXml()

            else:

                request = RequestJson()

            request.set_auth_token(token)

            test_account = config.get("admin_request_test", "test_account")

            if "TEMP" in test_account:

                # Generate a random number and add it to the test account

                random.seed()
                temp_account = random.randint(1000000, 5000000)

                test_account = test_account.replace("TEMP", str(temp_account))

            test_displayname = config.get(
                "admin_request_test",
                "test_displayname"
            )

            if sys.version < '3':

                # Create unicode string for py2

                test_displayname = test_displayname.decode("utf-8")

            request.add_request(
                "CreateAccountRequest",
                {
                    "name": test_account,
                    "password": config.get(
                        "admin_request_test",
                        "test_password"
                    ),
                    "a": {
                        "n": "displayName",
                        "_content": test_displayname
                    }
                },
                "urn:zimbraAdmin"
            )

            if request_type == "xml":

                response = ResponseXml()

            else:

                response = ResponseJson()

            comm.send_request(request, response)

            if response.is_fault():

                self.fail(
                    "CreateAccount faulted. (%s) %s" % (
                        response.get_fault_code(),
                        response.get_fault_message()
                    )
                )

            account_id = response.get_response(
            )["CreateAccountResponse"]["account"]["id"]

            # Get account from database and compare display name to the setting

            request.clean()
            request.set_auth_token(token)
            response.clean()

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

            comm.send_request(request, response)

            if response.is_fault():

                self.fail(
                    "GetAccount faulted. (%s) %s" % (
                        response.get_fault_code(),
                        response.get_fault_message()
                    )
                )

            returned_name = get_value(
                response.get_response()["GetAccountResponse"]["account"]["a"],
                "displayName"
            )

            self.assertEqual(
                returned_name,
                test_displayname,
                "Zimbra didn't save the display name as requested."
            )

            # Try to log in as the new account

            user_token = authenticate(
                config.get("admin_request_test", "url"),
                test_account,
                config.get("admin_request_test", "test_password"),
                "name",
                request_type=request_type,
                use_password=True
            )

            if user_token is None:

                self.fail("Cannot log in as the test user.")

            # Remove account

            request.clean()
            response.clean()
            request.set_auth_token(token)

            request.add_request(
                "DeleteAccountRequest",
                {
                    "id": account_id
                },
                "urn:zimbraAdmin"
            )

            comm.send_request(request, response)

            if response.is_fault():

                self.fail(
                    "Cannot remove test account: (%s) %s" % (
                        response.get_fault_code(),
                        response.get_fault_message()
                    )
                )
Exemple #28
0
class Zimbra():
    def __init__(self):
        self.comm = None
        self.token = None

    def connect(self):
        url = os.environ['ZIMBRA_URL']
        account = os.environ['ZIMBRA_ACCOUNT']
        password = os.environ['ZIMBRA_PASSWORD']
        try:
            self.comm = Communication(url)
            self.token = auth.authenticate(url, account, password, admin_auth=True)
        except:
            log.exception("locals=%s", locals())
        if not self.token:
            raise Exception("Authentication failed: locals=%s", locals())

    def request(self, request_name, params=None, context=None, urn=None, opts={}):
        """ Send a single request to Zimbra; return the response as native python data. """
        if 'xml' in opts and opts['xml']:
            request, response = RequestXml(), ResponseXml()
        else:
            request, response = RequestJson(), ResponseJson()

        try:
            if urn == None:
                urn = urn_for_request(request_name)
            request.add_request(request_name, params or {}, "urn:"+urn)
            if context:
                request.set_context_params(context)
            request.set_auth_token(self.token)
        except:
            log.exception("failed to build request: request_name=%s, params=%s, context=%s",
                          request_name, params, context)
            return None
        if opts['debug']:
            if isinstance(request, RequestXml):
                print xml.dom.minidom.parseString(request.get_request()).toprettyxml(indent=".   ")
            else:
                pprint.pprint(yaml.load(request.get_request()))

        try:
            self.comm.send_request(request, response)
        except urllib2.HTTPError as e:
            requestbody = request.get_request()
            responsebody = e.read()
            log.error('''send_request HTTP Error %s: %s; request="%s" response="%s"''',
                          e.code, e.reason, requestbody, responsebody)
            if isinstance(request, RequestXml):
                requestdoc = xml.dom.minidom.parseString(requestbody)
                print "REQUEST=", requestdoc.toprettyxml(indent=".   ")
            else:
                print "REQUEST=\n", pprint.pformat(yaml.load(requestbody))
            if isinstance(response, ResponseXml):
                responsedoc = xml.dom.minidom.parseString(responsebody)
                print "RESPONSE=", responsedoc.toprettyxml(indent=".   ")
            else:
                print "RESPONSE=\n", pprint.pformat(yaml.load(responsebody))
            return None
        except Exception as e:
            log.exception("send_request failed (%s): request=%s", type(e), request.get_request())
            return None
        if response.is_fault():
            log.error("send_request returned fault: request=%s, response=%s",
                      request.get_request(), response.get_response())
            return None
        info = response.get_response()
        return info
Exemple #29
0
    def _get_devices(self, user):

        self.app.log.debug("Fetching devices of user %s" % user)

        (local_part, domain_part) = user.split("@")

        if domain_part not in self.preauth_cache:

            # No preauth key cached. Fetch one

            self.app.log.debug("Fetch preauth key for domain %s" % domain_part)

            comm = Communication(self.url)

            preauthkey_request = comm.gen_request(token=self.token)

            preauthkey_request.add_request(
                "GetDomainRequest",
                {
                    "domain": {
                        "by": "name",
                        "_content": domain_part
                    }
                },
                "urn:zimbraAdmin"
            )

            preauthkey_response = comm.send_request(preauthkey_request)

            if preauthkey_response.is_fault():

                self.app.log.fatal(
                    "Cannot fetch preauth key for domain %s" % domain_part,
                    preauthkey_response.get_fault_code(),
                    preauthkey_response.get_fault_message(),
                )

                exit(1)

            preauth = get_value(
                preauthkey_response.get_response()["GetDomainResponse"][
                    "domain"]["a"],
                "zimbraPreAuthKey"
            )

            if preauth is None:

                self.app.log.fatal(
                    "Domain %s has no preauthkey. Please use zmprov gdpak "
                    "<domain> first." % domain_part
                )

                exit(1)

            self.preauth_cache[domain_part] = preauth

        else:

            preauth = self.preauth_cache[domain_part]

        user_token = auth.authenticate(
            self.user_url,
            user,
            preauth
        )

        if user_token is None:

            self.app.log.fatal("Cannot login as user %s" % user)

            exit(1)

        user_comm = Communication(self.user_url)

        get_device_status_request = user_comm.gen_request(token=user_token)

        get_device_status_request.add_request(
            "GetDeviceStatusRequest",
            {},
            "urn:zimbraSync"
        )

        get_device_status_response = user_comm.send_request(
            get_device_status_request)

        if get_device_status_response.is_fault():

            self.app.log.fatal(
                "Cannot fetch devices for user %s: (%s) %s" % (
                    user,
                    get_device_status_response.get_fault_code(),
                    get_device_status_response.get_fault_message()
                )
            )

            exit(1)

        devices = []

        if "device" in get_device_status_response.get_response()[
                "GetDeviceStatusResponse"]:

            devices = get_device_status_response.get_response()[
                "GetDeviceStatusResponse"]["device"]

        if type(devices) == dict:

            devices = [devices]

        return devices
Exemple #30
0
    def _get_scope(self):

        """ Build up a list of users for the specified scope
        """

        self.app.log.debug("Building up user list of scope definition")

        if self.app.pargs.scope == "":

            # Set the scope to the admin user's domain

            (local_part, domain_part) = self.app.pargs.user.split("@")

            scope_config = "DOMAIN=%s" % domain_part

        else:

            scope_config = self.app.pargs.scope

        (scope, scope_value) = scope_config.split("=")

        if scope not in ("DOMAIN", "LIST", "USER"):

            self.app.log.fatal("Scope not correctly configured. Please use "
                               "DOMAIN, LIST or USER")
            exit(1)

        userlist = []

        if scope == "DOMAIN":

            # Fetch all users in a domain

            self.app.log.debug(
                "Searching for accounts in domain %s" % scope_value
            )

            comm = Communication(self.url)
            search_account_request = comm.gen_request(token=self.token)

            search_account_request.add_request(
                "SearchAccountsRequest",
                {
                    "query": "",
                    "domain": scope_value
                },
                "urn:zimbraAdmin"
            )

            search_account_response = comm.send_request(search_account_request)

            if search_account_response.is_fault():

                self.app.log.fatal(
                    "Cannot search for accounts in the specified domain %s: "
                    "(%s) %s" % (
                        scope_value,
                        search_account_response.get_fault_code(),
                        search_account_response.get_fault_message()
                    )
                )

            for account in search_account_response.get_response()[
                    "SearchAccountsResponse"]["account"]:

                userlist.append(account["name"])

        elif scope == "LIST":

            # Fetch all users in a distribution list

            self.app.log.debug(
                "Searching for users in distribution list %s" % scope_value
            )

            comm = Communication(self.url)
            get_distributionlist_request = comm.gen_request(token=self.token)

            get_distributionlist_request.add_request(
                "GetDistributionListRequest",
                {
                    "dl": {
                        "by": "name",
                        "_content": scope_value
                    }
                },
                "urn:zimbraAdmin"
            )

            get_distributionlist_response = comm.send_request(
                get_distributionlist_request
            )

            if get_distributionlist_response.is_fault():

                self.app.log.fatal(
                    "Cannot search for accounts in the specified list %s: "
                    "(%s) %s" % (
                        scope_value,
                        get_distributionlist_response.get_fault_code(),
                        get_distributionlist_response.get_fault_message()
                    )
                )

            for member in get_distributionlist_response.get_response()[
                    "GetDistributionListResponse"]["dl"]["dlm"]:

                if type(member) == dict:

                    userlist.append(member["_content"])

                else:

                    userlist.append(member)

        elif scope == "USER":

            # Just a single user

            userlist.append(scope_value)

        self.app.log.debug("Found these users:\n %s" % userlist)

        return userlist
def authenticate(url, account, key, by='name', expires=0, timestamp=None,
                 timeout=None, request_type="xml", admin_auth=False,
                 use_password=False, raise_on_error=False):

    """ Authenticate to the Zimbra server

    :param url: URL of Zimbra SOAP service
    :param account: The account to be authenticated against
    :param key: The preauth key of the domain of the account or a password (if
      admin_auth or use_password is True)
    :param by: If the account is specified as a name, an ID or a
      ForeignPrincipal
    :param expires: When the token expires (or 0 for default expiration)
    :param timestamp: When the token was requested (None for "now")
    :param timeout: Timeout for the communication with the server. Defaults
      to the urllib2-default
    :param request_type: Which type of request to use ("xml" (default) or
      "json")
    :param admin_auth: This request should authenticate and generate an admin
      token. The "key"-parameter therefore holds the admin password (implies
      use_password)
    :param use_password: The "key"-parameter holds a password. Do a password-
      based user authentication.
    :param raise_on_error: Should I raise an exception when an authentication
      error occurs or just return None?
    :return: The authentication token or None
    :rtype: str or None or unicode
    """

    if timestamp is None:
        timestamp = int(time.time()) * 1000

    pak = ""
    if not admin_auth:
        pak = preauth.create_preauth(account, key, by, expires, timestamp)

    if request_type == 'xml':

        auth_request = RequestXml()

    else:

        auth_request = RequestJson()

    request_data = {
        'account': {
            'by': by,
            '_content': account
        }
    }

    ns = "urn:zimbraAccount"

    if admin_auth:

        ns = "urn:zimbraAdmin"

        request_data['password'] = key

    elif use_password:

        request_data['password'] = {
            "_content": key
        }

    else:

        request_data['preauth'] = {
            'timestamp': timestamp,
            'expires': expires,
            '_content': pak
        }

    auth_request.add_request(
        'AuthRequest',
        request_data,
        ns
    )

    server = Communication(url, timeout)

    if request_type == 'xml':

        response = ResponseXml()

    else:

        response = ResponseJson()

    server.send_request(auth_request, response)

    if response.is_fault():

        if raise_on_error:

            raise AuthenticationFailed(
                "Cannot authenticate user: (%s) %s" % (
                    response.get_fault_code(),
                    response.get_fault_message()
                )
            )

        return None

    return response.get_response()['AuthResponse']['authToken']
Exemple #32
0
    def test_genrequest_batch_invalid_xml(self):

        """ Create a batchrequest only using the Communication-object,
            send it and request an invalid request id (xml)
        """

        config = get_config()

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

            # Run only if enabled

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

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

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

            request = comm.gen_request(
                request_type="xml",
                token=token,
                set_batch=True
            )

            self.assertEqual(
                type(request),
                RequestXml,
                "Generated request wasn't an json-request, which should be "
                "the default."
            )

            request.add_request(
                "NoOpRequest",
                {

                },
                "urn:zimbraMail"
            )

            request.add_request(
                "NoOpRequest",
                {

                },
                "urn:zimbraMail"
            )

            response = comm.send_request(request)

            if response.is_fault():

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

            self.assertIsNone(
                response.get_response(3),
                "Querying an invalid requestId didn't return None"
            )
    if options.quiet:
        logging.basicConfig(level=logging.ERROR)
    elif options.debug:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.INFO)

    logging.debug("Starting followup-agent.")

    logging.debug("Authenticating as administrator to get users and domain "
                  "preauth")

    server_url = "https://%s:7071/service/admin/soap" % server_name

    comm = Communication(server_url)

    token = auth.authenticate(server_url,
                              admin_account, admin_password, admin_auth=True)

    users = []

    if options.distlist:

        logging.debug("Retrieving distribution list members from list %s" % (
            options.distlist
        ))

        get_members_request = comm.gen_request(token=token)
        get_members_request.add_request(
            "GetDistributionListRequest",
Exemple #34
0
    def run_admin_test(self, request_type):

        """ Actually do the work
        """

        config = get_config()

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

            # Run only if enabled

            token = authenticate(
                config.get("admin_request_test", "url"),
                config.get("admin_request_test", "account"),
                config.get("admin_request_test", "password"),
                config.get("admin_request_test", "account_by"),
                admin_auth=True,
                request_type=request_type
            )

            if token is None:

                self.fail("Authentication with the configured settings "
                          "was not successful")

            # Create an account

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

            if request_type == "xml":

                request = RequestXml()

            else:

                request = RequestJson()

            request.set_auth_token(token)

            request.add_request(
                "CreateAccountRequest",
                {
                    "name": config.get("admin_request_test", "test_account"),
                    "password": config.get(
                        "admin_request_test",
                        "test_password"
                    )
                },
                "urn:zimbraAdmin"
            )

            if request_type == "xml":

                response = ResponseXml()

            else:

                response = ResponseJson()

            comm.send_request(request, response)

            if response.is_fault():

                self.fail(
                    "CreateAccount faulted. %s" % (response.get_response())
                )

            account_id = response.get_response(
            )["CreateAccountResponse"]["account"]["id"]

            # Try to log in as the new account

            user_token = authenticate(
                config.get("admin_request_test", "user_url"),
                config.get("admin_request_test", "test_account"),
                config.get("admin_request_test", "test_password"),
                "name",
                request_type=request_type,
                use_password=True
            )

            if user_token is None:

                self.fail("Cannot log in as the test user.")

            # Remove account

            request.clean()
            response.clean()
            request.set_auth_token(token)

            request.add_request(
                "DeleteAccountRequest",
                {
                    "id": account_id
                },
                "urn:zimbraAdmin"
            )

            comm.send_request(request, response)

            if response.is_fault():

                self.fail(
                    "Cannot remove test account. %s" % response.get_response()
                )
 def get_com(self):
     if self.comm is None:
         self.comm = Communication(self.url)
     return self.comm
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()
class Zimbra:
    """
    Classe utilitaire pour l'accès à l'API de Zimbra.
    """

    def __init__( self , cfg ):
        """
        Initialise l'accès à l'API en lisant les paramètres de connexion dans la
        configuration et en créant l'instance de communication.
        """
        self.url_ = cfg.get( 'bss' , 'zimbra-url' ,
                'https://webmail.partage.renater.fr/service/soap' )
        self.domain_ = cfg.get( 'bss' , 'domain' )
        self.dkey_ = cfg.get( 'bss' , 'token' )
        self.fake_it_ = cfg.has_flag( 'bss' , 'simulate' )
        self.user_ = None
        self.token_ = None

        # On lit le timeout depuis la configuration, s'il est défini. Sinon,
        # on utilise 10s par défaut.
        timeout_cfg = cfg.get( 'bss' , 'zimbra-timeout' , '10' )
        try:
            timeout_cfg = int( timeout_cfg )
        except ValueError as e:
            Logging( 'zimbra' ).error( 'Paramètre zimbra-timeout invalide' )
            raise FatalError( 'Erreur de configuration' , e )
        from pythonzimbra.communication import Communication
        self.comm_ = Communication( self.url_ , timeout = timeout_cfg )

    def terminate( self ):
        """
        Termine la session actuelle, s'il y en a une, en envoyant une requête
        'EndSession'.
        """
        if self.user_ is None:
            return
        Logging( 'zimbra' ).debug( 'Déconnexion de {}'.format( self.user_ ) )
        try:
            self.send_request_( 'Account' , 'EndSession' )
        finally:
            self.user_ = None
            self.token_ = None

    def set_user( self , user_name ):
        """
        Tente de se connecter à l'API Zimbra avec un utilisateur donné. Si le
        nom d'utilisateur n'inclut pas un domaine, il sera rajouté.

        :param str user_name: le nom d'utilisateur

        :raises ZimbraError: une erreur de communication s'est produite
        """
        if '@' not in user_name:
            user_name = '{}@{}'.format( user_name , self.domain_ )

        if self.user_ is not None and self.user_ == user_name:
            return
        self.terminate( ) # ...Yes dear, you can self-terminate.

        from pythonzimbra.tools import auth
        from pythonzimbra.exceptions.auth import AuthenticationFailed
        Logging( 'zimbra' ).debug( 'Connexion pour {}'.format( user_name ) )
        try:
            ttok = auth.authenticate(
                    self.url_ , user_name ,
                    self.dkey_ , raise_on_error = True )
        except AuthenticationFailed as e:
            Logging( 'zimbra' ).error(
                    'Échec de la connexion: {}'.format( str( e ) ) )
            raise ZimbraConnectionError( e ) #FIXME
        Logging( 'zimbra' ).debug( 'Connexion réussie; jeton: {}'.format(
                ttok ) )

        assert ttok is not None
        self.user_ = user_name
        self.token_ = ttok

    def get_folder( self , path = '/' , uuid = None , recursive = True ):
        """
        Lit les informations d'un dossier (au sens Zimbra du terme) et
        éventuellement de ses sous-dossiers.

        :param str path: le chemin absolu du dossier sur lequel on veut \
                des informations; sera ignoré si un UUID est spécifié
        :param str uuid: l'UUID du dossier sur lequel on veut des informations
        :param bool recursive: les informations des sous-dossiers seront \
                également lues si ce paramètre est True

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        Logging( 'zimbra' ).debug( 'Récupération{} du dossier {}'.format(
                ' récursive' if recursive else '' , path ) )
        folder_spec = dict( )
        if uuid is None:
            folder_spec[ 'path' ] = path
        else:
            folder_spec[ 'uuid' ] = uuid
        ls = self.send_request_( 'Mail' , 'GetFolder' , {
                'folder' : folder_spec ,
                'depth' : -1 if recursive else 0
            } )
        return ls[ 'folder' ] if 'folder' in ls else None

    def extract_acl( self , folder_data ):
        """
        Extrait les entrées d'ACL d'un dossier vers un dictionnaire.

        :param folder_data: les informations d'un dossier

        :return: un dictionnaire associant à chaque EPPN disposant de droits \
                sur un dossier un sous-dictionnaire contenant les permissions \
                et l'identifiant de l'entrée dans ses champs 'perm' et 'zid', \
                respectivement
        """
        if 'acl' not in folder_data or 'grant' not in folder_data[ 'acl' ]:
            return dict( )
        rv = dict( )
        for entry in folder_data[ 'acl' ][ 'grant' ]:
            if 'd' not in entry or 'perm' not in entry or 'zid' not in entry:
                continue
            rv[ entry[ 'd' ] ] = {
                    'zid'   : entry[ 'zid' ] ,
                    'perm'  : entry[ 'perm' ] ,
                }
        return rv

    def create_folder( self , name , parent_id , folder_type ,
            color = None , url = None , flags = None , others = None ):
        """
        Demande la création d'un dossier. Si le mode simulé est activé, l'appel
        ne sera pas effectué.

        :param str name: le nom du dossier à créer
        :param parent_id: l'identifiant du dossier parent
        :param str folder_type: le type de dossier à créer (conversation, \
                message, contact, appointment, task, wiki, document)
        :param str color: la couleur, sous la forme d'un code RGB hexadécimal \
                ('#RRGGBB')
        :param str url: l'URL de synchronisation, s'il y en a une
        :param str flags: les drapeaux Zimbra du dossier
        :param dict others: un dictionnaire contenant des valeurs \
                supplémentaires à associer au dossier

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        Logging( 'zimbra' ).debug(
                'Création{} du dossier {}, parent {}, type {}'.format(
                    ' simulée' if self.fake_it_ else '' ,
                    name , parent_id , folder_type ) )

        if self.fake_it_:
            return {}
        data = {
            'name' : name ,
            'view' : folder_type ,
            'l' : parent_id ,
            'fie' : '1' ,
        }
        if others is not None: data.update( others )
        if 'acl' not in data: data[ 'acl' ] = {}
        if color is not None: data[ 'rgb' ] = color
        if url is not None: data[ 'url' ] = url
        if flags is not None: data[ 'f' ] = flags
        return self.send_request_( 'Mail' , 'CreateFolder' , {
                'folder' : data
            } )

    def mount( self , source_eppn , source_id , name , parent_id , folder_type ,
            color = None , flags = None , others = None ):
        """
        Demande le montage d'un dossier. Si le mode simulé est activé, l'appel
        ne sera pas effectué.

        :param source_eppn: l'EPPN de l'utilisateur depuis le compte duquel \
                on veut monter un dossier
        :param source_id: l'identifiant Zimbra du dossier à monter
        :param str name: le nom du dossier à créer
        :param parent_id: l'identifiant du dossier parent
        :param str folder_type: le type de dossier à créer (conversation, \
                message, contact, appointment, task, wiki, document)
        :param str color: la couleur, sous la forme d'un code RGB hexadécimal \
                ('#RRGGBB')
        :param str flags: les drapeaux Zimbra du dossier
        :param dict others: un dictionnaire contenant des valeurs \
                supplémentaires à associer au dossier

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        Logging( 'zimbra' ).debug(
                ( 'Montage{} du dossier #{} de {} dans le dossier {} '
                    + '(parent {}, type {})' ).format(
                        ' simulé' if self.fake_it_ else '' ,
                        source_id , source_eppn , name , parent_id ,
                        folder_type ) )
        if self.fake_it_:
            return {}
        data = {
            'name' : name ,
            'view' : folder_type ,
            'l' : parent_id ,
            'owner' : source_eppn ,
            'rid' : source_id ,
        }
        if others is not None: data.update( others )
        if color is not None: data[ 'rgb' ] = color
        if flags is not None: data[ 'f' ] = flags
        return self.send_request_( 'Mail' , 'CreateMountpoint' , {
                'link' : data
            } )

    def move_folder( self , folder_id , to_id ):
        """
        Demande le déplacement d'un dossier. Si le mode simulé est activé,
        l'appel ne sera pas effectué.

        :param folder_id: l'identifiant du dossier à déplacer
        :param to_id: l'identifiant du nouveau dossier parent

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        Logging( 'zimbra' ).debug(
                'Déplacement{} du dossier #{} vers dossier #{}'.format(
                    ' simulé' if self.fake_it_ else '' ,
                    folder_id , to_id ) )
        if self.fake_it_:
            return {}
        return self.send_request_( 'Mail' , 'FolderAction' , {
                'action' : {
                    'op' : 'move' ,
                    'id' : folder_id ,
                    'l' : to_id ,
                }
            } )

    def rename_folder( self , folder_id , name ):
        """
        Demande à l'API de renommer un dossier. Si le mode simulé est activé,
        l'appel ne sera pas effectué.

        :param folder_id: l'identifiant du dossier à renommer
        :param str name: le nouveau nom du dossier

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        Logging( 'zimbra' ).debug(
                'Renommage{} du dossier #{}; nouveau nom "{}"'.format(
                    ' simulé' if self.fake_it_ else '' ,
                    folder_id , name ) )
        if self.fake_it_:
            return {}
        return self.send_request_( 'Mail' , 'FolderAction' , {
                'action' : {
                    'op' : 'rename' ,
                    'id' : folder_id ,
                    'name' : name ,
                }
            } )

    def delete_folder( self , folder_id ):
        """
        Demande à l'API de supprimer un dossier. Si le mode simulé est activé,
        l'appel ne sera pas effectué.

        :param folder_id: l'identifiant du dossier à supprimer

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        Logging( 'zimbra' ).debug(
                'Suppression{} du dossier #{}'.format(
                    ' simulée' if self.fake_it_ else '' ,
                    folder_id ) )
        if self.fake_it_:
            return {}
        return self.send_request_( 'Mail' , 'FolderAction' , {
                'action' : {
                    'op' : 'delete' ,
                    'id' : folder_id ,
                }
            } )

    def set_grant( self , folder_id , eppn , perm ):
        """
        Demande à l'API de donner des droits sur un dossier à un utilisateur.

        :param folder_id: l'identifiant du dossier sur lequel des droits \
                doivent être positionnés
        :param eppn: l'EPPN de l'utilisateur pour lequel des droits doivent \
                être positionnés
        :param perm: les permissions à attribuer à l'utilisateur

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        Logging( 'zimbra' ).debug(
                'Dossier #{}: mise en place des droits "{}" pour {}'.format(
                        folder_id , perm , eppn ) )
        if self.fake_it_:
            return {}
        return self.send_request_( 'Mail' , 'FolderAction' , {
                'action' : {
                    'op' : 'grant' ,
                    'id' : folder_id ,
                    'grant' : {
                        'perm' : perm ,
                        'gt' : 'usr' ,
                        'd' : eppn ,
                    } ,
                } ,
            } )

    def remove_grant( self , folder_id , zid ):
        """
        Demande à l'API de supprimer des droits sur un dossier.

        :param folder_id: l'identifiant du dossier pour lequel les droits \
                doivent être modifiés
        :param zid: l'identifiant de l'entrée d'ACL à supprimer

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        Logging( 'zimbra' ).debug(
                'Dossier #{}: suppression de l\'entrée ACL {}'.format(
                        folder_id , zid ) )
        if self.fake_it_:
            return {}
        return self.send_request_( 'Mail' , 'FolderAction' , {
                'action': {
                    'op': '!grant' ,
                    'id': folder_id ,
                    'zid' : zid
                }
            } )

    def get_data_sources( self ):
        """
        Récupère la liste des sources de données externes associées à un compte
        Zimbra.

        :return: le résultat de la requête

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        Logging( 'zimbra' ).debug( 'Liste des sources de données' )
        return self.send_request_( 'Mail' , 'GetDataSources' , {} )

    def modify_data_source( self , ds_type , ds_id , **kwargs ):
        """
        Met à jour les paramètres d'une source de données Zimbra. Cette méthode
        est essentiellement un wrapper autour de la requête SOAP
        ZimbraMail::ModifyDataSource. Les paramètres supplémentaires seront
        passés tels quels à l'API Zimbra.

        :param ds_type: le type de la source de données à modifier
        :param ds_id: l'identifiant de la source de données à modifier

        :raises ZimbraError: une erreur de communication ou de requête \
                s'est produite
        """
        assert len( kwargs ) != 0
        data = dict( kwargs )
        data.update({ 'id' : ds_id })
        Logging( 'zimbra' ).debug(
                'Modification de source de données {}: {}'.format(
                        ds_type , repr( data ) ) )
        if self.fake_it_:
            return
        self.send_request_( 'Mail' , 'ModifyDataSource' , {
            ds_type : data
        } )

    def send_request_( self , namespace , request , data = None ):
        """
        Envoie une requête au serveur Zimbra

        :param str namespace: l'espace de nommage de l'appel, par exemple Mail
        :param str request: le nom de la requête, par exemple GetFolder

        :return: la réponse renvoyée par le serveur

        :raises ZimbraRequestError: une erreur de communication ou de requête \
                s'est produite
        """
        assert self.token_ is not None
        if data is None:
            data = dict()
        Logging( 'zimbra.request' ).debug(
                'Requête {}.{}( {} )'.format(
                    namespace , request , repr( data ) ) )
        req = self.comm_.gen_request( token = self.token_ )
        req.add_request( request + 'Request' , data , 'urn:zimbra' + namespace )
        response = self.comm_.send_request( req )
        if response.is_fault( ):
            raise ZimbraRequestError( namespace , request , response )
        rv = response.get_response( )
        return rv[ request + 'Response' ]
Exemple #38
0
class GetZimbra():
    def __init__(self, hostname, user, password, domain, pathzm, atributo, add_domain, quota, path_archive):
        self.url = f'https://{hostname}:7071/service/admin/soap'

        self.usr = user
        self.__password = password
        self.domain = domain.split(',')
        self.__atributo = atributo
        self.quota = quota
        self.__add_domain = add_domain
        self.__path_archive = path_archive

        self.today = datetime.datetime.now().date()
        self.colum = ['name']
        
    def connect(self):
        try:
            self.comm = Communication(self.url)

            self.__token = auth.authenticate(self.url, self.usr, self.__password, admin_auth=True)
            self.request = self.comm.gen_request(token=self.__token, set_batch=True)

            return 'Conectado'

        except urllib.error.URLError as E:
        # catastrophic error. bail.
            return f'Erro na conexão: {E}'


    def getAllDomains(self):
        """Retornar todos os dominios do ambiente"""
        self.request.add_request(
        'GetAllDomainsRequest',
        {
        },
            'urn:zimbraAdmin'
        )
        response = self.comm.send_request(self.request)
        if not response.is_fault():
            alldomains = response.get_response(1)['GetAllDomainsResponse']['domain']
            return [{k: v for k, v in cos.items() if k == 'name'} for cos in alldomains] # Retornar somente name do dominio

        else:
            return f'Error CountAccount: {response.get_fault_message()}'


    def countAccount(self):
        """Limite de COS por dominio"""
        for dom in self.domain:
            self.request.add_request(
            'CountAccountRequest',
            {
                    "domain": {
                        "_content": dom,
                        "by": "name"
                    }
                },
                'urn:zimbraAdmin'
            )
        response = self.comm.send_request(self.request)
        if not response.is_fault():
            allcount = response.get_response(1)['CountAccountResponse']
            l_domains = []
            d = 0

            if len(self.domain) > 1:
                for a in allcount:
                    cos = {}
                    cos.update({'name': self.domain[d]})
                    d =+ 1
                    for v in a['cos']:
                        cos.update({v['name']: v['_content']})
                        if v['name'] not in self.colum:
                            self.colum.append(v['name'])
                    l_domains.append(cos)
            else: 
                cos = {}
                cos.update({'name': self.domain[d]})
                for a in allcount['cos']:
                    cos.update({a['name']: a['_content']})
                    if a['name'] not in self.colum:
                        self.colum.append(a['name'])
                l_domains.append(cos)

            return l_domains

        else:
            return f'Error CountAccount: {response.get_fault_message()}'


    def getAllAccounts(self):
        """Consulta todas as contas por dominio"""

        if self.__add_domain:
            if 'name' in self.__atributo:
                self.__atributo.insert(1, 'domain')
            else:
                return "Só é possivel adicionar o dominio se adicionar o atributo 'name'"

        self.request.add_request(
            "GetAllAccountsRequest",
            {   
                "domain": {
                    "_content": self.domain[0],
                    "by": "name"
                }
            },
            "urn:zimbraAdmin"
        )
        self.request.add_request(
            "GetAllCosRequest",
            {
            },
            "urn:zimbraAdmin"
        )
        if self.quota:
            self.request.add_request(
                "GetQuotaUsageRequest",
                {   
                    "domain": self.domain[0],
                    "allServers": "1"
                },
                "urn:zimbraAdmin"
                )
        response = self.comm.send_request(self.request)
        
        if not response.is_fault():
            allAccounts = [response.get_response(1)['GetAllAccountsResponse']['account'],response.get_response(2)['GetAllCosResponse']['cos']]
            if self.quota:
                allAccounts.append(response.get_response(3)['GetQuotaUsageResponse']['account'])
            return allAccounts

        else:
            return f'Error getAllAccounts: {response.get_fault_message()}'


    def getFormatted(self, func=None):
        quota = ''
        lista_dict = []
        for account in func[0]:
            a_atr = {}
            a_atr['name'] = account['name']
            for atr in account['a']:
                if atr['n'] in self.__atributo:
                    a_atr[atr['n']] = atr['_content']
            lista_dict.append(a_atr)

        if 'zimbraCOSId' in self.__atributo:
            allcos = [{k: v for k, v in cos.items() if k == 'name' or k == 'id'} for cos in func[1]] 

        if self.quota:
            quota = func[2]
            for account in quota:
                account['used'] = f"{int(account['used'] / 1024 / 1024)} MB"
                account['limit'] = f"{int(account['limit'] / 1024 / 1024)} MB"
            [self.__atributo.append(i) for i in ['used', 'limit']]
            
        with open(f'{self.__path_archive}Relatório_{self.domain[0]}.csv', 'w') as archive:
            data_csv = DictWriter(archive, fieldnames=self.__atributo)
            data_csv.writeheader()
            for account in lista_dict:
                dict_comprehension = {chave: valor for chave, valor in account.items()}

                if self.__add_domain:
                    dict_comprehension['domain'] = dict_comprehension['name'].split('@')[1]

                if self.quota: # Configurar quota com as contas
                    for i in quota:
                        if i['name'] == account['name']:
                            dict_comprehension['used'] = i['used']
                            dict_comprehension['limit'] = i['limit']
                

                if 'zimbraLastLogonTimestamp' in dict_comprehension.keys(): # Configurar data do ultimo login
                    data = dict_comprehension['zimbraLastLogonTimestamp']
                    date_br = datetime.datetime(
                    int(data[:4]),
                    int(data[4:6]),
                    int(data[6:8]),
                    int(data[8:10]),
                    int(data[10:12]),
                    int(data[12:14])
                    ).strftime('%d/%m/%Y %H:%M')

                    dict_comprehension['zimbraLastLogonTimestamp'] = date_br

                if 'zimbraCreateTimestamp' in dict_comprehension.keys(): # Configurar data de criação
                    data1 = dict_comprehension['zimbraCreateTimestamp']
                    date1_br = datetime.datetime(
                    int(data1[:4]),
                    int(data1[4:6]),
                    int(data1[6:8]),
                    int(data1[8:10]),
                    int(data1[10:12]),
                    int(data1[12:14])
                    ).strftime('%d/%m/%Y %H:%M')

                    dict_comprehension['zimbraCreateTimestamp'] = date1_br
                
                if 'zimbraCOSId' in dict_comprehension.keys(): # Configurar nome do COS
                    cosid = dict_comprehension['zimbraCOSId']
                    cosname = [cos['name'] for cos in allcos if cosid == cos['id']]

                    dict_comprehension['zimbraCOSId'] = cosname[0]

                data_csv.writerow(dict_comprehension)

        return f'Planilha criada com SUCESSO! \nRelatório_{self.today}'


    def formatedPlan(self, func):
        list_domains = func

        with open(f'{self.__path_archive}Relatório_{self.today}.csv', 'w') as archive:
            data_csv = DictWriter(archive, fieldnames=self.colum)
            data_csv.writeheader()
            for domain_count in list_domains:
                list_domains = {chave: valor for chave, valor in domain_count.items()}
                data_csv.writerow(list_domains)
        
        return f'Planilha criada com SUCESSO! \nRelatório_{self.today}'
def authenticate(url, account, key, by='name', expires=0, timestamp=None,
                 timeout=None, request_type="xml"):

    """ Authenticate to the Zimbra server

    :param url: URL of Zimbra SOAP service
    :param account: The account to be authenticated against
    :param key: The preauth key of the domain of the account
    :param by: If the account is specified as a name, an ID or a
      ForeignPrincipal
    :param expires: When the token expires (or 0 for default expiration)
    :param timestamp: When the token was requested (None for "now")
    :param timeout: Timeout for the communication with the server. Defaults
      to the urllib2-default
    :return: The authentication token
    :rtype: str or None or unicode
    """

    if timestamp is None:
        timestamp = int(datetime.now().strftime("%s")) * 1000

    pak = preauth.create_preauth(account, key, by, expires, timestamp)

    if request_type == 'xml':

        auth_request = RequestXml()

    else:

        auth_request = RequestJson()

    auth_request.add_request(
        'AuthRequest',
        {
            'account': {
                'by': by,
                '_content': account
            },
            'preauth': {
                'timestamp': timestamp,
                'expires': expires,
                '_content': pak
            }
        },
        'urn:zimbraAccount'
    )

    server = Communication(url, timeout)

    if request_type == 'xml':

        response = ResponseXml()

    else:

        response = ResponseJson()

    try:

        server.send_request(auth_request, response)

    except HTTPError:

        # A HTTPError (which is an AuthError in most cases) occured. Simply
        # return nothing

        return None

    return response.get_response()['AuthResponse']['authToken']['_content']
Exemple #40
0
    def run_admin_test(self, request_type):
        """ Actually do the work
        """

        config = get_config()

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

            # Run only if enabled

            token = authenticate(config.get("admin_request_test", "admin_url"),
                                 config.get("admin_request_test",
                                            "admin_account"),
                                 config.get("admin_request_test",
                                            "admin_password"),
                                 config.get("admin_request_test",
                                            "admin_account_by"),
                                 admin_auth=True,
                                 request_type=request_type)

            if token is None:

                self.fail("Authentication with the configured settings "
                          "was not successful")

            # Create an account

            comm = Communication(config.get("admin_request_test", "admin_url"))

            if request_type == "xml":

                request = RequestXml()

            else:

                request = RequestJson()

            request.set_auth_token(token)

            request.add_request(
                "CreateAccountRequest", {
                    "name": config.get("admin_request_test", "test_account"),
                    "password": config.get("admin_request_test",
                                           "test_password")
                }, "urn:zimbraAdmin")

            if request_type == "xml":

                response = ResponseXml()

            else:

                response = ResponseJson()

            comm.send_request(request, response)

            if response.is_fault():

                self.fail(
                    "CreateAccount faulted. (%s) %s" %
                    (response.get_fault_code(), response.get_fault_message()))

            account_id = response.get_response(
            )["CreateAccountResponse"]["account"]["id"]

            # Try to log in as the new account

            user_token = authenticate(config.get("admin_request_test", "url"),
                                      config.get("admin_request_test",
                                                 "test_account"),
                                      config.get("admin_request_test",
                                                 "test_password"),
                                      "name",
                                      request_type=request_type,
                                      use_password=True)

            if user_token is None:

                self.fail("Cannot log in as the test user.")

            # Remove account

            request.clean()
            response.clean()
            request.set_auth_token(token)

            request.add_request("DeleteAccountRequest", {"id": account_id},
                                "urn:zimbraAdmin")

            comm.send_request(request, response)

            if response.is_fault():

                self.fail(
                    "Cannot remove test account: (%s) %s" %
                    (response.get_fault_code(), response.get_fault_message()))
Exemple #41
0
    def test_genrequest_batch_xml(self):

        """ Create a batch-request only using the Communication-object (
            xml-version)
        """

        config = get_config()

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

            # Run only if enabled

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

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

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

            request = comm.gen_request(
                request_type="xml",
                token=token,
                set_batch=True
            )

            self.assertEqual(
                type(request),
                RequestXml,
                "Generated request wasn't an xml-request"
            )

            request.add_request(
                "NoOpRequest",
                {

                },
                "urn:zimbraMail"
            )

            request.add_request(
                "NoOpRequest",
                {

                },
                "urn:zimbraMail"
            )

            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.is_batch(),
                True,
                "Batch-request didn't return a Batch response."
            )

            expected_batch = {
                'nameToId': {
                    'NoOpResponse': [
                        '1',
                        '2'
                    ]
                },
                'hasFault': False,
                'idToName': {
                    '1': 'NoOpResponse',
                    '2': 'NoOpResponse'
                }
            }

            self.assertEqual(
                response.get_batch(),
                expected_batch,
                "Batch-dictionary wasn't expected"
            )