Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP added is_google_cloud to version info for issue #12 #40

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 33 additions & 28 deletions pgbedrock/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@

from pgbedrock import common


logger = logging.getLogger(__name__)


Q_GET_ALL_CURRENT_DEFAULTS = """
WITH relkind_mapping (objkey, objkind) AS (
WITH relkind_mapping (objkey, objkind) AS (
VALUES ('f', 'functions'),
('r', 'tables'),
('S', 'sequences'),
Expand All @@ -24,7 +22,7 @@
(aclexplode(def.defaclacl)).privilege_type
FROM
pg_default_acl def
JOIN pg_authid auth
JOIN {roles_table} auth
ON def.defaclrole = auth.oid
JOIN pg_namespace nsp
ON def.defaclnamespace = nsp.oid
Expand All @@ -41,7 +39,7 @@
subq.privilege_type
FROM
subq
JOIN pg_authid t_grantee
JOIN {roles_table} t_grantee
ON subq.grantee_oid = t_grantee.oid
WHERE
subq.grantor_oid != subq.grantee_oid
Expand All @@ -66,14 +64,14 @@
(aclexplode(c.relacl)).privilege_type
FROM
pg_class c
JOIN pg_authid t_owner
JOIN {roles_table} t_owner
ON c.relowner = t_owner.OID
JOIN pg_namespace nsp
ON c.relnamespace = nsp.oid
JOIN relkind_mapping map
ON c.relkind = map.objkey
WHERE
nsp.nspname NOT LIKE 'pg\_t%'
nsp.nspname NOT LIKE 'pg\_t%%'
AND c.relacl IS NOT NULL
), schemas AS (
SELECT
Expand All @@ -83,7 +81,7 @@
t_owner.rolname AS owner,
(aclexplode(nsp.nspacl)).privilege_type
FROM pg_namespace nsp
JOIN pg_authid t_owner
JOIN {roles_table} t_owner
ON nsp.nspowner = t_owner.OID
), combined AS (
SELECT *
Expand All @@ -99,7 +97,7 @@
combined.privilege_type
FROM
combined
JOIN pg_authid t_grantee
JOIN {roles_table} t_grantee
ON combined.grantee_oid = t_grantee.oid
WHERE combined.owner != t_grantee.rolname
;
Expand All @@ -118,7 +116,7 @@
rolreplication,
rolsuper,
rolvaliduntil
FROM pg_authid
FROM {roles_table}
WHERE rolname != 'pg_signal_backend'
;
"""
Expand All @@ -129,9 +127,9 @@
auth_group.rolname AS group
FROM
pg_auth_members link_table
JOIN pg_authid auth_member
JOIN {roles_table} auth_member
ON link_table.member = auth_member.oid
JOIN pg_authid auth_group
JOIN {roles_table} auth_group
ON link_table.roleid = auth_group.oid
;
"""
Expand Down Expand Up @@ -190,17 +188,17 @@
t_owner.rolname AS owner,
co.is_dependent
FROM combined AS co
JOIN pg_authid t_owner
JOIN {roles_table} t_owner
ON co.owner_id = t_owner.OID
WHERE
co.schema NOT LIKE 'pg\_t%'
co.schema NOT LIKE 'pg\_t%%'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this double percent sign correct? I've never needed to do that anywhere else in pgbedrock, and the other uses of a percent sign within a query in this file weren't modified, which makes me wonder if this is a remnant of tinkering.

;
"""

