コード例 #1
0
def test_get_schema_objects_no_entry():
    dbcontext = context.DatabaseContext(cursor=DUMMY, verbose=False)
    dbcontext._cache['get_all_nonschema_objects_and_owners'] = lambda: {
        common.ObjectName('foo'): 'bar',
    }
    actual = dbcontext.get_schema_objects(common.ObjectName('key_not_in_response'))
    assert actual == []
コード例 #2
0
ファイル: test_common.py プロジェクト: xyzlat/pgbedrock
def test_objectname_equivalence():
    objname1 = common.ObjectName(schema='myschema', unqualified_name='mytable')
    objname2 = common.ObjectName(schema='myschema', unqualified_name='mytable')
    assert objname1 == objname2

    objname1 = common.ObjectName(schema='myschema')
    objname2 = common.ObjectName(schema='myschema')
    assert objname1 == objname2
コード例 #3
0
def test_get_schema_objects():
    schema = common.ObjectName('foo')
    expected = 'bar'
    dbcontext = context.DatabaseContext(cursor=DUMMY, verbose=False)
    dbcontext._cache['get_all_nonschema_objects_and_owners'] = lambda: {
        common.ObjectName('foo'): expected
    }
    actual = dbcontext.get_schema_objects(schema)
    assert actual == expected
コード例 #4
0
def add_nonschema_ownerships(spec, dbcontext, objkind):
    """
    Add non-schema ownerships for a specific object kind (e.g. tables, sequences, etc.) to an
    existing spec.

    Objects that are dependent on other objects are skipped as we cannot configure their ownership;
    their ownership is tied to the object they depend on. Additionally, objects in personal schemas
    are skipped as they are managed by ownerships.py as part of the personal schema ownership.

    Returns:
        dict: The input spec with nonschema ownerships added
    """
    personal_schemas = dbcontext.get_all_personal_schemas()
    all_objects_and_owners = dbcontext.get_all_object_attributes()
    objects_and_owners = all_objects_and_owners.get(objkind, {})

    for schema, objects_and_attributes in objects_and_owners.items():
        # Skip objects in personal schemas; their ownership is already managed by ownerships.py
        if common.ObjectName(schema=schema) in personal_schemas:
            continue

        all_owners = set()
        for objattr in objects_and_attributes.values():
            if not objattr['is_dependent']:
                all_owners.add(objattr['owner'])

        # If all objects have the same owner, we just need to do 'schema.*' and we're done
        if len(all_owners) == 1:
            owner = list(all_owners)[0]

            if 'owns' not in spec[owner]:
                spec[owner]['owns'] = {objkind: []}
            elif objkind not in spec[owner]['owns']:
                spec[owner]['owns'][objkind] = []

            objname = common.ObjectName(schema=schema, unqualified_name='*')
            spec[owner]['owns'][objkind].append(objname)

            # Since all objects in this schema are owned by one role, we can skip the below
            continue

        for objname, objattr in objects_and_attributes.items():
            # Skip dependent objects; their ownership is managed by the object they depend on
            if objattr['is_dependent']:
                continue

            owner = objattr['owner']
            if 'owns' not in spec[owner]:
                spec[owner]['owns'] = {objkind: []}
            elif objkind not in spec[owner]['owns']:
                spec[owner]['owns'][objkind] = []

            spec[owner]['owns'][objkind].append(objname)

    return spec
