mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Merge pull request #1339 from inertialbit/add-basic-pg-role-attribute-support
add role_attr_flags parameter to postgresql_user
This commit is contained in:
commit
3c739b1a88
2 changed files with 66 additions and 19 deletions
|
@ -36,3 +36,6 @@
|
|||
|
||||
- name: ensure user has access to database
|
||||
action: postgresql_user db=$dbname user=$dbuser password=$dbpassword priv=ALL
|
||||
|
||||
- name: ensure user does not have unnecessary privilege
|
||||
action: postgresql_user user=$dbuser role_attr_flags=NOSUPERUSER,NOCREATEDB
|
|
@ -77,6 +77,13 @@ options:
|
|||
- "PostgreSQL privileges string in the format: C(table:priv1,priv2)"
|
||||
required: false
|
||||
default: null
|
||||
role_attr_flags:
|
||||
description:
|
||||
- "PostgreSQL role attributes string in the format: CREATEDB,CREATEROLE,SUPERUSER"
|
||||
required: false
|
||||
default: null
|
||||
choices: [ "[NO]SUPERUSER","[NO]CREATEROLE", "[NO]CREATEUSER", "[NO]CREATEDB",
|
||||
"[NO]INHERIT", "[NO]LOGIN", "[NO]REPLICATION" ]
|
||||
state:
|
||||
description:
|
||||
- The database state
|
||||
|
@ -86,6 +93,8 @@ options:
|
|||
examples:
|
||||
- code: postgresql_user db=acme user=django password=ceec4eif7ya priv=CONNECT/products:ALL
|
||||
description: Create django user and grant access to database and products table
|
||||
- code: postgresql_user user=rails password=secret role_attr_flags=CREATEDB,NOSUPERUSER
|
||||
- description: Create rails user, grant privilege to create other databases and demote rails from super user status
|
||||
- code: postgresql_user db=acme user=test priv=ALL/products:ALL state=absent fail_on_user=no
|
||||
description: Remove test user privileges from acme
|
||||
- code: postgresql_user db=test user=test priv=ALL state=absent
|
||||
|
@ -125,29 +134,45 @@ def user_exists(cursor, user):
|
|||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
def user_add(cursor, user, password):
|
||||
def user_add(cursor, user, password, role_attr_flags):
|
||||
"""Create a new user with write access to the database"""
|
||||
query = "CREATE USER %(user)s with PASSWORD '%(password)s'"
|
||||
cursor.execute(query % {"user": user, "password": password})
|
||||
query = "CREATE USER %(user)s with PASSWORD '%(password)s' %(role_attr_flags)s"
|
||||
cursor.execute(query % {"user": user, "password": password, "role_attr_flags": role_attr_flags})
|
||||
return True
|
||||
|
||||
def user_chpass(cursor, user, password):
|
||||
def user_alter(cursor, user, password, role_attr_flags):
|
||||
"""Change user password"""
|
||||
changed = False
|
||||
|
||||
# Handle passwords.
|
||||
if password is not None:
|
||||
select = "SELECT rolpassword FROM pg_authid where rolname=%(user)s"
|
||||
cursor.execute(select, {"user": user})
|
||||
current_pass_hash = cursor.fetchone()[0]
|
||||
# Not sure how to hash the new password, so we just initiate the
|
||||
# change and check if the hash changed
|
||||
alter = "ALTER USER %(user)s WITH PASSWORD '%(password)s'"
|
||||
cursor.execute(alter % {"user": user, "password": password})
|
||||
cursor.execute(select, {"user": user})
|
||||
new_pass_hash = cursor.fetchone()[0]
|
||||
if current_pass_hash != new_pass_hash:
|
||||
changed = True
|
||||
if password is not None or role_attr_flags is not None:
|
||||
# Define columns for select.
|
||||
columns = 'rolpassword,rolsuper,rolinherit,rolcreaterole,rolcreatedb,rolcanlogin,rolreplication'
|
||||
# Select password and all flag-like columns in order to verify changes.
|
||||
# rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcatupdate |
|
||||
# rolcanlogin | rolreplication | rolconnlimit | rolpassword | rolvaliduntil
|
||||
# Not sure how to interpolate properly in python yet...
|
||||
select = "SELECT " + columns + " FROM pg_authid where rolname=%(user)s"
|
||||
cursor.execute(select, {"columns": columns, "user": user})
|
||||
# Grab current role attributes.
|
||||
current_role_attrs = cursor.fetchone()
|
||||
|
||||
if password is not None:
|
||||
# Update the role attributes, including password.
|
||||
alter = "ALTER USER %(user)s WITH PASSWORD '%(password)s' %(role_attr_flags)s"
|
||||
cursor.execute(alter % {"user": user, "password": password, "role_attr_flags": role_attr_flags})
|
||||
else:
|
||||
# Update the role attributes, excluding password.
|
||||
alter = "ALTER USER %(user)s WITH %(role_attr_flags)s"
|
||||
cursor.execute(alter % {"user": user, "role_attr_flags": role_attr_flags})
|
||||
# Grab new role attributes.
|
||||
cursor.execute(select, {"columns": columns, "user": user})
|
||||
new_role_attrs = cursor.fetchone()
|
||||
|
||||
# Detect any differences between current_ and new_role_attrs.
|
||||
for i in range(len(current_role_attrs)):
|
||||
if current_role_attrs[i] != new_role_attrs[i]:
|
||||
changed = True
|
||||
|
||||
return changed
|
||||
|
||||
|
@ -267,6 +292,23 @@ def grant_privileges(cursor, user, privs):
|
|||
|
||||
return changed
|
||||
|
||||
def parse_role_attrs(role_attr_flags):
|
||||
"""
|
||||
Parse role attributes string for user creation.
|
||||
Format:
|
||||
|
||||
attributes[,attributes,...]
|
||||
|
||||
Where:
|
||||
|
||||
attributes := CREATEDB,CREATEROLE,NOSUPERUSER,...
|
||||
"""
|
||||
if ',' not in role_attr_flags:
|
||||
return role_attr_flags
|
||||
flag_set = role_attr_flags.split(",")
|
||||
o_flags = " ".join(flag_set)
|
||||
return o_flags
|
||||
|
||||
def parse_privs(privs, db):
|
||||
"""
|
||||
Parse privilege string to determine permissions for database db.
|
||||
|
@ -316,7 +358,8 @@ def main():
|
|||
priv=dict(default=None),
|
||||
db=dict(default=''),
|
||||
port=dict(default='5432'),
|
||||
fail_on_user=dict(default='yes')
|
||||
fail_on_user=dict(default='yes'),
|
||||
role_attr_flags=dict(default='')
|
||||
)
|
||||
)
|
||||
user = module.params["user"]
|
||||
|
@ -328,6 +371,7 @@ def main():
|
|||
module.fail_json(msg="privileges require a database to be specified")
|
||||
privs = parse_privs(module.params["priv"], db)
|
||||
port = module.params["port"]
|
||||
role_attr_flags = parse_role_attrs(module.params["role_attr_flags"])
|
||||
|
||||
if not postgresqldb_found:
|
||||
module.fail_json(msg="the python psycopg2 module is required")
|
||||
|
@ -355,12 +399,12 @@ def main():
|
|||
user_removed = False
|
||||
if state == "present":
|
||||
if user_exists(cursor, user):
|
||||
changed = user_chpass(cursor, user, password)
|
||||
changed = user_alter(cursor, user, password, role_attr_flags)
|
||||
else:
|
||||
if password is None:
|
||||
msg = "password parameter required when adding a user"
|
||||
module.fail_json(msg=msg)
|
||||
changed = user_add(cursor, user, password)
|
||||
changed = user_add(cursor, user, password, role_attr_flags)
|
||||
changed = grant_privileges(cursor, user, privs) or changed
|
||||
else:
|
||||
if user_exists(cursor, user):
|
||||
|
|
Loading…
Reference in a new issue