Merge pull request #30053 from akissa/add-postgres-privileges-support

Add postgres privileges support
This commit is contained in:
Mike Place 2015-12-29 09:50:44 -07:00
commit e915f4b591
5 changed files with 1588 additions and 0 deletions

View File

@ -0,0 +1,6 @@
===============================
salt.states.postgres_privileges
===============================
.. automodule:: salt.states.postgres_privileges
:members:

View File

@ -31,6 +31,7 @@ import distutils.version # pylint: disable=import-error,no-name-in-module
import logging
import hashlib
import os
import re
import tempfile
try:
import csv
@ -41,6 +42,7 @@ except ImportError:
# Import salt libs
import salt.utils
import salt.utils.itertools
from salt.exceptions import SaltInvocationError
# Import 3rd-party libs
import salt.ext.six as six
@ -62,6 +64,40 @@ _EXTENSION_FLAGS = (
_EXTENSION_TO_UPGRADE,
_EXTENSION_TO_MOVE,
)
_PRIVILEGES_MAP = {
'a': 'INSERT',
'C': 'CREATE',
'D': 'TRUNCATE',
'c': 'CONNECT',
't': 'TRIGGER',
'r': 'SELECT',
'U': 'USAGE',
'T': 'TEMPORARY',
'w': 'UPDATE',
'X': 'EXECUTE',
'x': 'REFERENCES',
'd': 'DELETE',
'*': 'GRANT',
}
_PRIVILEGES_OBJECTS = frozenset(
(
'schema',
'tablespace',
'language',
'sequence',
'table',
'group',
'database',
)
)
_PRIVILEGE_TYPE_MAP = {
'table': 'arwdDxt',
'tablespace': 'C',
'language': 'U',
'sequence': 'rwU',
'schema': 'UC',
'database': 'CTc',
}
def __virtual__():
@ -2204,3 +2240,666 @@ def language_remove(name,
password=password)
return ret['retcode'] == 0
def _make_privileges_list_query(name, object_type, prepend):
'''
Generate the SQL required for specific object type
'''
if object_type == 'table':
query = (' '.join([
'SELECT relacl AS name',
'FROM pg_catalog.pg_class c',
'JOIN pg_catalog.pg_namespace n',
'ON n.oid = c.relnamespace',
"WHERE nspname = '{0}'",
"AND relname = '{1}'",
"AND relkind = 'r'",
'ORDER BY relname',
])).format(prepend, name)
elif object_type == 'sequence':
query = (' '.join([
'SELECT relacl AS name',
'FROM pg_catalog.pg_class c',
'JOIN pg_catalog.pg_namespace n',
'ON n.oid = c.relnamespace',
"WHERE nspname = '{0}'",
"AND relname = '{1}'",
"AND relkind = 'S'",
'ORDER BY relname',
])).format(prepend, name)
elif object_type == 'schema':
query = (' '.join([
'SELECT nspacl AS name',
'FROM pg_catalog.pg_namespace',
"WHERE nspname = '{0}'",
'ORDER BY nspname',
])).format(name)
# elif object_type == 'function':
# query = (' '.join([
# 'SELECT proacl AS name',
# 'FROM pg_catalog.pg_proc p',
# 'JOIN pg_catalog.pg_namespace n',
# 'ON n.oid = p.pronamespace',
# "WHERE nspname = '{0}'",
# "AND proname = '{1}'",
# 'ORDER BY proname, proargtypes',
# ])).format(prepend, name)
elif object_type == 'tablespace':
query = (' '.join([
'SELECT spcacl AS name',
'FROM pg_catalog.pg_tablespace',
"WHERE spcname = '{0}'",
'ORDER BY spcname',
])).format(name)
elif object_type == 'language':
query = (' '.join([
'SELECT lanacl AS name',
'FROM pg_catalog.pg_language',
"WHERE lanname = '{0}'",
'ORDER BY lanname',
])).format(name)
elif object_type == 'database':
query = (' '.join([
'SELECT datacl AS name',
'FROM pg_catalog.pg_database',
"WHERE datname = '{0}'",
'ORDER BY datname',
])).format(name)
elif object_type == 'group':
query = (' '.join([
'SELECT rolname, admin_option',
'FROM pg_catalog.pg_auth_members m',
'JOIN pg_catalog.pg_roles r',
'ON m.member=r.oid',
'WHERE m.roleid IN',
'(SELECT oid',
'FROM pg_catalog.pg_roles',
"WHERE rolname='{0}')",
'ORDER BY rolname',
])).format(name)
return query
def _get_object_owner(name,
object_type,
prepend='public',
maintenance_db=None,
user=None,
host=None,
port=None,
password=None,
runas=None):
'''
Return the owner of a postgres object
'''
if object_type == 'table':
query = (' '.join([
'SELECT tableowner AS name',
'FROM pg_tables',
"WHERE schemaname = '{0}'",
"AND tablename = '{1}'"
])).format(prepend, name)
elif object_type == 'sequence':
query = (' '.join([
'SELECT rolname AS name',
'FROM pg_catalog.pg_class c',
'JOIN pg_roles r',
'ON c.relowner = r.oid',
'JOIN pg_catalog.pg_namespace n',
'ON n.oid = c.relnamespace',
"WHERE relkind='S'",
"AND nspname='{0}'",
"AND relname = '{1}'",
])).format(prepend, name)
elif object_type == 'schema':
query = (' '.join([
'SELECT rolname AS name',
'FROM pg_namespace n',
'JOIN pg_roles r',
'ON n.nspowner = r.oid',
"WHERE nspname = '{0}'",
])).format(name)
elif object_type == 'tablespace':
query = (' '.join([
'SELECT rolname AS name',
'FROM pg_tablespace t',
'JOIN pg_roles r',
'ON t.spcowner = r.oid',
"WHERE spcname = '{0}'",
])).format(name)
elif object_type == 'language':
query = (' '.join([
'SELECT rolname AS name',
'FROM pg_language l',
'JOIN pg_roles r',
'ON l.lanowner = r.oid',
"WHERE lanname = '{0}'",
])).format(name)
elif object_type == 'database':
query = (' '.join([
'SELECT rolname AS name',
'FROM pg_database d',
'JOIN pg_roles r',
'ON d.datdba = r.oid',
"WHERE datname = '{0}'",
])).format(name)
rows = psql_query(
query,
runas=runas,
host=host,
user=user,
port=port,
maintenance_db=maintenance_db,
password=password)
try:
ret = rows[0]['name']
except IndexError:
ret = None
return ret
def _validate_privileges(object_type, privs, privileges):
'''
Validate the supplied privileges
'''
if object_type != 'group':
_perms = [_PRIVILEGES_MAP[perm]
for perm in _PRIVILEGE_TYPE_MAP[object_type]]
_perms.append('ALL')
if object_type not in _PRIVILEGES_OBJECTS:
raise SaltInvocationError(
'Invalid object_type: {0} provided'.format(object_type))
if not set(privs).issubset(set(_perms)):
raise SaltInvocationError(
'Invalid privilege(s): {0} provided for object {1}'.format(
privileges, object_type))
else:
if privileges:
raise SaltInvocationError(
'The privileges option should not '
'be set for object_type group')
def _mod_priv_opts(object_type, privileges):
'''
Format options
'''
object_type = object_type.lower()
privileges = '' if privileges is None else privileges
_privs = re.split(r'\s?,\s?', privileges.upper())
return object_type, privileges, _privs
def _process_priv_part(perms):
'''
Process part
'''
_tmp = {}
previous = None
for perm in perms:
if previous is None:
_tmp[_PRIVILEGES_MAP[perm]] = False
previous = _PRIVILEGES_MAP[perm]
else:
if perm == '*':
_tmp[previous] = True
else:
_tmp[_PRIVILEGES_MAP[perm]] = False
previous = _PRIVILEGES_MAP[perm]
return _tmp
def privileges_list(
name,
object_type,
prepend='public',
maintenance_db=None,
user=None,
host=None,
port=None,
password=None,
runas=None):
'''
.. versionadded:: Boron
Return a list of privileges for the specified object.
CLI Example:
.. code-block:: bash
salt '*' postgres.privileges_list table_name table maintenance_db=db_name
name
Name of the object for which the permissions should be returned
object_type
The object type, which can be one of the following:
- table
- sequence
- schema
- tablespace
- language
- database
- group
prepend
Table and Sequence object types live under a schema so this should be
provided if the object is not under the default `public` schema
maintenance_db
The database to connect to
user
database username if different from config or default
password
user password if any password for a specified user
host
Database host if different from config or default
port
Database port if different from config or default
runas
System user all operations should be performed on behalf of
'''
object_type = object_type.lower()
query = _make_privileges_list_query(name, object_type, prepend)
if object_type not in _PRIVILEGES_OBJECTS:
raise SaltInvocationError(
'Invalid object_type: {0} provided'.format(object_type))
rows = psql_query(
query,
runas=runas,
host=host,
user=user,
port=port,
maintenance_db=maintenance_db,
password=password)
ret = {}
for row in rows:
if object_type != 'group':
result = row['name']
result = result.strip('{}')
parts = result.split(',')
for part in parts:
perms_part, _ = part.split('/')
rolename, perms = perms_part.split('=')
if rolename == '':
rolename = 'public'
_tmp = _process_priv_part(perms)
ret[rolename] = _tmp
else:
if row['admin_option'] == 't':
admin_option = True
else:
admin_option = False
ret[row['rolname']] = admin_option
return ret
def has_privileges(name,
object_name,
object_type,
privileges=None,
grant_option=None,
prepend='public',
maintenance_db=None,
user=None,
host=None,
port=None,
password=None,
runas=None):
'''
.. versionadded:: Boron
Check if a role has the specified privileges on an object
CLI Example:
.. code-block:: bash
salt '*' postgres.has_privileges user_name table_name table \
SELECT,INSERT maintenance_db=db_name
name
Name of the role whose privilages should be checked on object_type
object_name
Name of the object on which the check is to be performed
object_type
The object type, which can be one of the following:
- table
- sequence
- schema
- tablespace
- language
- database
- group
privileges
Comma separated list of privilages to check, from the list below:
- INSERT
- CREATE
- TRUNCATE
- CONNECT
- TRIGGER
- SELECT
- USAGE
- TEMPORARY
- UPDATE
- EXECUTE
- REFERENCES
- DELETE
- ALL
grant_option
If grant_option is set to True, the grant option check is performed
prepend
Table and Sequence object types live under a schema so this should be
provided if the object is not under the default `public` schema
maintenance_db
The database to connect to
user
database username if different from config or default
password
user password if any password for a specified user
host
Database host if different from config or default
port
Database port if different from config or default
runas
System user all operations should be performed on behalf of
'''
object_type, privileges, _privs = _mod_priv_opts(object_type, privileges)
_validate_privileges(object_type, _privs, privileges)
if object_type != 'group':
owner = _get_object_owner(object_name, object_type, prepend=prepend,
maintenance_db=maintenance_db, user=user, host=host, port=port,
password=password, runas=runas)
if owner is not None and name == owner:
return True
_privileges = privileges_list(object_name, object_type, prepend=prepend,
maintenance_db=maintenance_db, user=user, host=host, port=port,
password=password, runas=runas)
if name in _privileges:
if object_type == 'group':
if grant_option:
retval = _privileges[name]
else:
retval = True
return retval
else:
_perms = _PRIVILEGE_TYPE_MAP[object_type]
if grant_option:
perms = dict((_PRIVILEGES_MAP[perm], True) for perm in _perms)
retval = perms == _privileges[name]
else:
perms = [_PRIVILEGES_MAP[perm] for perm in _perms]
if 'ALL' in _privs:
retval = perms.sort() == _privileges[name].keys().sort()
else:
retval = set(_privs).issubset(
set(_privileges[name].keys()))
return retval
return False
def privileges_grant(name,
object_name,
object_type,
privileges=None,
grant_option=None,
prepend='public',
maintenance_db=None,
user=None,
host=None,
port=None,
password=None,
runas=None):
'''
.. versionadded:: Boron
Grant privileges on a postgres object
CLI Example:
.. code-block:: bash
salt '*' postgres.privileges_grant user_name table_name table \
SELECT,UPDATE maintenance_db=db_name
name
Name of the role to which privilages should be granted
object_name
Name of the object on which the grant is to be performed
object_type
The object type, which can be one of the following:
- table
- sequence
- schema
- tablespace
- language
- database
- group
privileges
Comma separated list of privilages to grant, from the list below:
- INSERT
- CREATE
- TRUNCATE
- CONNECT
- TRIGGER
- SELECT
- USAGE
- TEMPORARY
- UPDATE
- EXECUTE
- REFERENCES
- DELETE
- ALL
grant_option
If grant_option is set to True, the recipient of the privilege can
in turn grant it to others
prepend
Table and Sequence object types live under a schema so this should be
provided if the object is not under the default `public` schema
maintenance_db
The database to connect to
user
database username if different from config or default
password
user password if any password for a specified user
host
Database host if different from config or default
port
Database port if different from config or default
runas
System user all operations should be performed on behalf of
'''
object_type, privileges, _privs = _mod_priv_opts(object_type, privileges)
_validate_privileges(object_type, _privs, privileges)
if has_privileges(name, object_name, object_type, privileges,
prepend=prepend, maintenance_db=maintenance_db, user=user,
host=host, port=port, password=password, runas=runas):
log.info('The object: %s of type: %s already has privileges: %s set',
object_name, object_type, privileges)
return False
_grants = ','.join(_privs)
if object_type in ['table', 'sequence']:
on_part = '{0}.{1}'.format(prepend, object_name)
else:
on_part = object_name
if grant_option:
if object_type == 'group':
query = 'GRANT {0} TO {1} WITH ADMIN OPTION'.format(
object_name, name)
else:
query = 'GRANT {0} ON {1} {2} TO {3} WITH GRANT OPTION'.format(
_grants, object_type.upper(), on_part, name)
else:
if object_type == 'group':
query = 'GRANT {0} TO {1}'.format(object_name, name)
else:
query = 'GRANT {0} ON {1} {2} TO {3}'.format(
_grants, object_type.upper(), on_part, name)
ret = _psql_prepare_and_run(['-c', query],
user=user,
host=host,
port=port,
maintenance_db=maintenance_db,
password=password,
runas=runas)
return ret['retcode'] == 0
def privileges_revoke(name,
object_name,
object_type,
privileges=None,
prepend='public',
maintenance_db=None,
user=None,
host=None,
port=None,
password=None,
runas=None):
'''
.. versionadded:: Boron
Revoke privileges on a postgres object
CLI Example:
.. code-block:: bash
salt '*' postgres.privileges_revoke user_name table_name table \
SELECT,UPDATE maintenance_db=db_name
name
Name of the role whose privilages should be revoked
object_name
Name of the object on which the revoke is to be performed
object_type
The object type, which can be one of the following:
- table
- sequence
- schema
- tablespace
- language
- database
- group
privileges
Comma separated list of privilages to revoke, from the list below:
- INSERT
- CREATE
- TRUNCATE
- CONNECT
- TRIGGER
- SELECT
- USAGE
- TEMPORARY
- UPDATE
- EXECUTE
- REFERENCES
- DELETE
- ALL
maintenance_db
The database to connect to
user
database username if different from config or default
password
user password if any password for a specified user
host
Database host if different from config or default
port
Database port if different from config or default
runas
System user all operations should be performed on behalf of
'''
object_type, privileges, _privs = _mod_priv_opts(object_type, privileges)
_validate_privileges(object_type, _privs, privileges)
if not has_privileges(name, object_name, object_type, privileges,
prepend=prepend, maintenance_db=maintenance_db, user=user,
host=host, port=port, password=password, runas=runas):
log.info('The object: %s of type: %s does not'
' have privileges: %s set', object_name, object_type, privileges)
return False
_grants = ','.join(_privs)
if object_type in ['table', 'sequence']:
on_part = '{0}.{1}'.format(prepend, object_name)
else:
on_part = object_name
if object_type == 'group':
query = 'REVOKE {0} FROM {1}'.format(object_name, name)
else:
query = 'REVOKE {0} ON {1} {2} FROM {3}'.format(
_grants, object_type.upper(), on_part, name)
ret = _psql_prepare_and_run(['-c', query],
user=user,
host=host,
port=port,
maintenance_db=maintenance_db,
password=password,
runas=runas)
return ret['retcode'] == 0

