Exemplo n.º 1
0
    def add_share(self, shares, *, can_edit=False):
        """Share a cube to new groups and users

        By default will give query access. Set can_edit to True for editor access.

        Args:
            shares (list[Group,User]): Users and groups to share the cube to
            can_edit (bool): (Optional) True for edit privileges
        """

        shares = PySenseUtils.make_iterable(shares)
        curr_shares_arr = self.get_shares_json()
        rule = 'r' if can_edit is False else 'w'
        curr_id_arr = []
        for share in curr_shares_arr:
            party_id = share['partyId']
            curr_id_arr.append(party_id)
            del share['partyId']
            share['party'] = party_id

        for share in shares:
            share_id = share.get_id()
            if share_id is None:
                raise PySenseException.PySenseException(
                    'No id found for {}'.format(share))
            elif share_id in curr_id_arr:
                index = curr_id_arr.index(share_id)
                curr_shares_arr[index]['permission'] = rule
            elif isinstance(share, PySenseUser.User):
                curr_shares_arr.append({
                    'party': share.get_id(),
                    'type': 'user',
                    'permission': rule
                })
            elif isinstance(share, PySenseGroup.Group):
                curr_shares_arr.append({
                    'party': share.get_id(),
                    'type': 'group',
                    'rule': rule
                })
            else:
                raise PySenseException.PySenseException(
                    'Add Share expected User or group, got {}'.format(
                        type(share)))

        self.py_client.connector.rest_call(
            'put',
            'api/elasticubes/{}/{}/permissions'.format(
                self.server_address, self.get_title(url_encoded=True)),
            json_payload=curr_shares_arr)
Exemplo n.º 2
0
    def start_build(self, build_type, *, row_limit=None):
        """Initiates a build of the data model

        Only supported on Linux

        Args:
            build_type (str): Type of build (schema_changes, by_table, full, publish)
            row_limit (int): (Optional) Number of rows to build
        Returns:
            BuildTask: The build task object for the build
        """

        PySenseUtils.validate_version(self.py_client, SisenseVersion.Version.LINUX, 'start_build')

        build_type = build_type.lower()
        if build_type not in ['schema_changes', 'by_table', 'full', 'publish']:
            raise PySenseException.PySenseException('Unsupported build type {}'.format(build_type))

        json_payload = {
            'datamodelId': self.get_oid(),
            'buildType': build_type
        }
        if row_limit is not None:
            json_payload['rowLimit'] = row_limit

        resp_json = self.py_client.connector.rest_call('post', 'api/v2/builds',
                                                       json_payload=json_payload)

        return PySenseBuildTask.BuildTask(self.py_client, resp_json)
Exemplo n.º 3
0
    def remove_shares(self, shares):
        """Unshare a cube to groups and users

        To unshare a cube we have to:
            - Query for the whom the cube is currently shared with
            - Delete the users/groups we want to unshare with
            - Re upload the reduced share

        Args:
            shares (list[Group,User]): Users and groups to unshare the cube to
        """

        curr_shares_arr = self.get_shares_json()
        curr_id_arr = []
        for share in curr_shares_arr:
            curr_id_arr.append(share['partyId'])

        for share in PySenseUtils.make_iterable(shares):
            share_id = share.get_id()
            if share_id is None:
                raise PySenseException.PySenseException(
                    'No id found for {}'.format(share))
            elif share_id in curr_id_arr:
                index = curr_id_arr.index(share_id)
                del curr_shares_arr[index]
                del curr_id_arr[index]

        self.py_client.connector.rest_call(
            'put',
            'api/elasticubes/{}/{}/permissions'.format(
                self.server_address, self.get_title(url_encoded=True)),
            json_payload=curr_shares_arr)
