summaryrefslogtreecommitdiff
path: root/database/postgresql/postgresql_user.py
diff options
context:
space:
mode:
authorToshio Kuratomi <toshio@fedoraproject.org>2015-05-19 12:41:48 -0700
committerToshio Kuratomi <toshio@fedoraproject.org>2015-05-19 12:57:06 -0700
commitc9b17136e404ec8090a1d140f6875b6687720639 (patch)
tree4940c383bc63b5f647f22e58eb3c3fc7acdac3c8 /database/postgresql/postgresql_user.py
parent7dd9f57e161b78981eb797a4c77fd6e7042ad7fd (diff)
downloadansible-modules-core-c9b17136e404ec8090a1d140f6875b6687720639.tar.gz
Fix a problem introduced with #1101 and optimize privilege handling
* If a db user belonged to a role which had a privilege, the user would not have the privilege added as the role gave the appearance that the user already had it. Fixed to always check the privileges specific to the user. * Make fewer db queries to determine if privileges need to be changed and change them (was four for each privilege. Now two for each object that has a set of privileges changed).
Diffstat (limited to 'database/postgresql/postgresql_user.py')
-rw-r--r--database/postgresql/postgresql_user.py136
1 files changed, 81 insertions, 55 deletions
diff --git a/database/postgresql/postgresql_user.py b/database/postgresql/postgresql_user.py
index 78aab6b8..98f234fc 100644
--- a/database/postgresql/postgresql_user.py
+++ b/database/postgresql/postgresql_user.py
@@ -324,12 +324,21 @@ def user_delete(cursor, user):
cursor.execute("RELEASE SAVEPOINT ansible_pgsql_user_delete")
return True
-def has_table_privilege(cursor, user, table, priv):
- if priv == 'ALL':
- priv = ','.join([ p for p in VALID_PRIVS['table'] if p != 'ALL' ])
- query = 'SELECT has_table_privilege(%s, %s, %s)'
- cursor.execute(query, (user, table, priv))
- return cursor.fetchone()[0]
+def has_table_privileges(cursor, user, table, privs):
+ """
+ Return the difference between the privileges that a user already has and
+ the privileges that they desire to have.
+
+ :returns: tuple of:
+ * privileges that they have and were requested
+ * privileges they currently hold but were not requested
+ * privileges requested that they do not hold
+ """
+ cur_privs = get_table_privileges(cursor, user, table)
+ have_currently = cur_privs.intersection(privs)
+ other_current = cur_privs.difference(privs)
+ desired = privs.difference(cur_privs)
+ return (have_currently, other_current, desired)
def get_table_privileges(cursor, user, table):
if '.' in table:
@@ -339,26 +348,21 @@ def get_table_privileges(cursor, user, table):
query = '''SELECT privilege_type FROM information_schema.role_table_grants
WHERE grantee=%s AND table_name=%s AND table_schema=%s'''
cursor.execute(query, (user, table, schema))
- return set([x[0] for x in cursor.fetchall()])
+ return frozenset([x[0] for x in cursor.fetchall()])
-def grant_table_privilege(cursor, user, table, priv):
+def grant_table_privileges(cursor, user, table, privs):
# Note: priv escaped by parse_privs
- prev_priv = get_table_privileges(cursor, user, table)
+ privs = ', '.join(privs)
query = 'GRANT %s ON TABLE %s TO %s' % (
- priv, pg_quote_identifier(table, 'table'), pg_quote_identifier(user, 'role') )
+ privs, pg_quote_identifier(table, 'table'), pg_quote_identifier(user, 'role') )
cursor.execute(query)
- curr_priv = get_table_privileges(cursor, user, table)
- return len(curr_priv) > len(prev_priv)
-def revoke_table_privilege(cursor, user, table, priv):
+def revoke_table_privileges(cursor, user, table, privs):
# Note: priv escaped by parse_privs
- prev_priv = get_table_privileges(cursor, user, table)
+ privs = ', '.join(privs)
query = 'REVOKE %s ON TABLE %s FROM %s' % (
- priv, pg_quote_identifier(table, 'table'), pg_quote_identifier(user, 'role') )
+ privs, pg_quote_identifier(table, 'table'), pg_quote_identifier(user, 'role') )
cursor.execute(query)
- curr_priv = get_table_privileges(cursor, user, table)
- return len(curr_priv) < len(prev_priv)
-
def get_database_privileges(cursor, user, db):
priv_map = {
@@ -370,80 +374,89 @@ def get_database_privileges(cursor, user, db):
cursor.execute(query, (db,))
datacl = cursor.fetchone()[0]
if datacl is None:
- return []
+ return set()
r = re.search('%s=(C?T?c?)/[a-z]+\,?' % user, datacl)
if r is None:
- return []
- o = []
+ return set()
+ o = set()
for v in r.group(1):
- o.append(priv_map[v])
- return o
+ o.add(priv_map[v])
+ return normalize_privileges(o, 'database')
-def has_database_privilege(cursor, user, db, priv):
- if priv == 'ALL':
- priv = ','.join([ p for p in VALID_PRIVS['database'] if p != 'ALL' ])
- query = 'SELECT has_database_privilege(%s, %s, %s)'
- cursor.execute(query, (user, db, priv))
- return cursor.fetchone()[0]
+def has_database_privileges(cursor, user, db, privs):
+ """
+ Return the difference between the privileges that a user already has and
+ the privileges that they desire to have.
+
+ :returns: tuple of:
+ * privileges that they have and were requested
+ * privileges they currently hold but were not requested
+ * privileges requested that they do not hold
+ """
+ cur_privs = get_database_privileges(cursor, user, db)
+ have_currently = cur_privs.intersection(privs)
+ other_current = cur_privs.difference(privs)
+ desired = privs.difference(cur_privs)
+ return (have_currently, other_current, desired)
-def grant_database_privilege(cursor, user, db, priv):
+def grant_database_privileges(cursor, user, db, privs):
# Note: priv escaped by parse_privs
- prev_priv = get_database_privileges(cursor, user, db)
+ privs =', '.join(privs)
if user == "PUBLIC":
query = 'GRANT %s ON DATABASE %s TO PUBLIC' % (
- priv, pg_quote_identifier(db, 'database'))
+ privs, pg_quote_identifier(db, 'database'))
else:
query = 'GRANT %s ON DATABASE %s TO %s' % (
- priv, pg_quote_identifier(db, 'database'),
+ privs, pg_quote_identifier(db, 'database'),
pg_quote_identifier(user, 'role'))
cursor.execute(query)
- curr_priv = get_database_privileges(cursor, user, db)
- return len(curr_priv) > len(prev_priv)
-def revoke_database_privilege(cursor, user, db, priv):
+def revoke_database_privileges(cursor, user, db, privs):
# Note: priv escaped by parse_privs
- prev_priv = get_database_privileges(cursor, user, db)
+ privs = ', '.join(privs)
if user == "PUBLIC":
query = 'REVOKE %s ON DATABASE %s FROM PUBLIC' % (
- priv, pg_quote_identifier(db, 'database'))
+ privs, pg_quote_identifier(db, 'database'))
else:
query = 'REVOKE %s ON DATABASE %s FROM %s' % (
- priv, pg_quote_identifier(db, 'database'),
+ privs, pg_quote_identifier(db, 'database'),
pg_quote_identifier(user, 'role'))
cursor.execute(query)
- curr_priv = get_database_privileges(cursor, user, db)
- return len(curr_priv) < len(prev_priv)
def revoke_privileges(cursor, user, privs):
if privs is None:
return False
+ revoke_funcs = dict(table=revoke_table_privileges, database=revoke_database_privileges)
+ check_funcs = dict(table=has_table_privileges, database=has_database_privileges)
+
changed = False
- revoke_funcs = dict(table=revoke_table_privilege, database=revoke_database_privilege)
- check_funcs = dict(table=has_table_privilege, database=has_database_privilege)
for type_ in privs:
for name, privileges in privs[type_].iteritems():
- for privilege in privileges:
- if check_funcs[type_](cursor, user, name, privilege):
- changed = revoke_funcs[type_](cursor, user, name, privilege)\
- or changed
-
+ # Check that any of the privileges requested to be removed are
+ # currently granted to the user
+ differences = check_funcs[type_](cursor, user, name, privileges)
+ if differences[0]:
+ revoke_funcs[type_](cursor, user, name, privileges)
+ changed = True
return changed
def grant_privileges(cursor, user, privs):
if privs is None:
return False
- grant_funcs = dict(table=grant_table_privilege, database=grant_database_privilege)
- check_funcs = dict(table=has_table_privilege, database=has_database_privilege)
+
+ grant_funcs = dict(table=grant_table_privileges, database=grant_database_privileges)
+ check_funcs = dict(table=has_table_privileges, database=has_database_privileges)
changed = False
for type_ in privs:
for name, privileges in privs[type_].iteritems():
- for privilege in privileges:
- if not check_funcs[type_](cursor, user, name, privilege):
- changed = grant_funcs[type_](cursor, user, name, privilege)\
- or changed
-
+ # Check that any of the privileges requested for the user are
+ # currently missing
+ differences = check_funcs[type_](cursor, user, name, privileges)
+ if differences[2]:
+ grant_funcs[type_](cursor, user, name, privileges)
+ changed = True
return changed
def parse_role_attrs(role_attr_flags):
@@ -472,6 +485,17 @@ def parse_role_attrs(role_attr_flags):
o_flags = ' '.join(flag_set)
return o_flags
+def normalize_privileges(privs, type_):
+ new_privs = set(privs)
+ if 'ALL' in privs:
+ new_privs.update(VALID_PRIVS[type_])
+ new_privs.remove('ALL')
+ if 'TEMP' in privs:
+ new_privs.add('TEMPORARY')
+ new_privs.remove('TEMP')
+
+ return new_privs
+
def parse_privs(privs, db):
"""
Parse privilege string to determine permissions for database db.
@@ -504,6 +528,8 @@ def parse_privs(privs, db):
if not priv_set.issubset(VALID_PRIVS[type_]):
raise InvalidPrivsError('Invalid privs specified for %s: %s' %
(type_, ' '.join(priv_set.difference(VALID_PRIVS[type_]))))
+
+ priv_set = normalize_privileges(priv_set, type_)
o_privs[type_][name] = priv_set
return o_privs