add role_attr_flags parameter to postgresql_user

Pass role_attr_flags a list of comma separated role
attributes when creating or updating a user.
pull/1339/head
Jeremiah Heller 12 years ago
parent 036f853d42
commit 37bdefae74

@ -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…
Cancel
Save