Exemplo n.º 4
0
    def __init__(self,
                 host,
                 token,
                 version,
                 *,
                 debug=False,
                 verify=True,
                 param_dict=None,
                 connector=None):
        """ Initializes a PySense instance

        Args:
            host (str): host address
            token (json): a json bearer token with format
                {'authorization':  "Bearer yourlongaccesstokenstringthatyougotfromapreviouslogin"}
            version (str): version (either 'Windows' or 'Linux')
            debug (bool): (Optional) If true, prints detailed REST API logs to console. False by default.
            verify (bool): (Optional) If false, disables SSL Certification. True by default.
            param_dict (dict): (Optional) For passing in additional parameters
            connector (RestConnector): (Optional) Pass in your own connector (normally used for mock tests)
        """

        if param_dict is None:
            self.param_dict = {}
        else:
            self.param_dict = param_dict

        default_dict = {'CUBE_CACHE_TIMEOUT_SECONDS': 60}

        for key, value in default_dict.items():
            if key not in self.param_dict:
                self.param_dict[key] = value

        # Verify version
        if version.lower() == 'windows':
            self.version = SisenseVersion.Version.WINDOWS
        elif version.lower() == 'linux':
            self.version = SisenseVersion.Version.LINUX
        else:
            raise PySenseException.PySenseException(
                '{} not a valid OS. Please select Linux or Windows'.format(
                    version))

        # Initiate PySense Connection
        if connector is not None:
            self.connector = connector
        else:
            self.connector = PySenseRestConnector.RestConnector(
                host, token, debug, verify)
            # Set up Roles
            roles = self.connector.rest_call('get', 'api/roles')
            self.roles = {}
            for role in roles:
                if role['name'] in [
                        'dataDesigner', 'super', 'dataAdmin', 'admin',
                        'contributor', 'consumer'
                ]:
                    self.roles[SisenseRole.Role.from_str(
                        role['name'])] = role['_id']
Exemplo n.º 5
0
    def get_oid(self):
        """Returns the Elasticube id"""

        if 'oid' in self.json:
            return self.json['oid']
        else:
            raise PySenseException(
                'Cube {} is not currently running so this action cannot be performed'
            )
Exemplo n.º 6
0
    def get_role_by_id(self, role_id):
        """Get role from id

        Args:
            role_id (str): The role id for the role to find

        Returns:
            Role: The role with the given role id
        """

        for role in self.roles:
            if self.roles[role] == role_id:
                return role
        raise PySenseException.PySenseException(
            'No role with id {} found'.format(role_id))
Exemplo n.º 7
0
    def get_user_by_email(self, email):
        """Returns a single user based on email

        Args:
            email (str): The email of the user to get

        Returns:
            User: The user with the email address
        """
        users = self.get_users(email=email)
        if len(users) == 0:
            None
        elif len(users) > 1:
            raise PySenseException.PySenseException('{} users with email {} found. '.format(len(users), email))
        else:
            return users[0]
Exemplo n.º 8
0
 def from_str(role):
     """Returns the Role for a given string"""
     role = role.lower().replace(" ", "")
     if role in ['datadesigner', 'data_designer']:
         return Role.DATA_DESIGNER
     elif role in ['super', 'sysadmin', 'sys_admin']:
         return Role.SYS_ADMIN
     elif role in ['dataadmin', 'data_admin']:
         return Role.DATA_ADMIN
     elif role in ['admin']:
         return Role.ADMIN
     elif role in ['contributor', 'designer']:
         return Role.DESIGNER
     elif role in ['consumer', 'viewer']:
         return Role.VIEWER
     else:
         raise PySenseException.PySenseException(
             'No such role {} found'.format(role))
Exemplo n.º 9
0
def generate_token(host, username, password, verify=True):
    """Creates a new PySense client with the username and password

    Args:
        host (str): The Sisense server address
        username (str): Sisense username
        password (str): Sisense password
        verify (bool): SSL Verification

    Returns:
        JSON: A json authorization header
    """

    host = PySenseUtils.format_host(host)
    data = {'username': username, 'password': password}
    resp = requests.post('{}/api/v1/authentication/login'.format(host),
                         verify=verify,
                         data=data)
    if resp.status_code not in [200, 201, 204]:
        raise PySenseException.PySenseException(
            'ERROR: {}: {}\nURL: {}'.format(resp.status_code, resp.content,
                                            resp.url))
    return {'authorization': "Bearer " + resp.json()['access_token']}
Exemplo n.º 10
0
def validate_version(py_client, expected_version, function_name):
    if py_client.version != expected_version:
        raise PySenseException.PySenseException(
            '{} is only supported on {}'.format(function_name,
                                                expected_version))
Exemplo n.º 11
0
def parse_response(response):
    """Parses response and throw exception if not successful."""
    if response.status_code not in [200, 201, 204]:
        raise PySenseException.PySenseException(
            'ERROR: {}: {}\nURL: {}'.format(response.status_code,
                                            response.content, response.url))
Exemplo n.º 12
0
def does_cube_exist(py_client, cube_name, server_name):
    if py_client.get_elasticube_by_name(cube_name) is None:
        raise PySenseException.PySenseException(
            'No PySense Elasticube found on {} server.'.format(server_name)
        )
