From 61d9472312e41c251e9b5a5e11fc9dbcdf497a16 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Sat, 26 Oct 2013 11:32:16 -0400 Subject: [PATCH 1/3] now supports users w/o a password, encrypted passwords and expiration Signed-off-by: Brian Coca --- library/database/postgresql_user | 106 +++++++++++++++++-------------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/library/database/postgresql_user b/library/database/postgresql_user index 90667c156a5..9a32022d2e7 100644 --- a/library/database/postgresql_user +++ b/library/database/postgresql_user @@ -44,7 +44,7 @@ options: password: description: - set the user's password - required: true + required: false default: null db: description: @@ -90,6 +90,16 @@ options: required: false default: present choices: [ "present", "absent" ] + encrypted: + description: + - denotes if the password is already encrypted. boolean. + required: false + default: false + expires: + description: + - sets the user's password expiration. + required: false + default: null notes: - The default authentication assumes that you are either logging in as or sudo'ing to the postgres account on the host. @@ -146,15 +156,18 @@ def user_exists(cursor, user): return cursor.rowcount > 0 -def user_add(cursor, user, password, role_attr_flags): +def user_add(cursor, user, password, role_attr_flags, encrypted, expires): """Create a new database user (role).""" - query = 'CREATE USER "%(user)s" WITH PASSWORD %%(password)s %(role_attr_flags)s' % { - "user": user, "role_attr_flags": role_attr_flags - } - cursor.execute(query, {"password": password}) + query = 'CREATE USER "%(user)s"' % { "user": user} + if password is not None: + query = query + " WITH %(crypt)s PASSWORD '%(password)s'" % { "crypt": encrypted, "password": password } + if expires is not None: + query = query + " VALID UNTIL '%(expires)s'" % { "exipres": expires } + query = query + " " + role_attr_flags + cursor.execute(query) return True -def user_alter(cursor, user, password, role_attr_flags): +def user_alter(cursor, user, password, role_attr_flags, encrypted, expires): """Change user password and/or attributes. Return True if changed, False otherwise.""" changed = False @@ -174,16 +187,18 @@ def user_alter(cursor, user, password, role_attr_flags): # Grab current role attributes. current_role_attrs = cursor.fetchone() + alter = 'ALTER USER "%(user)s"' % {"user": user} if password is not None: - # Update the role attributes, including password. - alter = 'ALTER USER "%(user)s" WITH PASSWORD %%(password)s %(role_attr_flags)s' % { - "user": user, "role_attr_flags": role_attr_flags + alter = alter + " WITH %(crypt)s PASSWORD '%(password)s' %(flags)s" % { + "crypt": encrypted, "password": password, "flags": role_attr_flags } - cursor.execute(alter, {"password": password}) - 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}) + elif role_attr_flags: + alter = alter + ' WITH ' + role_attr_flags + if expires is not None: + alter = alter + " VALID UNTIL '%(expires)s'" % { "exipres": expires } + + cursor.execute(alter) + # Grab new role attributes. cursor.execute(select, {"user": user}) new_role_attrs = cursor.fetchone() @@ -383,8 +398,10 @@ def main(): priv=dict(default=None), db=dict(default=''), port=dict(default='5432'), - fail_on_user=dict(default='yes'), - role_attr_flags=dict(default='') + fail_on_user=dict(type='bool', choices=BOOLEANS, default='yes'), + role_attr_flags=dict(default=''), + encrypted=dict(type='bool', choices=BOOLEANS, default='no'), + expires=dict(default=None) ), supports_check_mode = True ) @@ -392,13 +409,18 @@ def main(): user = module.params["user"] password = module.params["password"] state = module.params["state"] - fail_on_user = module.params["fail_on_user"] == 'yes' + fail_on_user = module.params["fail_on_user"] db = module.params["db"] if db == '' and module.params["priv"] is not None: 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 module.params["encrypted"]: + encrypted = "ENCRYPTED" + else: + encrypted = "UNENCRYPTED" + expires = module.params["expires"] if not postgresqldb_found: module.fail_json(msg="the python psycopg2 module is required") @@ -424,44 +446,32 @@ def main(): kw = dict(user=user) changed = False user_removed = False - - if state == "present": + if state == "present": if user_exists(cursor, user): - if module.check_mode: - kw['changed'] = True - module.exit_json(**kw) - - changed = user_alter(cursor, user, password, role_attr_flags) + changed = user_alter(cursor, user, password, role_attr_flags, encrypted, expires) else: - if password is None: - msg = "password parameter required when adding a user" - module.fail_json(msg=msg) - - if module.check_mode: - kw['changed'] = True - module.exit_json(**kw) - - changed = user_add(cursor, user, password, role_attr_flags) + changed = user_add(cursor, user, password, role_attr_flags, encrypted, expires) changed = grant_privileges(cursor, user, privs) or changed else: - if user_exists(cursor, user): - if module.check_mode: - kw['changed'] = True - kw['user_removed'] = True - module.exit_json(**kw) - - changed = revoke_privileges(cursor, user, privs) - user_removed = user_delete(cursor, user) - changed = changed or user_removed - if fail_on_user and not user_removed: - msg = "unable to remove user" - module.fail_json(msg=msg) - kw['user_removed'] = user_removed + if module.check_mode: + changed = True + kw['user_removed'] = True + else: + changed = revoke_privileges(cursor, user, privs) + user_removed = user_delete(cursor, user) + changed = changed or user_removed + if fail_on_user and not user_removed: + msg = "unable to remove user" + module.fail_json(msg=msg) + kw['user_removed'] = user_removed if changed: - db_connection.commit() + if module.check_mode: + db_connection.rollback() + else: + db_connection.commit() kw['changed'] = changed module.exit_json(**kw) From 594ecc00c162470ad441903de5ad346e38bf2e0f Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Sat, 26 Oct 2013 11:37:45 -0400 Subject: [PATCH 2/3] updated docs Signed-off-by: Brian Coca --- library/database/postgresql_user | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/database/postgresql_user b/library/database/postgresql_user index 9a32022d2e7..f213f06ece6 100644 --- a/library/database/postgresql_user +++ b/library/database/postgresql_user @@ -95,11 +95,13 @@ options: - denotes if the password is already encrypted. boolean. required: false default: false + version_added: '1.4' expires: description: - sets the user's password expiration. required: false default: null + version_added: '1.4' notes: - The default authentication assumes that you are either logging in as or sudo'ing to the postgres account on the host. @@ -131,6 +133,9 @@ EXAMPLES = ''' # Example privileges string format INSERT,UPDATE/table:SELECT/anothertable:ALL + +# Remove an existing user's password +- postgresql_user: db=test user=test password=NULL ''' import re From 87e886a5502fb8106de7448669b9a3f5475352b5 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Sat, 26 Oct 2013 12:47:22 -0400 Subject: [PATCH 3/3] updated to make note that password is required before v1.4 Signed-off-by: Brian Coca --- library/database/postgresql_user | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/database/postgresql_user b/library/database/postgresql_user index f213f06ece6..e7d2793a535 100644 --- a/library/database/postgresql_user +++ b/library/database/postgresql_user @@ -43,7 +43,7 @@ options: default: null password: description: - - set the user's password + - set the user's password, before 1.4 this was requried. required: false default: null db: