Why is a new user allowed to create a table?
When you create a new database, any role is allowed to create objects in the public
schema. To remove this possibility, you may issue immediately after the database creation:
REVOKE ALL ON schema public FROM public;
Edit: after the above command, only a superuser may create new objects inside the public
schema, which is not practical. Assuming a non-superuser foo_user
should be granted this privilege, this should be done with:
GRANT ALL ON schema public TO foo_user;
To know what ALL
means for a schema, we must refer to GRANT in the doc, (in PG 9.2 there are no less than 14 forms of GRANT statements that apply to different things...). It appears that for a schema it means CREATE
and USAGE
.
On the other hand, GRANT ALL PRIVILEGES ON DATABASE...
will grant CONNECT
and CREATE
and TEMP
, but CREATE
in this context relates to schemas, not permanent tables.
Regarding this error: ERROR: no schema has been selected to create in
, it happens when trying to create an object without schema qualification (as in create table foo(...)
) while lacking the permission to create it in any schema of the search_path
.
The crucial thing to understand here is that privileges are not heirachical and are not inherited from containing objects. ALL
means all privileges for this object not all privileges for this object and all contained objects.
When you grant ALL
on a database, you're granting CREATE, CONNECT, TEMP
. These are actions on the database object its self:
CONNECT
: Connect to the DBCREATE
: Create a schema (not a table)TEMP
: Create temporary objects, including but not limited to temp tables
Now, each PostgreSQL database by default has a public
schema that's created when the database is created. This schema has all rights granted to the role public
, of which everybody is implicitly a member. For a schema, ALL
means CREATE, USAGE
:
CREATE
: Create objects (including tables) within this schemaUSAGE
: List objects in the schema and access them if their permissions permit
If you do not specify the schema to create an object like a table in, the database engine uses the search_path
, and by default the public
schema is first on the search_path
so the table is created there. Everyone has rights to public
by default, so the creation is allowed. The users' rights on the database are irrelevant at this point, as the user isn't trying to do anything to the database object its self, only a schema within it.
It doesn't matter that you haven't granted the user any rights other than granting CONNECT
on the database, because the public
schema permits all users to create tables in it by default. Daniel has already explained how to revoke that right if desired.
If you want to delegate every right explicitly, revoke all from public, or simply drop the public schema. You can create a new template database with this change applied if you want to. Alternately you can apply it to template1
, but that'll likely break lots of 3rd party code that assumes that public
exists and is writeable.
This might make more sense if you look at a filesystem analogy.
If I have the directory structure (mode simplified to only show the mode that applies to the current user):
/dir1 mode=r-x
/dir1/dir2 mode=rwx
then I cannot create anything within /dir1
, because I do not have write permission. So if I touch /dir1/somefile
I will get a permission denied error.
However, I do have permission to look inside /dir1
and to access contained files and directories, including /dir1/dir2
. I have write permission on dir2
. So touch /dir1/dir2/somefile
will succeed, even though I don't have write permission to dir1
.
Same thing with databases and schemas.
If you only want to prevent new users from creating tables, you need to run the following command:
REVOKE CREATE ON SCHEMA public FROM public;
If you REVOKE ALL
(as other answers suggest), you will also prevent users from having USAGE
permissions. USAGE
means that users can use the permissions assigned to them, so if you remove that then your users will not be able to list or access tables that they have access to.
Alternatively, you could also REVOKE CREATE
for a specific user:
REVOKE CREATE ON schema public FROM myuser;
See also: How to create a read only user with PostgreSQL.