Exemplo n.º 13
0
    def rest_call(self, action_type, url, *,
                  data=None, json_payload=None, query_params=None, path=None, file=None, raw=False):
        """Run an arbitrary rest command against your Sisense instance

        Args:
            action_type (str): REST request type (get, post, patch, put, delete)
            url (str): api end point, example api/v1/app_database/encrypt_database_password or api/branding
            data (Any): (Optional) The data portion of the payload
            json_payload (JSON): (Optional) The JSON portion of the payload
            query_params (dict[Str,Any]): (Optional) A dictionary of query values to be added to the end of the url
                Method will strip out None dictionary values.
            path (str): (Optional) If set, response will be downloaded to file path
            file (str): (Optional) Path to files to be uploaded. Only usable with post.
            raw (bool): (Optional) If true, write raw data to file. Writes JSON by default

        Returns:
            JSON: Returns the json content blob by default. If path is set, returns nothing
        """

        action_type = action_type.lower()
        if query_params is not None:
            query_string = build_query_string(query_params)
        else:
            query_string = ''
        full_url = '{}/{}{}'.format(self.host, url, query_string)

        if self.debug:
            print('{}: {}'.format(action_type, full_url))
            if data is not None:
                print('Data: {}'.format(data))
            if json_payload is not None:
                print('JSON: {}'.format(json_payload))

        if file is not None:
            if action_type != 'post':
                raise PySenseException.PySenseException('Files can only be uploaded via post')
            file = {'file': open(file, 'rb')}

        if path is not None:
            with requests.request(
                action_type, full_url, stream=True,
                headers=self.token, data=data, json=json_payload, verify=self.verify
            ) as response:
                if raw:
                    with open(path, 'wb') as f:
                        shutil.copyfileobj(response.raw, f)
                else:
                    PySenseUtils.dump_json(response.json(), path)
        else:
            response = requests.request(
                action_type, full_url,
                headers=self.token, data=data, json=json_payload, verify=self.verify, files=file
            )
            parse_response(response)
            if len(response.content) == 0:
                return None
            else:
                try:
                    return response.json()
                except ValueError as e:
                    return response.content
Exemplo n.º 14
0
    def add_data_security_rule(self,
                               table,
                               column,
                               data_type,
                               *,
                               shares=None,
                               members=None,
                               server_address=None,
                               exclusionary=False,
                               all_members=None):
        """Define a data security rule

        Args:
            table (str): The table to apply security on
            column (str): The column to apply security on
            data_type (str): The data type of the column
            shares (list[Group,User]): (Optional) The users or groups to assign the security rule to.
                If none, will be 'everyone else' rule.
            members (list[str]): (Optional) An array of values which users should have access to
                If left blank, user will get 'Nothing'.
            server_address (str): (Optional) The server address of the ElastiCube.
                Set this to your server ip if this method fails without it set.
                Use 'Set' for Elasticube Set. Required for elasticube sets.
            exclusionary (bool): (Optional) Set to True if exclusionary rule
            all_members (bool): (Optional) Set to True to set 'Everything' rule

        Returns:
             Rule: The new security rule
        """

        server_address = server_address if server_address else self.server_address

        rule_json = [{
            "column": column,
            "datatype": data_type,
            "table": table,
            "elasticube": self.get_title(),
            "server": server_address,
            "exclusionary": exclusionary,
            "allMembers": all_members
        }]

        shares_json = []
        if shares is None:
            # Default rule
            rule_json[0]['shares'] = [{"type": "default"}]
        else:
            for party in PySenseUtils.make_iterable(shares):
                if isinstance(party, PySenseUser.User):
                    shares_json.append({
                        'party': party.get_id(),
                        'type': 'user'
                    })
                elif isinstance(party, PySenseGroup.Group):
                    shares_json.append({
                        'party': party.get_id(),
                        'type': 'group'
                    })
                else:
                    raise PySenseException.PySenseException(
                        '{} is not a user or a group object'.format(party))
            rule_json[0]['shares'] = shares_json

        member_arr = []
        if members is None:
            rule_json[0]['members'] = []
            rule_json[0][
                'allMembers'] = False if all_members is None else all_members
        else:
            rule_json[0]['allMembers'] = None
            for member in members:
                member_arr.append(str(member))
            rule_json[0]['members'] = member_arr
        resp_json = self.py_client.connector.rest_call(
            'post',
            'api/elasticubes/{}/{}/datasecurity'.format(
                server_address, self.get_title(url_encoded=True)),
            json_payload=rule_json)

        return PySenseRule.Rule(self.py_client, resp_json[0])