View File

@ -0,0 +1,307 @@
# -*- coding: utf-8 -*-
'''
Management of PostgreSQL Privileges
===================================
The postgres_privileges module is used to manage Postgres privileges.
Privileges can be set as either absent or present.
Privileges can be set on the following database object types:
* database
* schema
* tablespace
* table
* sequence
* language
* group
Setting the grant option is supported as well.
.. versionadded:: Boron
.. code-block:: yaml
baruwa:
postgres_privileges.present:
- object_name: awl
- object_type: table
- privileges:
- SELECT
- INSERT
- DELETE
- grant_option: False
- prepend: public
- maintenance_db: testdb
.. code-block:: yaml
andrew:
postgres_privileges.present:
- object_name: admins
- object_type: group
- grant_option: False
- maintenance_db: testdb
.. code-block:: yaml
baruwa:
postgres_privileges.absent:
- object_name: awl
- object_type: table
- privileges:
- SELECT
- INSERT
- DELETE
- prepend: public
- maintenance_db: testdb
.. code-block:: yaml
andrew:
postgres_privileges.absent:
- object_name: admins
- object_type: group
- maintenance_db: testdb
'''
from __future__ import absolute_import
def __virtual__():
'''
Only load if the postgres module is present
'''
return 'postgres.privileges_grant' in __salt__
def present(name,
object_name,
object_type,
privileges=None,
grant_option=None,
prepend=None,
maintenance_db=None,
user=None,
db_password=None,
db_host=None,
db_port=None,
db_user=None):
'''
Grant the requested privilege(s) on the specified object to a role
name
Name of the role to which privilages should be granted
object_name
Name of the object on which the grant is to be performed
object_type
The object type, which can be one of the following:
- table
- sequence
- schema
- tablespace
- language
- database
- group
privileges
Comma separated list of privilages to grant, from the list below:
- INSERT
- CREATE
- TRUNCATE
- CONNECT
- TRIGGER
- SELECT
- USAGE
- TEMPORARY
- UPDATE
- EXECUTE
- REFERENCES
- DELETE
- ALL
:note: privileges should not be set when granting group membership
grant_option
If grant_option is set to True, the recipient of the privilege can
in turn grant it to others
prepend
Table and Sequence object types live under a schema so this should be
provided if the object is not under the default `public` schema
maintenance_db
The name of the database in which the language is to be installed
user
System user all operations should be performed on behalf of
db_user
database username if different from config or default
db_password
user password if any password for a specified user
db_host
Database host if different from config or default
db_port
Database port if different from config or default
'''
ret = {
'name': name,
'changes': {},
'result': True,
'comment': 'The requested privilege(s) are already set'
}
privileges = ','.join(privileges) if privileges else None
kwargs = {
'privileges': privileges,
'grant_option': grant_option,
'prepend': prepend,
'maintenance_db': maintenance_db,
'runas': user,
'host': db_host,
'user': db_user,
'port': db_port,
'password': db_password,
}
if not __salt__['postgres.has_privileges'](
name, object_name, object_type, **kwargs):
_privs = object_name if object_type == 'group' else privileges
if __opts__['test']:
ret['result'] = None
ret['comment'] = ('The privilege(s): {0} are'
' set to be granted to {1}').format(_privs, name)
return ret
if __salt__['postgres.privileges_grant'](
name, object_name, object_type, **kwargs):
ret['comment'] = ('The privilege(s): {0} have '
'been granted to {1}').format(_privs, name)
ret['changes'][name] = 'Present'
else:
ret['comment'] = ('Failed to grant privilege(s):'
' {0} to {1}').format(_privs, name)
ret['result'] = False
return ret
def absent(name,
object_name,
object_type,
privileges=None,
prepend=None,
maintenance_db=None,
user=None,
db_password=None,
db_host=None,
db_port=None,
db_user=None):
'''
Revoke the requested privilege(s) on the specificed object(s)
name
Name of the role whose privilages should be revoked
object_name
Name of the object on which the revoke is to be performed
object_type
The object type, which can be one of the following:
- table
- sequence
- schema
- tablespace
- language
- database
- group
privileges
Comma separated list of privilages to revoke, from the list below:
- INSERT
- CREATE
- TRUNCATE
- CONNECT
- TRIGGER
- SELECT
- USAGE
- TEMPORARY
- UPDATE
- EXECUTE
- REFERENCES
- DELETE
- ALL
NOTE: privileges should not be set when revoking group membership
prepend
Table and Sequence object types live under a schema so this should be
provided if the object is not under the default `public` schema
maintenance_db
The name of the database in which the language is to be installed
user
System user all operations should be performed on behalf of
db_user
database username if different from config or default
db_password
user password if any password for a specified user
db_host
Database host if different from config or default
db_port
Database port if different from config or default
'''
ret = {
'name': name,
'changes': {},
'result': True,
'comment': ('The requested privilege(s) are '
'not set so cannot be revoked')
}
privileges = ','.join(privileges) if privileges else None
kwargs = {
'privileges': privileges,
'prepend': prepend,
'maintenance_db': maintenance_db,
'runas': user,
'host': db_host,
'user': db_user,
'port': db_port,
'password': db_password,
}
if __salt__['postgres.has_privileges'](
name, object_name, object_type, **kwargs):
_privs = object_name if object_type == 'group' else privileges
if __opts__['test']:
ret['result'] = None
ret['comment'] = ('The privilege(s): {0} are'
' set to be revoked from {1}').format(_privs, name)
return ret
if __salt__['postgres.privileges_revoke'](
name, object_name, object_type, **kwargs):
ret['comment'] = ('The privilege(s): {0} have '
'been revoked from {1}').format(_privs, name)
ret['changes'][name] = 'Absent'
else:
ret['comment'] = ('Failed to revoke privilege(s):'
' {0} from {1}').format(_privs, name)
ret['result'] = False
return ret