コード例 #5
0
ファイル: privileges.py プロジェクト: xyzlat/pgbedrock
    def identify_desired_objects(self):
        """
        Create the sets of desired privileges. The sets will look like the following:

            self.desired_nondefaults:
                {(ObjectName(schema, unqualified_name), priv_name), ...}
                Example: {('myschema.mytable', 'SELECT'), ...}

            self.desired_defaults:
                {(grantor, schema, priv_name), ...}
                Example: {('svc-hr-etl', 'hr_schema', 'SELECT'), ...}
        """
        desired_nondefault_objs = set()
        schemas = []
        for objname in self.desired_items:
            if objname == common.ObjectName(
                    'personal_schemas') and self.object_kind == 'schemas':
                desired_nondefault_objs.update(self.personal_schemas)
            elif objname == common.ObjectName(
                    'personal_schemas') and self.object_kind != 'schemas':
                # The end-user is asking something impossible
                common.fail(
                    PERSONAL_SCHEMAS_ERROR_MSG.format(self.rolename,
                                                      self.object_kind,
                                                      self.access))
            elif objname == common.ObjectName('personal_schemas', '*'):
                schemas.extend(self.personal_schemas)
            elif objname.unqualified_name != '*':
                # This is a single non-default privilege ask
                owner = self.get_object_owner(objname)
                if owner != self.rolename:
                    desired_nondefault_objs.add(objname)
            else:
                # We were given a schema.*; we'll process those below
                schemas.append(objname.only_schema())

        for schema in schemas:
            # For schemas, we wish to have privileges for all existing objects, so get all
            # existing objects not owned by this role and add them to self.desired_nondefaults
            schema_objects = self.get_schema_objects(schema.qualified_name)
            desired_nondefault_objs.update(schema_objects)

        #Remove excepted elements
        desired_nondefault_objs.difference_update(self.excepted_items)

        # Cross our desired objects with the desired privileges
        priv_types = PRIVILEGE_MAP[self.object_kind][self.access]
        self.desired_nondefaults = set(
            itertools.product(desired_nondefault_objs, priv_types))

        if self.default_acl_possible:
            self.determine_desired_defaults(schemas)
コード例 #6
0
def test_get_role_objects_with_access(access, expected):
    dbcontext = context.DatabaseContext(cursor=DUMMY, verbose=True)
    dbcontext._cache['get_all_current_nondefaults'] = lambda: {
        ROLES[0]: {
            'tables': {
                'read': set([
                    (common.ObjectName(SCHEMAS[0], TABLES[0]), 'SELECT'),
                    (common.ObjectName(SCHEMAS[0], TABLES[1]), 'SELECT'),
                ])
            }
        }
    }
    actual = dbcontext.get_role_objects_with_access(ROLES[0], common.ObjectName(SCHEMAS[0]),
                                                    'tables', access)
    assert actual == expected
コード例 #7
0
def test_get_role_current_nondefaults(rolename, object_kind, access, expected):
    dbcontext = context.DatabaseContext(cursor=DUMMY, verbose=True)
    dbcontext._cache['get_all_current_nondefaults'] = lambda: {
        'role1': {
            'object_kind1': {
                'access1': set([
                    (common.ObjectName('foo', 'bar'), 'SELECT'),
                    (common.ObjectName('foo', 'baz'), 'SELECT'),
                    (common.ObjectName('foo', 'qux'), 'INSERT'),
                ])
            }
        }
    }
    actual = dbcontext.get_role_current_nondefaults(rolename, object_kind, access)
    assert actual == expected
コード例 #8
0
def test_has_default_privilege(rolename, schema, object_kind, access, expected):
    dbcontext = context.DatabaseContext(cursor=DUMMY, verbose=True)
    dbcontext._cache['get_all_current_defaults'] = lambda: {
        'role1': {
            'tables': {
                'read': set([
                    ('role1', common.ObjectName('schema2'), 'SELECT'),
                ]),
                'write': set([
                    ('not_this_role', common.ObjectName('schema1'), 'UPDATE'),
                ]),
            }
        }
    }
    assert dbcontext.has_default_privilege(rolename, schema, object_kind, access) == expected
コード例 #9
0
def test_get_schema_owner():
    schema = common.ObjectName('foo')
    expected_owner = 'bar'
    dbcontext = context.DatabaseContext(cursor=DUMMY, verbose=True)
    dbcontext._cache['get_all_schemas_and_owners'] = lambda: {schema: expected_owner}
    actual = dbcontext.get_schema_owner(schema)
    assert actual == expected_owner
コード例 #10
0
    def get_all_personal_schemas(self):
        """ Return all personal schemas

        Returns:
            set: A set of common.ObjectName instances
        """
        common.run_query(self.cursor, self.verbose, Q_GET_ALL_PERSONAL_SCHEMAS)
        personal_schemas = set([common.ObjectName(schema=row[0]) for row in self.cursor.fetchall()])
        return personal_schemas
