Install PostgreSQL extension for non-superuser - postgresql

My app's database has a dedicated user with full access like so;
CREATE DATABASE "mydb"
WITH OWNER = cloudsqlsuperuser
ENCODING = 'UTF8'
TABLESPACE = pg_default
LC_COLLATE = 'en_US.UTF8'
LC_CTYPE = 'en_US.UTF8'
CONNECTION LIMIT = -1;
GRANT CONNECT, TEMPORARY ON DATABASE "mydb" TO public;
GRANT ALL ON DATABASE "mydb" TO cloudsqlsuperuser;
GRANT ALL ON DATABASE "mydb" TO "mydb";
In the startup of my app I have the following query in place to make sure that UUID extension is available;
create extension if not exists "uuid-ossp";
This works great. However I recently tried adding an identical query to create "pgcrypto" extension like so;
create extension if not exists "pgcrypto";
This fails claiming I need super user privileges to create the extension. So I decided to manually create it as a superuser user. However even if I log as postgres user in Adminer and create the extension successfully, it does not become available to the queries in my app (mydb user).
I am not familiar with the namespace mechanisms used in PostgreSQL, so I want to know how can I create the extension so that it will become available from my app's code without giving app user superuser privileges?

Related

AWS RDS PostgreSQL Tablespace

I created an AWS RDS postgres instance. When creating i was prompted to create user. There is no reason for creating tablespaces with location in AWS RDS? The reason is when creating a database i want the database to goto the tablespace i created. When i run the below command
CREATE USER test;
CREATE TABLESPACE test OWNER test LOCATION '/test_data';
CREATE DATABASE test WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'C' LC_CTYPE = 'C' TABLESPACE = test;
ERROR: permission denied for tablespace test
I logged into postgres using test_admin user to run the above commands. The test_admin is the account i created during the RDS instance provisioning. Is that the only account that i should be using above to create all of the DB objects from that point?
Do you want the owner of the database to be test as well? If so, add that into your CREATE DATABASE.... Otherwise, you'll probably need to give the test_admin user permission on the test tablespace before trying to create it.
Tablespaces in postgres RDS are located in /rdsdbdata/db/base/tablespace/
and I think this question has been answered here
Create tablespace on PostgreSQL RDS

Permission denied to copy database

I'm still getting used to the concept of roles in Postgres.
I'm trying to create a role, migrator, that will have the ability to read from a production db and use it as a template to make stage and dev databases.
I've created this role migrator originally like so:
CREATE ROLE migrator LOGIN ENCRYPTED PASSWORD '<password>'
and proceeded to restrict access to the prod database:
REVOKE ALL ON DATABASE prod FROM PUBLIC;
GRANT CONNECT ON DATABASE prod TO migrator;
/* switch to prod database */
GRANT USAGE ON SCHEMA public TO migrator;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO migrator;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO migrator;
After trying a CREATE DATABASE stage TEMPLATE prod; and getting an error, I had to alter the role to create a db:
ALTER ROLE migrator CREATEDB;
and tried again. This time I got the error:
ERROR: permission denied to copy database "prod"
And again, I tried to add the replication permission to the migrator role (not sure if this is correct, as the manual says this is a very elevated permission)
ALTER ROLE migrator REPLICATION;
however, I still get the same error.
UPDATE: I've figured out that this has something to do with who owns the database; however, my problem remains. How can I allow another role with just READ privileges the ability to copy a database as well? I looked at role inheritance, but at first glance it looks like the inheriting role will just get the same permissions as the parent role.
It's not possible to copy a database unless the logged-in role is an owner or the database is flagged as a template:
datistemplate can be set to indicate that a database is intended as a template for CREATE DATABASE. If this flag is set, the database can be cloned by any user with CREATEDB privileges; if it is not set, only superusers and the owner of the database can clone it.

Database named "postgres"

I've just set up Postgres for use by different users on my network. Every user has his own username/password/database, but when I connect to Pg I can also see a 'postgres' database (and even create tables etc). I tried to REVOKE access to that database from public but then it won't let me connect. What exactly is the postgres database and why is it needed? Can I disable it so that users only see the database(s) I've created for them?
The postgres database is created by default when you run initdb.
Quote from the manual:
Creating a database cluster consists of creating the directories in which the database data will live (...) creating the template1 and postgres databases. When you later create a new database, everything in the template1 database is copied. (...) The postgres database is a default database meant for use by users, utilities and third party applications.
There is nothing special about it, and if you don't need it, you can drop it:
drop database postgres;
You need to do that as a superuser of course. The only downside of this is that when you run psql as the postgres operating system user, you need to explicitly provide a database name to connect to
If you drop the postgres database you'll find a few things to be confusing. Most tools default to using it as the default database to connect to, for one thing. Also, anything run under the postgres user will by default expect to connect to the postgres database.
Rather than dropping it, REVOKE the default connect right to it.
REVOKE connect ON DATABASE postgres FROM public;
The superuser (usually postgres), and any users you explicitly grant rights to access the database can still use it as a convenience DB to connect to. But others can't.
To grant connect rights to a user, simply:
GRANT connect ON DATABASE postgres TO myuser;