View File

@ -41,6 +41,18 @@ test_list_language_csv = (
'plpgsql\n'
)
test_privileges_list_table_csv = (
'name\n'
'"{baruwatest=arwdDxt/baruwatest,bayestest=arwd/baruwatest,baruwa=a*r*w*d*D*x*t*/baruwatest}"\n'
)
test_privileges_list_group_csv = (
'rolname,admin_option\n'
'baruwa,f\n'
'baruwatest2,t\n'
'baruwatest,f\n'
)
if NO_MOCK is False:
SALT_STUB = {
@ -989,6 +1001,400 @@ class PostgresTestCase(TestCase):
)
self.assertFalse(ret)
@patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0,
'stdout': test_privileges_list_table_csv}))
def test_privileges_list_table(self):
'''
Test privilege listing on a table
'''
ret = postgres.privileges_list(
'awl',
'table',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
expected = {
"bayestest": {
"INSERT": False,
"UPDATE": False,
"SELECT": False,
"DELETE": False,
},
"baruwa": {
"INSERT": True,
"TRUNCATE": True,
"UPDATE": True,
"TRIGGER": True,
"REFERENCES": True,
"SELECT": True,
"DELETE": True,
},
"baruwatest": {
"INSERT": False,
"TRUNCATE": False,
"UPDATE": False,
"TRIGGER": False,
"REFERENCES": False,
"SELECT": False,
"DELETE": False,
},
}
self.assertDictEqual(ret, expected)
query = ("COPY (SELECT relacl AS name FROM pg_catalog.pg_class c "
"JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
"WHERE nspname = 'public' AND relname = 'awl' AND relkind = 'r' "
"ORDER BY relname) TO STDOUT WITH CSV HEADER")
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-v', 'datestyle=ISO,MDY', '-c', query],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
@patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0,
'stdout': test_privileges_list_group_csv}))
def test_privileges_list_group(self):
'''
Test privilege listing on a group
'''
ret = postgres.privileges_list(
'admin',
'group',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
expected = {
'baruwa': False,
'baruwatest': False,
'baruwatest2': True,
}
self.assertDictEqual(ret, expected)
query = ("COPY (SELECT rolname, admin_option "
"FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles r "
"ON m.member=r.oid WHERE m.roleid IN (SELECT oid FROM "
"pg_catalog.pg_roles WHERE rolname='admin') ORDER BY rolname) "
"TO STDOUT WITH CSV HEADER")
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-v', 'datestyle=ISO,MDY', '-c', query],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
@patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0,
'stdout': test_privileges_list_table_csv}))
def test_has_privileges_on_table(self):
'''
Test privilege checks on table
'''
ret = postgres.has_privileges(
'baruwa',
'awl',
'table',
'SELECT,INSERT',
grant_option=True,
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
self.assertTrue(ret)
ret = postgres.has_privileges(
'baruwa',
'awl',
'table',
'ALL',
grant_option=True,
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
self.assertTrue(ret)
ret = postgres.has_privileges(
'bayestest',
'awl',
'table',
'SELECT,INSERT,TRUNCATE',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
self.assertFalse(ret)
ret = postgres.has_privileges(
'bayestest',
'awl',
'table',
'SELECT,INSERT',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
self.assertTrue(ret)
@patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0,
'stdout': test_privileges_list_group_csv}))
def test_has_privileges_on_group(self):
'''
Test privilege checks on group
'''
ret = postgres.has_privileges(
'baruwa',
'admin',
'group',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
self.assertTrue(ret)
ret = postgres.has_privileges(
'baruwa',
'admin',
'group',
grant_option=True,
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
self.assertFalse(ret)
ret = postgres.has_privileges(
'tony',
'admin',
'group',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
self.assertFalse(ret)
def test_privileges_grant_table(self):
'''
Test granting privileges on table
'''
with patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0})):
with patch('salt.modules.postgres.has_privileges',
Mock(return_value=False)):
ret = postgres.privileges_grant(
'baruwa',
'awl',
'table',
'ALL',
grant_option=True,
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
query = 'GRANT ALL ON TABLE public.awl TO baruwa WITH GRANT OPTION'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
with patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0})):
with patch('salt.modules.postgres.has_privileges',
Mock(return_value=False)):
ret = postgres.privileges_grant(
'baruwa',
'awl',
'table',
'ALL',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
query = 'GRANT ALL ON TABLE public.awl TO baruwa'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
def test_privileges_grant_group(self):
'''
Test granting privileges on group
'''
with patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0})):
with patch('salt.modules.postgres.has_privileges',
Mock(return_value=False)):
ret = postgres.privileges_grant(
'baruwa',
'admins',
'group',
grant_option=True,
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
query = 'GRANT admins TO baruwa WITH ADMIN OPTION'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
with patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0})):
with patch('salt.modules.postgres.has_privileges',
Mock(return_value=False)):
ret = postgres.privileges_grant(
'baruwa',
'admins',
'group',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
query = 'GRANT admins TO baruwa'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
def test_privileges_revoke_table(self):
'''
Test revoking privileges on table
'''
with patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0})):
with patch('salt.modules.postgres.has_privileges',
Mock(return_value=True)):
ret = postgres.privileges_revoke(
'baruwa',
'awl',
'table',
'ALL',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
query = 'REVOKE ALL ON TABLE public.awl FROM baruwa'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
def test_privileges_revoke_group(self):
'''
Test revoking privileges on group
'''
with patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0})):
with patch('salt.modules.postgres.has_privileges',
Mock(return_value=True)):
ret = postgres.privileges_revoke(
'baruwa',
'admins',
'group',
maintenance_db='db_name',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
query = 'REVOKE admins FROM baruwa'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
if __name__ == '__main__':
from integration import run_tests
run_tests(PostgresTestCase, needs_daemon=False)

View File

@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Andrew Colin Kissa <andrew@topdog.za.net>`
'''
from __future__ import absolute_import
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
from salt.states import postgres_privileges
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PostgresPrivilegesTestCase(TestCase):
'''
Test cases for salt.states.postgres_privileges
'''
def setUp(self):
'''
Setup data for the tests
'''
postgres_privileges.__opts__ = {}
postgres_privileges.__salt__ = {}
self.table_name = 'awl'
self.group_name = 'admins'
self.name = 'baruwa'
self.ret = {'name': self.name,
'changes': {},
'result': False,
'comment': ''}
self.mock_true = MagicMock(return_value=True)
self.mock_false = MagicMock(return_value=False)
def test_present_table(self):
'''
Test present
'''
with patch.dict(postgres_privileges.__salt__,
{'postgres.has_privileges': self.mock_true}):
comt = 'The requested privilege(s) are already set'
self.ret.update({'comment': comt, 'result': True})
self.assertDictEqual(
postgres_privileges.present(
self.name,
self.table_name,
'table'),
self.ret)
with patch.dict(postgres_privileges.__salt__,
{'postgres.has_privileges': self.mock_false,
'postgres.privileges_grant': self.mock_true}):
with patch.dict(postgres_privileges.__opts__, {'test': True}):
comt = ('The privilege(s): {0} are'
' set to be granted to {1}').format('ALL', self.name)
self.ret.update({'comment': comt, 'result': None})
self.assertDictEqual(
postgres_privileges.present(self.name,
self.table_name, 'table', privileges=['ALL']), self.ret)
with patch.dict(postgres_privileges.__opts__, {'test': False}):
comt = ('The privilege(s): {0} have '
'been granted to {1}').format('ALL', self.name)
self.ret.update({'comment': comt,
'result': True,
'changes': {'baruwa': 'Present'}})
self.assertDictEqual(
postgres_privileges.present(self.name,
self.table_name, 'table', privileges=['ALL']), self.ret)
def test_present_group(self):
'''
Test present group
'''
with patch.dict(postgres_privileges.__salt__,
{'postgres.has_privileges': self.mock_false,
'postgres.privileges_grant': self.mock_true}):
with patch.dict(postgres_privileges.__opts__, {'test': True}):
comt = ('The privilege(s): {0} are'
' set to be granted to {1}').format(self.group_name,
self.name)
self.ret.update({'comment': comt, 'result': None})
self.assertDictEqual(
postgres_privileges.present(self.name,
self.group_name, 'group'), self.ret)
with patch.dict(postgres_privileges.__opts__, {'test': False}):
comt = ('The privilege(s): {0} have '
'been granted to {1}').format(self.group_name,
self.name)
self.ret.update({'comment': comt,
'result': True,
'changes': {'baruwa': 'Present'}})
self.assertDictEqual(
postgres_privileges.present(self.name,
self.group_name, 'group'), self.ret)
def test_absent_table(self):
'''
Test absent
'''
with patch.dict(postgres_privileges.__salt__,
{'postgres.has_privileges': self.mock_false}):
with patch.dict(postgres_privileges.__opts__, {'test': True}):
comt = ('The requested privilege(s)'
' are not set so cannot be revoked')
self.ret.update({'comment': comt, 'result': True})
self.assertDictEqual(
postgres_privileges.absent(
self.name,
self.table_name,
'table'),
self.ret)
with patch.dict(postgres_privileges.__salt__,
{'postgres.has_privileges': self.mock_true,
'postgres.privileges_revoke': self.mock_true}):
with patch.dict(postgres_privileges.__opts__, {'test': True}):
comt = ('The privilege(s): {0} are'
' set to be revoked from {1}').format('ALL', self.name)
self.ret.update({'comment': comt, 'result': None})
self.assertDictEqual(
postgres_privileges.absent(self.name,
self.table_name, 'table', privileges=['ALL']), self.ret)
with patch.dict(postgres_privileges.__opts__, {'test': False}):
comt = ('The privilege(s): {0} have '
'been revoked from {1}').format('ALL', self.name)
self.ret.update({'comment': comt,
'result': True,
'changes': {'baruwa': 'Absent'}})
self.assertDictEqual(
postgres_privileges.absent(self.name,
self.table_name, 'table', privileges=['ALL']), self.ret)
def test_absent_group(self):
'''
Test absent group
'''
with patch.dict(postgres_privileges.__salt__,
{'postgres.has_privileges': self.mock_true,
'postgres.privileges_revoke': self.mock_true}):
with patch.dict(postgres_privileges.__opts__, {'test': True}):
comt = ('The privilege(s): {0} are'
' set to be revoked from {1}').format(self.group_name,
self.name)
self.ret.update({'comment': comt, 'result': None})
self.assertDictEqual(
postgres_privileges.absent(self.name,
self.group_name, 'group'), self.ret)
with patch.dict(postgres_privileges.__opts__, {'test': False}):
comt = ('The privilege(s): {0} have '
'been revoked from {1}').format(self.group_name,
self.name)
self.ret.update({'comment': comt,
'result': True,
'changes': {'baruwa': 'Absent'}})
self.assertDictEqual(
postgres_privileges.absent(self.name,
self.group_name, 'group'), self.ret)