コード例 #11
0
ファイル: test_common.py プロジェクト: xyzlat/pgbedrock
def test_objectname_sorting():
    list_of_objnames = [
        common.ObjectName(schema='baz'),
        common.ObjectName(schema='foo', unqualified_name='gamma'),
        common.ObjectName(schema='foo', unqualified_name='alpha'),
        common.ObjectName(schema='foo', unqualified_name='bravo'),
        common.ObjectName(schema='bar'),
    ]
    expected = [
        common.ObjectName(schema='bar'),
        common.ObjectName(schema='baz'),
        common.ObjectName(schema='foo', unqualified_name='alpha'),
        common.ObjectName(schema='foo', unqualified_name='bravo'),
        common.ObjectName(schema='foo', unqualified_name='gamma'),
    ]

    actual = sorted(list_of_objnames)
    assert actual == expected
コード例 #12
0
def test_get_all_personal_schemas(cursor):
    dbcontext = context.DatabaseContext(cursor, verbose=True)
    actual = dbcontext.get_all_personal_schemas()
    expected = set([common.ObjectName(schema) for schema in ROLES[1:3]])
    assert actual == expected

    # Make sure that this data is cached for future use
    cursor.close()
    actual_again = dbcontext.get_all_personal_schemas()
    assert actual_again == actual
コード例 #13
0
def get_spec_schemas(spec):
    """ Get all personal and non-personal schemas defined in the spec file """
    spec_schemas = []
    for rolename, config in spec.items():
        config = config or {}
        spec_schemas.extend(config.get('owns', {}).get('schemas', []))

        if config.get('has_personal_schema'):
            spec_schemas.append(common.ObjectName(rolename))

    return set(spec_schemas)
コード例 #14
0
 def get_all_schemas_and_owners(self):
     """
     Returns:
         dict: a dict of {schema_name: schema_owner}, where schema_name is a common.ObjectName
     """
     all_object_owners = self.get_all_object_attributes()
     schemas_subdict = all_object_owners.get('schemas', {})
     schema_owners = dict()
     for schema, attributes in schemas_subdict.items():
         objname = common.ObjectName(schema)
         schema_owners[objname] = attributes[objname]['owner']
     return schema_owners
コード例 #15
0
def determine_personal_schemas(spec):
    """
    Returns:
        set: A set of ObjectName instances of personal schemas
    """
    personal_schemas = set()
    for role, config in spec.items():
        if config and common.parse_bool(
                config.get('has_personal_schema', False)):
            personal_schemas.add(common.ObjectName(role))

    return personal_schemas
コード例 #16
0
def determine_schema_writers(spec):
    """
    Create a dict mapping from each schema to all roles that can create objects in that
    schema, i.e.:

    Returns:
        dict: A dict of the form {common.ObjectName(schema): [roleA, roleB, roleC], ...}
    """
    members_of_role = determine_role_members(spec)
    personal_schemas = determine_personal_schemas(spec)
    schema_owners = determine_schema_owners(spec)

    # At a minimum, the schema owner could conceivably create objects
    writers = {schema: set([owner]) for schema, owner in schema_owners.items()}

    for role, config in spec.items():
        try:
            writable_schemas = set(
                config['privileges']['schemas']['write']) if config else set()
        except KeyError:
            writable_schemas = set()

        if common.ObjectName('personal_schemas') in writable_schemas:
            writable_schemas.remove(common.ObjectName('personal_schemas'))
            writable_schemas.update(personal_schemas)

        for schema in writable_schemas:
            writers[schema].add(role)
            role_members = members_of_role[role]
            writers[schema].update(role_members)

    # Superusers can write in any schema
    superusers = determine_superusers(spec)
    for vals in writers.values():
        vals.update(superusers)

    return writers