Why can only a superuser CREATE EXTENSION hstore, but not on Heroku?

When I attempt to enable hstore on my database:
=> CREATE EXTENSION IF NOT EXISTS hstore;
ERROR: permission denied to create extension "hstore"
HINT: Must be superuser to create this extension.
My user is not a superuser, but is the owner of the database.
According to the CREATE EXTENSION docs:
Loading an extension requires the same privileges that would be required to create its component objects. For most extensions this means superuser or database owner privileges are needed. The user who runs CREATE EXTENSION becomes the owner of the extension for purposes of later privilege checks, as well as the owner of any objects created by the extension's script.
What is hstore doing that requires superuser privileges? Is it affecting parts of the cluster outside the database I'm adding it to?
Further confundity:
The DB user Heroku Postgres provides is not a superuser:
Heroku Postgres users are granted all non-superuser permissions on their database. These include SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER, CREATE, CONNECT, TEMPORARY, EXECUTE, and USAGE.
However, that user is able to CREATE EXTENSION hstore:
To create any supported extension, open a session with heroku pg:psql and run the appropriate command:
$ heroku pg:psql
Pager usage is off.
psql (9.2.4)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.
ad27m1eao6kqb1=> CREATE EXTENSION hstore;
CREATE EXTENSION
ad27m1eao6kqb1=>
(For context, I'm attempting to set up a Dokku deployment, so the comparison to Heroku is especially important.)
The hstore extension creates functions that call code from an external dynamic object, which requires superuser privilege. That's why creating the hstore extension requires superuser privilege.
As for Heroku, it is my understanding that they are running with a special extension whitelisting module, which allows users to create certain extensions even though they are not superusers. I believe it is based on this code: https://github.com/dimitri/pgextwlist. You can try to install that code yourself if you want the same functionality in your databases.
ALTER USER myuser WITH SUPERUSER;
If you run this command from a superuser, this solves your CREATE EXTENSION issue. You may check your available users with \du to find a superuser.
This is not related to heroku.
This is how I solved this issue in ubuntu 18.04.
Provide postgres super user access.
sudo su postgres
Then I run:
psql -U postgres your_database_name -c 'create extension hstore;'
Now I can alter table your_database_name and add hstore type columns in it.
Connect to your database
psql -d your_database_name -U your_user_role
And
alter table your_table_name add your_column_name HSTORE;
Though there might be saveral different ways to do it, but I solve it in this way.
Hope this will help novice users like me.

Why can I not set permissions on fresh install of PostgreSQL

A fresh installation of PostgreSQL 9.3 (according to the YUM Installation manual on the PostgreSQL wiki) on CentOS 6 (64-bit) will not grant permissions to any users.
I log in to the postgres user and open psql, then I create a role for my default user:
CREATE ROLE <name> WITH PASSWORD '<password>';
and then try to grant it privileges on the default postgres database:
GRANT ALL ON DATABASE postgres TO <user>;
which gives the expected output, but the user does not have any permissions on postgres.
The output of \dp <user> is quizically empty as well. Additional testing shows that I cannot give any users permissions. However, when I try to drop a role that has been granted these nonexistent permissions, it says
ERROR: role "<user>" cannot be dropped because some objects depend on it
DETAIL: privileges for database postgres
I am at a loss. I did also check to make sure the postgres Linux user has the appropriate file permissions on the PostgreSQL data directory.
Presumably you're expecting too much of GRANT ALL ON DATABASE postgres TO <user>;
ALL in this context means that the command is equivalent to:
GRANT CREATE,CONNECT,TEMPORARY ON DATABASE postgres TO <user>;
And the way you create the ROLE, it cannot login to any database anyway (you can check this with \du).
It could if it was created with:
CREATE ROLE name WITH LOGIN PASSWORD 'pass';
or use ALTER ROLE name WITH LOGIN later on.
Starting from this, to give the user permissions to create objects in the database, other forms of GRANT should be used.