Q_GET_ALL_PERSONAL_SCHEMAS = """
SELECT nsp.nspname
FROM pg_namespace nsp
JOIN pg_authid auth
JOIN {roles_table} auth
ON nsp.nspname = auth.rolname
WHERE auth.rolcanlogin IS TRUE
;
Expand All @@ -210,7 +208,8 @@
SELECT
substring(version from 'PostgreSQL ([0-9.]*) ') AS postgres_version,
substring(version from 'Redshift ([0-9.]*)') AS redshift_version,
version LIKE '%Redshift%' AS is_redshift
version LIKE '%Redshift%' AS is_redshift,
exists(SELECT * FROM pg_roles WHERE rolname = 'cloudsqladmin') AS is_google_cloud
FROM version()
;
"""
Expand All @@ -219,23 +218,23 @@
# don't add things like SELECT for tables into the write privileges
PRIVILEGE_MAP = {
'tables':
{'read': ('SELECT', ),
{'read': ('SELECT',),
'write': ('INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER')
},
'sequences':
{'read': ('SELECT', ),
{'read': ('SELECT',),
'write': ('USAGE', 'UPDATE')
},
'schemas':
{'read': ('USAGE', ),
'write': ('CREATE', )
{'read': ('USAGE',),
'write': ('CREATE',)
},
}

ObjectInfo = namedtuple('ObjectInfo', ['kind', 'name', 'owner', 'is_dependent'])
ObjectAttributes = namedtuple('ObjectAttributes',
['kind', 'schema', 'name', 'owner', 'is_dependent'])
VersionInfo = namedtuple('VersionInfo', ['postgres_version', 'redshift_version', 'is_redshift'])
VersionInfo = namedtuple('VersionInfo', ['postgres_version', 'redshift_version', 'is_redshift', 'is_google_cloud'])


class DatabaseContext(object):
Expand All @@ -259,6 +258,11 @@ def __init__(self, cursor, verbose):
self.cursor = cursor
self.verbose = verbose
self._cache = dict()
version_info = self.get_version_info()
if version_info.is_google_cloud:
self.query_context = {'roles_table': 'pg_roles'}
else:
self.query_context = {'roles_table': 'pg_authid'}

def __getattribute__(self, attr):
""" If the requested attribute should be cached and hasn't, fetch it and cache it. """
Expand Down Expand Up @@ -302,7 +306,7 @@ def get_all_current_defaults(self):
"""
DefaultRow = namedtuple('DefaultRow',
['grantee', 'objkind', 'grantor', 'schema', 'privilege'])
common.run_query(self.cursor, self.verbose, Q_GET_ALL_CURRENT_DEFAULTS)
common.run_query(self.cursor, self.verbose, Q_GET_ALL_CURRENT_DEFAULTS.format(**self.query_context))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there end up being more than one thing we have to change to get the queries to work, I can see having a container for all those variables (e.g. self.query_context, but I'd prefer to cross that bridge when we get there. For the moment it feels clearer / less like premature optimization to just have a self.roles_table that gets passed in. Not only is it less abstraction but it also lets us clearly communicate in the .format() what is being passed.


current_defaults = defaultdict(dict)
for i in self.cursor.fetchall():
Expand Down Expand Up @@ -376,7 +380,7 @@ def get_all_current_nondefaults(self):
"""
NonDefaultRow = namedtuple('NonDefaultRow',
['grantee', 'objkind', 'objname', 'privilege'])
common.run_query(self.cursor, self.verbose, Q_GET_ALL_CURRENT_NONDEFAULTS)
common.run_query(self.cursor, self.verbose, Q_GET_ALL_CURRENT_NONDEFAULTS.format(**self.query_context))
current_nondefaults = defaultdict(dict)

for i in self.cursor.fetchall():
Expand Down Expand Up @@ -410,8 +414,9 @@ def get_role_current_nondefaults(self, rolename, object_kind, access):
return set()

def get_all_role_attributes(self):
""" Return a dict with key = rolname and values = all fields in pg_authid """
common.run_query(self.cursor, self.verbose, Q_GET_ALL_ROLE_ATTRIBUTES)
""" Return a dict with key = rolname and values = all fields
in roles_table identified by get_context() depending on the DB environment"""
common.run_query(self.cursor, self.verbose, Q_GET_ALL_ROLE_ATTRIBUTES.format(**self.query_context))
role_attributes = {row['rolname']: dict(row) for row in self.cursor.fetchall()}
return role_attributes

Expand All @@ -430,7 +435,7 @@ def get_all_raw_object_attributes(self):
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)
common.run_query(self.cursor, self.verbose, Q_GET_ALL_RAW_OBJECT_ATTRIBUTES.format(**self.query_context))
results = [ObjectAttributes(*row) for row in self.cursor.fetchall()]
return results

Expand Down Expand Up @@ -471,7 +476,7 @@ def get_all_object_attributes(self):

def get_all_memberships(self):
""" Return a list of tuple, where each tuple is (member, group) """
common.run_query(self.cursor, self.verbose, Q_GET_ALL_MEMBERSHIPS)
common.run_query(self.cursor, self.verbose, Q_GET_ALL_MEMBERSHIPS.format(**self.query_context))
return self.cursor.fetchall()

def get_all_schemas_and_owners(self):
Expand All @@ -492,7 +497,7 @@ def get_role_memberships(self, rolename):

def get_all_personal_schemas(self):
""" Return all personal schemas as a set """
common.run_query(self.cursor, self.verbose, Q_GET_ALL_PERSONAL_SCHEMAS)
common.run_query(self.cursor, self.verbose, Q_GET_ALL_PERSONAL_SCHEMAS.format(**self.query_context))
personal_schemas = set([i[0] for i in self.cursor.fetchall()])
return personal_schemas

Expand Down