コード例 #17
0
def collapse_personal_schemas(role, objects, objkind, dbcontext):
    """ If all personal objects (e.g. 'roleA.*', 'roleB.*', etc.) are in
    objects, then replace them with 'personal_schemas.*'. We only verify that non-empty personal
    schemas are all present here. If so, we assume that the same behavior should be true for other
    personal schemas if / when they are populated.

    Note that this role's personal schema (if it exists) will not show up here at all as
    determine_all_nonschema_privileges() filters it out. That is ok and intended: once
    `pgbedrock configure` is run it will ensure that this role owns everything in its own
    personal schema

    Returns:
        set: A set of common.ObjectName instances
    """
    personal_schemas = dbcontext.get_all_personal_schemas()
    personal_schemas_star = set([
        common.ObjectName(schema=objname.schema, unqualified_name='*')
        for objname in personal_schemas
    ])

    if not personal_schemas_star:
        return objects

    non_empty_personal_schemas = set()
    for objname in personal_schemas:
        if objname.schema != role and not dbcontext.is_schema_empty(
                objname, objkind):
            non_empty_personal_schemas.add(
                common.ObjectName(schema=objname.schema, unqualified_name='*'))

    if non_empty_personal_schemas.difference(objects) == set():
        objects.difference_update(personal_schemas_star)
        objects.add(
            common.ObjectName(schema='personal_schemas', unqualified_name='*'))

    return objects
コード例 #18
0
def determine_schema_owners(spec):
    """ Create a dict of {ObjectName(schema): owner} """
    schema_owners = dict()
    for role, config in spec.items():
        if not config:
            continue

        if 'owns' in config:
            owned_schemas = config['owns'].get('schemas', ())
            for schema in owned_schemas:
                schema_owners[schema] = role

        if common.parse_bool(config.get('has_personal_schema', False)):
            schema_owners[common.ObjectName(role)] = role

    return schema_owners
コード例 #19
0
    def get_all_raw_object_attributes(self):
        """
        Fetch results for all object attributes.

        The results are used in several subsequent methods, so having consistent results is
        important. Thus, this helper method is here to ensure that we only run this query once.
        """
        common.run_query(self.cursor, self.verbose, Q_GET_ALL_RAW_OBJECT_ATTRIBUTES)
        results = []
        NamedRow = namedtuple('NamedRow', ['kind', 'schema', 'unqualified_name', 'owner', 'is_dependent'])
        for i in self.cursor.fetchall():
            row = NamedRow(*i)
            objname = common.ObjectName(schema=row.schema, unqualified_name=row.unqualified_name)
            entry = ObjectAttributes(row.kind, row.schema, objname, row.owner, row.is_dependent)
            results.append(entry)
        return results
コード例 #20
0
    def get_all_current_nondefaults(self):
        """ Return a dict of the form:
            {roleA: {
                objkindA: {
                    'read': set([
                        (objname, privilege),
                        ...
                        ]),
                    'write': set([
                        (objname, privilege),
                        ...
                        ]),
                    },
                }
             roleB:
                ....
            }

            This will not include privileges granted by this role to itself
        """
        NamedRow = namedtuple(
            'NamedRow',
            ['grantee', 'objkind', 'schema', 'unqualified_name', 'privilege'])
        common.run_query(self.cursor, self.verbose,
                         Q_GET_ALL_CURRENT_NONDEFAULTS)
        current_nondefaults = defaultdict(dict)

        for i in self.cursor.fetchall():
            row = NamedRow(*i)
            is_read_priv = row.privilege in PRIVILEGE_MAP[row.objkind]['read']
            access_key = 'read' if is_read_priv else 'write'

            role_nondefaults = current_nondefaults[row.grantee]
            # Create this role's dict substructure for the first entry we come across
            if row.objkind not in role_nondefaults:
                role_nondefaults[row.objkind] = {
                    'read': set(),
                    'write': set(),
                }

            objname = common.ObjectName(schema=row.schema,
                                        unqualified_name=row.unqualified_name)
            entry = (objname, row.privilege)
            role_nondefaults[row.objkind][access_key].add(entry)

        return current_nondefaults
