diff --git a/README.md b/README.md index 11d8182..4d4e017 100644 --- a/README.md +++ b/README.md @@ -97,13 +97,21 @@ To manage users, roles and permissions: password_hash => 'foo', } - postgresql::database_grant{'test1': - privilege => 'ALL', - db => 'test1', - role => 'dan', + postgresql::database_grant { 'test1': + privilege => 'ALL', + db => 'test1', + role => 'dan', } -In this example, you would grant ALL privileges on the test1 database to the user or group specified by dan. + postgresql::table_grant { 'my_table of test2': + privilege => 'ALL', + table => 'my_table', + db => 'test2', + role => 'dan', + } + + +In this example, you would grant ALL privileges on the test1 database and on the `my_table` table of the test2 database to the user or group specified by dan. At this point, you would just need to plunk these database name/username/password values into your PuppetDB config files, and you are good to go. @@ -127,6 +135,7 @@ Resources: * [postgresql::db](#resource-postgresqldb) * [postgresql::database](#resource-postgresqldatabase) * [postgresql::database_grant](#resource-postgresqldatabasegrant) +* [postgresql::table_grant](#resource-postgresqltablegrant) * [postgresql::role](#resource-postgresqlrole) * [postgresql::tablespace](#resource-postgresqltablespace) * [postgresql::validate_db_connection](#resource-postgresqlvalidatedbconnection) @@ -341,7 +350,7 @@ This defined type manages grant based access privileges for users. Consult the P Used to uniquely identify this resource, but functionality not used during grant. ####`privilege` -Can be one of `SELECT`, `INSERT`, `UPDATE`, `DELETE`, `TRUNCATE`, `REFERENCES`, `TRIGGER`, `USAGE`, `TEMPORARY`, `TEMP`, `CONNECT`. `ALL` is used as a synonym for `CREATE`. If you need to add multiple privileges, a space delimited string can be used. +Can be one of `SELECT`, `TEMPORARY`, `TEMP`, `CONNECT`. `ALL` is used as a synonym for `CREATE`. If you need to add multiple privileges, a space delimited string can be used. ####`db` Database to grant access to. @@ -355,6 +364,30 @@ Database to execute the grant against. This should not ordinarily be changed fro ####`psql_user` OS user for running `psql`. Defaults to the default user for the module, usually `postgres`. +###Resource: postgresql::table\_grant +This defined type manages grant based access privileges for users. Consult the PostgreSQL documentation for `grant` for more information. + +####`namevar` +Used to uniquely identify this resource, but functionality not used during grant. + +####`privilege` +Can be one of `SELECT`, `INSERT`, `UPDATE`, `REFERENCES`. `ALL` is used as a synonym for `CREATE`. If you need to add multiple privileges, a space delimited string can be used. + +####`table` +Table to grant access on. + +####`db` +Database of table. + +####`role` +Role or user whom you are granting access for. + +####`psql_db` +Database to execute the grant against. This should not ordinarily be changed from the default, which is `postgres`. + +####`psql_user` +OS user for running `psql`. Defaults to the default user for the module, usually `postgres`. + ###Resource: postgresql::role This resource creates a role or user in PostgreSQL. diff --git a/manifests/database_grant.pp b/manifests/database_grant.pp index 994fdc0..63943a8 100644 --- a/manifests/database_grant.pp +++ b/manifests/database_grant.pp @@ -15,50 +15,21 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -# TODO: in mysql module, the grant resource name might look like this: 'user@host/dbname'; -# I think that the API for the resource type should split these up, because it's -# easier / safer to recombine them for mysql than it is to parse them for other -# databases. Also, in the mysql module, the hostname portion of that string -# affects the user's ability to connect from remote hosts. In postgres this is -# managed via pg_hba.conf; not sure if we want to try to reconcile that difference -# in the modules or not. - define postgresql::database_grant( - # TODO: mysql supports an array of privileges here. We should do that if we - # port this to ruby. $privilege, $db, $role, - $psql_db = $postgresql::params::user, - $psql_user = $postgresql::params::user + $psql_db = undef, + $psql_user = undef ) { include postgresql::params - - Postgresql_psql { - psql_user => $postgresql::params::user, - psql_group => $postgresql::params::group, - psql_path => $postgresql::params::psql_path, - } - - # TODO: FIXME: only works on databases, due to using has_database_privilege - - # TODO: this is a terrible hack; if they pass "ALL" as the desired privilege, - # we need a way to test for it--and has_database_privilege does not recognize - # 'ALL' as a valid privilege name. So we probably need to hard-code a mapping - # between 'ALL' and the list of actual privileges that it entails, and loop - # over them to check them. That sort of thing will probably need to wait until - # we port this over to ruby, so, for now, we're just going to assume that if - # they have "CREATE" privileges on a database, then they have "ALL". (I told - # you that it was terrible!) - $unless_privilege = $privilege ? { - 'ALL' => 'CREATE', - default => $privilege, - } - - postgresql_psql {"GRANT ${privilege} ON database \"${db}\" TO \"${role}\"": - db => $psql_db, - psql_user => $psql_user, - unless => "SELECT 1 WHERE has_database_privilege('${role}', '${db}', '${unless_privilege}')", + postgresql::grant { "database:${name}": + role => $role, + db => $db, + privilege => $privilege, + object_type => 'DATABASE', + object_name => $db, + psql_db => $psql_db, + psql_user => $psql_user, } } diff --git a/manifests/grant.pp b/manifests/grant.pp new file mode 100644 index 0000000..6b383b3 --- /dev/null +++ b/manifests/grant.pp @@ -0,0 +1,77 @@ +# Resource postgresql::grant +# +# TODO: in mysql module, the grant resource name might look like this: 'user@host/dbname'; +# I think that the API for the resource type should split these up, because it's +# easier / safer to recombine them for mysql than it is to parse them for other +# databases. Also, in the mysql module, the hostname portion of that string +# affects the user's ability to connect from remote hosts. In postgres this is +# managed via pg_hba.conf; not sure if we want to try to reconcile that difference +# in the modules or not. +define postgresql::grant ( + $role, + $db, + # TODO: mysql supports an array of privileges here. We should do that if we + # port this to ruby. + $privilege = undef, + $object_type = 'database', + $object_name = $db, + $psql_db = $postgresql::params::user, + $psql_user = $postgresql::params::user +) { + + ## Munge the input values + $_object_type = upcase($object_type) + $_privilege = upcase($privilege) + + ## Validate that the object type is known + validate_string($_object_type, + #'COLUMN', + 'DATABASE', + #'FOREIGN SERVER', + #'FOREIGN DATA WRAPPER', + #'FUNCTION', + #'PROCEDURAL LANGUAGE', + #'SCHEMA', + #'SEQUENCE', + 'TABLE', + #'TABLESPACE', + #'VIEW', + ) + + ## Validate that the object type's privilege is acceptable + case $_object_type { + 'DATABASE': { + validate_string($_privilege,'CREATE','CONNECT','TEMPORARY','TEMP','ALL','ALL PRIVILEGES') + $unless_function = 'has_database_privilege' + $on_db = $psql_db + } + 'TABLE': { + validate_string($_privilege,'SELECT','INSERT','UPDATE','REFERENCES','ALL','ALL PRIVILEGES') + $unless_function = 'has_table_privilege' + $on_db = $db + } + default: { + fail("Missing privilege validation for object type ${_object_type}") + } + } + + # TODO: this is a terrible hack; if they pass "ALL" as the desired privilege, + # we need a way to test for it--and has_database_privilege does not recognize + # 'ALL' as a valid privilege name. So we probably need to hard-code a mapping + # between 'ALL' and the list of actual privileges that it entails, and loop + # over them to check them. That sort of thing will probably need to wait until + # we port this over to ruby, so, for now, we're just going to assume that if + # they have "CREATE" privileges on a database, then they have "ALL". (I told + # you that it was terrible!) + $unless_privilege = $_privilege ? { + 'ALL' => 'CREATE', + default => $_privilege, + } + postgresql_psql { "GRANT ${_privilege} ON ${_object_type} \"${object_name}\" TO \"${role}\"": + db => $on_db, + psql_user => $psql_user, + psql_group => $postgresql::params::group, + psql_path => $postgresql::params::psql_path, + unless => "SELECT 1 WHERE ${unless_function}('${role}', '${object_name}', '${unless_privilege}')", + } +} diff --git a/manifests/table_grant.pp b/manifests/table_grant.pp new file mode 100644 index 0000000..850a15c --- /dev/null +++ b/manifests/table_grant.pp @@ -0,0 +1,20 @@ +# Resource postgresql::table_grant +define postgresql::table_grant( + $privilege, + $table, + $db, + $role, + $psql_db = undef, + $psql_user = undef +) { + include postgresql::params + postgresql::grant { "table:${name}": + role => $role, + db => $db, + privilege => $privilege, + object_type => 'TABLE', + object_name => $table, + psql_db => $psql_db, + psql_user => $psql_user, + } +} diff --git a/spec/system/install_spec.rb b/spec/system/install_spec.rb index e3eab84..9c37037 100644 --- a/spec/system/install_spec.rb +++ b/spec/system/install_spec.rb @@ -241,7 +241,7 @@ describe 'install:' do end end - describe 'postgresql::grant' do + describe 'postgresql::database_grant' do it 'should grant access so a user can create in a database' do begin pp = <<-EOS @@ -258,8 +258,10 @@ describe 'install:' do postgresql::database_user { $user: password_hash => postgresql_password($user, $password), - require => [ Class['postgresql::server'], - User[$user] ], + require => [ + Class['postgresql::server'], + User[$user], + ], } postgresql::database { $db: @@ -267,11 +269,13 @@ describe 'install:' do } postgresql::database_grant { 'grant create test': - privilege => 'CREATE', - db => $db, - role => $user, - require => [ Postgresql::Database[$db], - Postgresql::Database_user[$user] ], + privilege => 'CREATE', + db => $db, + role => $user, + require => [ + Postgresql::Database[$db], + Postgresql::Database_user[$user], + ], } EOS @@ -295,6 +299,73 @@ describe 'install:' do end end + describe 'postgresql::table_grant' do + it 'should grant access so a user can insert in a table' do + begin + pp = <<-EOS + $db = 'table_grant' + $user = 'psql_table_tester' + $password = 'psql_table_pw' + + include postgresql::server + + # Since we are not testing pg_hba or any of that, make a local user for ident auth + user { $user: + ensure => present, + } + + postgresql::database_user { $user: + password_hash => postgresql_password($user, $password), + require => [ + Class['postgresql::server'], + User[$user], + ], + } + + postgresql::database { $db: + require => Class['postgresql::server'], + } + + postgresql_psql { 'Create testing table': + command => 'CREATE TABLE "test_table" (field integer NOT NULL)', + db => $db, + unless => "SELECT * FROM pg_tables WHERE tablename = 'test_table'", + require => Postgresql::Database[$db], + } + + postgresql::table_grant { 'grant insert test': + privilege => 'INSERT', + table => 'test_table', + db => $db, + role => $user, + require => [ + Postgresql::Database[$db], + Postgresql::Database_user[$user], + Postgresql_psql['Create testing table'], + ], + } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + + puppet_apply(pp) do |r| + r.exit_code.should be_zero + end + + ## Check that the user can create a table in the database + #psql('--command="create table foo (foo int)" postgres', 'psql_grant_tester') do |r| + # r.stdout.should =~ /CREATE TABLE/ + # r.stderr.should be_empty + # r.exit_code.should == 0 + #end + ensure + #psql('--command="drop table foo" postgres', 'psql_grant_tester') + end + end + end + describe 'postgresql::validate_db_connections' do it 'should run puppet with no changes declared if database connectivity works' do pp = <<-EOS