Skip to content

Commit

Permalink
[omnibus] modify pg_user provider for rds security reqs
Browse files Browse the repository at this point in the history
Modify pg_user to better support the limited access provided by RDS.
This includes:

- pg_shadow may not be available due to permissions, which prevents us
  from checking if a password change is required. If it's not,
  instead of failing we'll just perform the change as a password update-
  basically a no-op if the password doesn't need updating.
  to ensure it's correct.
- due to potential unavailability of pg_shadow, we instead check pg_user
  to see if a user already exists
- ensure that for remote databases, we add the superuser to newly
  created roles, so that if the superuser needs to modify the databases
  owned by that role, it can.
  • Loading branch information
Marc Paradise committed Jul 23, 2015
1 parent 21a86ec commit 8d1f5ca
Showing 1 changed file with 55 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,40 +20,65 @@ def whyrun_supported?
end

def do_create(connection)
user_info = connection.exec('select usesuper, passwd from pg_shadow where usename = $1', [ new_resource.username ])
user_info = connection.exec('select usesuper from pg_catalog.pg_user where usename = $1', [ new_resource.username ])
if user_info.ntuples > 0
user_info = user_info[0]
changes = [ "Update Postgres user #{new_resource.username}" ]
sql = ''
if user_info['usesuper'] != (new_resource.superuser ? 't' : 'f')
changes << " Set superuser to #{!!new_resource.superuser}"
sql << (new_resource.superuser ? 'SUPERUSER' : 'NOSUPERUSER')
end
if new_resource.password && user_info['passwd'] != ::PGconn.encrypt_password(new_resource.password, new_resource.username)
changes << ' Update password'
sql << " ENCRYPTED PASSWORD '#{connection.escape(new_resource.password)}'"
end
if changes.size > 1
converge_by changes do
connection.exec("ALTER USER \"#{new_resource.username}\" #{sql}")
end
end
update_user(connection, user_info[0])
else
changes = [ "Create Postgres user #{new_resource.username}" ]
sql = ''
if new_resource.superuser
changes << " Set superuser to #{!!new_resource.superuser}"
sql << (new_resource.superuser ? 'SUPERUSER' : 'NOSUPERUSER')
end
if new_resource.password
changes << ' Update password'
sql << " ENCRYPTED PASSWORD '#{connection.escape(new_resource.password)}'"
create_user(connection)
end
end

def update_user(connection, user_info)
changes = [ "Update Postgres user #{new_resource.username}" ]
sql = ''
if user_info['usesuper'] != (new_resource.superuser ? 't' : 'f')
changes << " Set superuser to #{!!new_resource.superuser}"
sql << (new_resource.superuser ? 'SUPERUSER' : 'NOSUPERUSER')
end
begin
# In some configurations we may not have access to pg_shadow - let's try it so we can see if we need to
# change a password. If access fails, we'll assume that we do need to update password (at worst, this
# will be a no-op)
pg_shadow_info = connection.exec('select passwd from pg_shadow where usename = $1', [ new_resource.username ])
if pg_shadow_info.ntuples > 0
pg_shadow_info = pg_shadow_info[0]
if new_resource.password && pg_shadow_info['passwd'] != ::PGconn.encrypt_password(new_resource.password, new_resource.username)
changes << ' Update password'
sql << " ENCRYPTED PASSWORD '#{connection.escape(new_resource.password)}'"
end
end
rescue PG::InsufficientPrivilege
changes << ' Update password'
sql << " ENCRYPTED PASSWORD '#{connection.escape(new_resource.password)}'"
end
if changes.size > 1
converge_by changes do
connection.exec("CREATE USER \"#{new_resource.username}\" #{sql}")
if node['private_chef']['postgresql']['external']
connection.exec("GRANT #{new_resource.username} TO \"#{node['private_chef']['postgresql']['db_superuser']}\"")
end
connection.exec("ALTER USER \"#{new_resource.username}\" #{sql}")
end
end
end

def create_user(connection)
changes = [ "Create PostgreSQL user #{new_resource.username}" ]
sql = ''
if new_resource.superuser
changes << " Give superuser access to #{!!new_resource.superuser}"
sql << (new_resource.superuser ? 'SUPERUSER' : 'NOSUPERUSER')
end
if new_resource.password
changes << ' Set password'
sql << " ENCRYPTED PASSWORD '#{connection.escape(new_resource.password)}'"
end
statements = [ "CREATE USER \"#{new_resource.username}\" #{sql} " ]
# To support modifying databases owned by this user, the superuser must
# have the new user's role.
if node['private_chef']['postgresql']['external']
statements << "GRANT #{new_resource.username} TO \"#{node['private_chef']['postgresql']['db_superuser']}\""
changes << " Grant role '#{new_resource.username}' to database superuser."
end
converge_by changes do
statements.each do |statement|
connection.exec(statement)
end
end
end

0 comments on commit 8d1f5ca

Please sign in to comment.