コード例 #21
0
def test_get_all_current_defaults(cursor):
    dbcontext = context.DatabaseContext(cursor, verbose=True)
    expected = {
        ROLES[0]: {
            'tables': {
                'read': set([
                    (ROLES[3], common.ObjectName(SCHEMAS[0]), 'SELECT'),
                ]),
                'write': set(),
            }
        }
    }
    actual = dbcontext.get_all_current_defaults()
    assert actual == expected

    # Make sure that this data is cached for future use
    cursor.close()
    actual_again = dbcontext.get_all_current_defaults()
    assert actual_again == expected
コード例 #22
0
def test_get_all_schemas_and_owners(cursor):
    dbcontext = context.DatabaseContext(cursor, verbose=True)
    expected = {
        common.ObjectName(SCHEMAS[0]): ROLES[0],
        common.ObjectName(SCHEMAS[1]): ROLES[0],
        common.ObjectName(SCHEMAS[2]): ROLES[1],
        common.ObjectName(ROLES[1]): ROLES[1],
        # These already existed
        common.ObjectName('public'): 'postgres',
        common.ObjectName('information_schema'): 'postgres',
        common.ObjectName('pg_catalog'): 'postgres',
    }

    actual = dbcontext.get_all_schemas_and_owners()
    assert actual == expected

    # Make sure that this data is cached for future use
    cursor.close()
    actual_again = dbcontext.get_all_schemas_and_owners()
    assert actual_again == actual
コード例 #23
0
def ensure_no_schema_owned_twice(spec):
    """ Check spec for schemas with multiple owners. """
    schema_ownerships = defaultdict(list)
    for rolename, config in spec.items():
        if not config:
            continue
        if config.get('has_personal_schema'):
            # Indicates a role has a personal schema with its same name
            schema_ownerships[common.ObjectName(rolename)].append(rolename)
        if config.get('owns') and config['owns'].get('schemas'):
            role_owned_schemas = config['owns']['schemas']
            for schema in role_owned_schemas:
                schema_ownerships[schema].append(rolename)

    error_messages = []
    for schema, owners in schema_ownerships.items():
        if len(owners) > 1:
            owners_formatted = ", ".join(sorted(owners))
            error_messages.append(
                MULTIPLE_SCHEMA_OWNER_ERR_MSG.format(schema.qualified_name,
                                                     owners_formatted))

    return error_messages
コード例 #24
0
def test_get_all_object_attributes(cursor):
    dbcontext = context.DatabaseContext(cursor, verbose=True)
    expected = {
        'tables': {
            SCHEMAS[0]: {
                common.ObjectName(SCHEMAS[0], TABLES[0]): {'owner': ROLES[1], 'is_dependent': False},
                common.ObjectName(SCHEMAS[0], TABLES[1]): {'owner': ROLES[1], 'is_dependent': False},
                common.ObjectName(SCHEMAS[0], TABLES[2]): {'owner': ROLES[3], 'is_dependent': False},
            }
        },
        'sequences': {
            SCHEMAS[0]: {
                common.ObjectName(SCHEMAS[0], SEQUENCES[0]): {'owner': ROLES[1], 'is_dependent': False},
                common.ObjectName(SCHEMAS[0], SEQUENCES[1]): {'owner': ROLES[2], 'is_dependent': False},
                common.ObjectName(SCHEMAS[0], SEQUENCES[2]): {'owner': ROLES[2], 'is_dependent': False},
            }
        },
        'schemas': {
            SCHEMAS[0]: {
                common.ObjectName(SCHEMAS[0]): {'owner': ROLES[0], 'is_dependent': False},
            },
            'public': {
                'public': {'owner': 'postgres', 'is_dependent': False},
            }
        }
    }

    actual = dbcontext.get_all_object_attributes()

    # We do this to avoid having to look at / filter out entries from
    # information_schema or pg_catalog
    for key in expected.keys():
        expected_entries = expected[key][SCHEMAS[0]]
        actual_entries = actual[key][SCHEMAS[0]]
        assert expected_entries == actual_entries

    # Make sure that this data is cached for future use
    cursor.close()
    actual_again = dbcontext.get_all_object_attributes()
    assert actual_again == actual
コード例 #25
0
def test_get_all_nonschema_objects_and_owners(cursor):
    dbcontext = context.DatabaseContext(cursor, verbose=True)
    expected = {
        common.ObjectName(SCHEMAS[0]): [
            context.ObjectInfo('tables', common.ObjectName(SCHEMAS[0], TABLES[0]), ROLES[0], False),
            context.ObjectInfo('sequences', common.ObjectName(SCHEMAS[0], SEQUENCES[1]), ROLES[0], False),
        ],
        common.ObjectName(SCHEMAS[1]): [
            context.ObjectInfo('tables', common.ObjectName(SCHEMAS[1], TABLES[0]), ROLES[1], False),
            context.ObjectInfo('sequences', common.ObjectName(SCHEMAS[1], SEQUENCES[2]), ROLES[1], False),
        ],
    }
    actual = dbcontext.get_all_nonschema_objects_and_owners()

    # We are deliberately not checking pg_catalog or information_schema here since that's a
    # lot of work and those should not be touched
    for k, v in expected.items():
        assert set(v) == set(actual[k])

    # Make sure that this data is cached for future use
    cursor.close()
    actual_again = dbcontext.get_all_nonschema_objects_and_owners()
    assert actual_again == actual
コード例 #26
0
ファイル: test_common.py プロジェクト: xyzlat/pgbedrock
def test_objectname_schema():
    objname = common.ObjectName(schema='myschema')
    assert objname.schema == 'myschema'
    assert objname.unqualified_name is None
    assert objname.qualified_name == 'myschema'
コード例 #27
0
def determine_nonschema_privileges_for_schema(role, objkind, objname,
                                              dbcontext):
    """
    Determine all non-schema privileges granted to a given role for all objects of objkind in
    the specified schema `objname`. Results will be returned as two sets: objects granted write
    access and objects granted read access.

    We explicitly start with writes because if a role has write access then pgbedrock will also
    grant it read access, meaning we won't need to grant that object a read. As a result, we have
    to start with writes first.

    If any write default privileges exist for this role x objkind x schema then we assume that write
    default privileges should be applied universally in this schema. This could definitely not be
    the case though. For example, because default privileges are scoped based on the grantor, it's
    possible that the role had write default privileges for things created by roleA but not for
    things created by roleB. pgbedrock is not fine-grained enough to handle a situation like this
    though, so if there is a write default privilege we just assume that this role should have
    default writes for all objects of objkind in this schema.

    Also note that we're lumping all write default privileges together. It is possible a role might
    have default privileges for UPDATE on all tables but no other write-level default privileges.
    Again, pgbedrock isn't meant to be this fine-grained: if it sees any write-level default
    privilege then it will identify this as a role that should get write default privileges, which
    means that this role will get _all_ write-level default privileges for that objkind in this
    schema.

    Args:
        role (str)

        objkind (str): The type of object. This must be one of the keys of
            context.PRIVILEGE_MAP, e.g. 'schemas', 'tables', etc.

        objname (common.ObjectName): The schema to determine non-schema privileges for

        dbcontext (context.DatabaseContext): A context.DatabaseContext instance for getting
            information for the associated database

    Returns:
        tuple: A tuple of with two items in it: a set of common.ObjectName instances with write
            privileges and a set of common.ObjectName instances with read privileges
    """
    # Get all objects of this objkind in this schema and which are not owned by this role
    objects_and_owners = dbcontext.get_schema_objects(objname)
    schema_objects = set()
    for entry in objects_and_owners:
        if entry.kind == objkind and entry.owner != role:
            schema_objects.add(entry.objname)

    has_default_write = dbcontext.has_default_privilege(
        role, objname, objkind, 'write')
    all_writes = dbcontext.get_role_objects_with_access(
        role, objname, objkind, 'write')

    if has_default_write or (all_writes == schema_objects
                             and all_writes != set()):
        # In the second condition, every object has a write privilege, so we assume that means
        # that this role should have default write privileges
        return set(
            [common.ObjectName(schema=objname.schema,
                               unqualified_name='*')]), set()

    # If we haven't returned yet then no write default privilege exists; we will have to
    # grant each write individually, meaning we also need to look at read privileges
    has_default_read = dbcontext.has_default_privilege(role, objname, objkind,
                                                       'read')
    all_reads = dbcontext.get_role_objects_with_access(role, objname, objkind,
                                                       'read')

    if has_default_read or (all_reads == schema_objects
                            and all_reads != set()):
        # In the second condition, every object has a read privilege, so we assume that means
        # that this role should have default read privileges
        return all_writes, set(
            [common.ObjectName(schema=objname.schema, unqualified_name='*')])
    else:
        # We have to grant each read individually as well. Because a write will already grant
        # a read, we have to remove all write-granted objects from our read grants
        only_reads = all_reads.difference(all_writes)
        return all_writes, only_reads
コード例 #28
0
def determine_schema_privileges(role, dbcontext):
    """
    Identify and return two lists:
        1) a list of schemas this role can write to
        2) a list of schemas this role can read from

    If a role can write it to a schema is assumed it can read as well, so that schema will only
    show up in the write list. As a result of this, we determine schemas with write access first
    so we can exclude write schemas from the read schemas list.

    Note that in Postgres if you own a schema you will always have read (USAGE) access to that
    schema but you _may not_ have write (CREATE) access to it, though you do by default. Because
    this is confusing and non-intuitive, we will include schemas in the write section even if they
    are owned by this role (and because they are in the write section then they will also be granted
    read privileges as well).

    Returns:
        tuple: A tuple of (write privileges, read privileges), where each component in the tuple
            is a set of common.ObjectName instances
    """
    # Make a copy of personal_schemas (by making a new set from it) as we will be mutating it
    personal_schemas = set(dbcontext.get_all_personal_schemas())

    # Get all schemas this role has write and read access to
    write_schemas_and_privs = dbcontext.get_role_current_nondefaults(
        role, 'schemas', 'write')
    write_schemas = set([objname for objname, _ in write_schemas_and_privs])
    read_schemas_and_privs = dbcontext.get_role_current_nondefaults(
        role, 'schemas', 'read')
    read_schemas = set([objname for objname, _ in read_schemas_and_privs])

    # Get all schemas owned by this role
    all_owned_schemas = dbcontext.get_all_schemas_and_owners()
    role_owned_schemas = {
        s
        for s, owner in all_owned_schemas.items() if owner == role
    }

    # Add all schemas owned by this role to the write and read schemas
    write_schemas.update(role_owned_schemas)
    read_schemas.update(role_owned_schemas)

    # Remove this role's personal schema if it exists
    write_schemas.difference_update({common.ObjectName(role)})
    read_schemas.difference_update({common.ObjectName(role)})
    personal_schemas.difference_update({common.ObjectName(role)})

    # If all personal schemas are in write_schemas then replace them with 'personal_schemas'
    if personal_schemas and personal_schemas.difference(
            write_schemas) == set():
        write_schemas.difference_update(personal_schemas)
        write_schemas.add(common.ObjectName('personal_schemas'))

    if personal_schemas and personal_schemas.difference(read_schemas) == set():
        read_schemas.difference_update(personal_schemas)
        read_schemas.add(common.ObjectName('personal_schemas'))

    # Remove all schemas this role has write access to
    read_only_schemas = read_schemas.difference(write_schemas)

    schemas_privs = {}
    if write_schemas:
        schemas_privs['write'] = write_schemas
    if read_only_schemas:
        schemas_privs['read'] = read_only_schemas

    return schemas_privs
コード例 #29
0
ファイル: test_common.py プロジェクト: xyzlat/pgbedrock
def test_objectname_nonschema():
    objname = common.ObjectName(schema='myschema', unqualified_name='mytable')
    assert objname.schema == 'myschema'
    assert objname.unqualified_name == 'mytable'
    assert objname.qualified_name == 'myschema."mytable"'
コード例 #30
0
ファイル: test_common.py プロジェクト: xyzlat/pgbedrock
def test_objectname_only_schema():
    objname = common.ObjectName(schema='myschema', unqualified_name='mytable')
    only_schema = objname.only_schema()
    assert only_schema.qualified_name == 'myschema'
    assert only_schema.unqualified_name is None