Postgres: How Can a Role Both Have INSERT Permissions and Not? - postgresql

I have a table called resources and a user/role called public_anonymous ... and as far as I can tell, the user doesn't have INSERT permissions on that table. Here's DBeaver showing as much:
And here's the output of \z resources (at the psql command line):
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+-----------+-------+--------------------------------+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------
public | resources | table | admin=arwdDxt/admin +| | select_resources (r):
+
| | | public_postgraphile=arwd/admin+| | (u): true
+
| | | public_anonymous=r/admin +| | update_resources (w):
+
| | | public_admin_user=arwd/admin +| | (u): (( SELECT resource_authors.user_id
+
| | | public_user=ar/admin +| | FROM resource_authors
+
| | | =a/admin | | WHERE ((resource_authors.resource_id = resource_authors.resource_id) AND (resource_authors.user_id = (NULLIF(current_setting('jwt.claims.person_id'::text, true), ''::text))::integer))) IS NOT NULL)+
| | | | | to: public_user
+
| | | | | delete_resources (d):
+
| | | | | (u): (( SELECT resource_authors.user_id
+
| | | | | FROM resource_authors
+
| | | | | WHERE ((resource_authors.resource_id = resource_authors.resource_id) AND (resource_authors.user_id = (NULLIF(current_setting('jwt.claims.per
son_id'::text, true), ''::text))::integer))) IS NOT NULL)+
| | | | | to: public_user
The key part of that is:
public_anonymous=r/admin
ie. the role has only been granted read ("r") permissions by the admin role.
Furthermore, I have repeatedly tried to remove INSERT permissions by running:
REVOKE INSERT ON resources FROM public_anonymous;
Nevertheless, when I tried using pgTAP to verify my permissions, I was surprised to find that it reported that the user did have INSERT permissions. When I asked the maintainer why it thought that, he explained that it used the pg_catalog.has_table_privilege function ... and sure enough, when I ran:
select pg_catalog.has_table_privilege('public_anonymous', 'resources', 'INSERT');
I saw:
has_table_privilege
---------------------
t
I'm not a Postgres expert, so forgive me if this is a dumb question. Also I should note that I have been using both GRANT statements and row-level security policies on this table ...
... but still, I don't understand why multiple sources all say the user doesn't have INSERT permissions, AND I explicitly REVOKE-ed them ... yet pg_catalog.has_table_privilege still thinks I do have the permission.
Can anyone explain what's going on, and possibly how I might remove this permission (for good)?

This ACL item
=a/admin
means that PUBLIC (that is, everyone) has INSERT privileges on the relation. REVOKE that if you don't want it.

Related

postgREST can find relation

I'm trying to set up postgREST. Have been following the tutorial at http://postgrest.org/en/v5.1/tutorials/tut0.html. Here is what I see. First, the schemas:
entercarlson=# \dn
List of schemas
Name | Owner
--------+---------
api | carlson
public | carlson
Then a table:
carlson=# \d api.todos
Table "api.todos"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+---------------------------------------
id | integer | | not null | nextval('api.todos_id_seq'::regclass)
done | boolean | | not null | false
task | text | | not null |
due | timestamp with time zone | | |
Indexes:
"todos_pkey" PRIMARY KEY, btree (id)
Finally, some data:
carlson=# select * from api.todos;
id | done | task | due
----+------+-------------------+-----
1 | f | finish tutorial 0 |
2 | f | pat self on back |
(2 rows)
But then I get this:
$ curl http://localhost:3000/todos
{"hint":null,"details":null,"code":"42P01","message":"relation
\"api.todos\" does not exist"}
Which is consistent with this:
carlson=# \d
Did not find any relations.
What am I doing wrong?
PS. I don't see which database this schema belongs to
Seems you're targeting the wrong database, check the db-uri config value and make sure this uri contains the api.todos table through psql.
Also, want to clarify that the search_path is modified by PostgREST on each request, so if you ALTER your connection user search_path it'll have no effect on the schemas PostgREST searches.

PostgreSQL: Serializable transactions with GRANT/DROP role

I have been reading a lot about serializable transactions in Postgres, but came across an issue that I haven't been able to resolve. Let's assume that I have two Postgres sessions A and B from different psql processes, and that before starting any transactions, I create roles role1 and role2
myuser=# CREATE ROLE role1 ;
CREATE ROLE
myuser=# CREATE ROLE role2 ;
CREATE ROLE
At this point, I can start transactions in both of the sessions via myuser=# BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE ; and verify that both sessions see both roles, via SELECT * FROM pg_catalog.pg_roles;
Let's assume that I drop the role role2 from the session B, verify that it's not shown and commit:
myuser=# DROP ROLE role2 ;
DROP ROLE
myuser=# SELECT * FROM pg_catalog.pg_roles;
rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolconnlimit | rolpassword | rolvaliduntil | rolbypassrls | rolconfig | oid
-----------------------------------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+-------------+---------------+--------------+-----------+-------
role1 | f | t | f | f | f | f | -1 | ******** | | f | | 16563
myuser=# commit ;
COMMIT
Now, let's go back to session A:
Even after dropping role2 and committing inside session B, we can still see that the transaction in session A still sees both roles (as expected, as this is serializable):
myuser=# SELECT * FROM pg_catalog.pg_roles;
rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolconnlimit | rolpassword | rolvaliduntil | rolbypassrls | rolconfig | oid
-----------------------------------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+-------------+---------------+--------------+-----------+------
role1 | f | t | f | f | f | f | -1 | ******** | | f | | 16563
role2 | f | t | f | f | f | f | -1 | ******** | | f | | 16564
Now, let's try the GRANT command. Here is the weird part:
myuser=# GRANT role2 TO role1 ;
ERROR: role "role2" does not exist
Right before running the GRANT, we can see that both roles role1 and role2 exist, but now we see such error. Why is that?
Thanks!
With “normal” (i.e. non-catalog) tables conflicting operations like this would lead to a serialization error. While a serializable transaction sees an old state of the database, any attempt to modify a value that has been changed since would lead to such an error.
What makes this case different is that the tables that store roles and role membership (pg_authid and pg_auth_members) are catalog tables (even shared catalogs since roles apply to all databases), and for these processing and error messages are slightly different.
It is good and necessary that the operation fails, even if the error message is surprising.

Can see tables in phpPgAdmin but not in psql (or any other method)

I have recently installed postgres on my Mac and loaded a dump from our public website that I am trying to replicate locally. The pg_restore appeared to be successful, and I can see all my tables and user permissions in phpPgAdmin. They all look normal, and I can browse the data. However, when I access the database via psql I get no relations found. Similarly I have python scripts that access this database, and they also fail to find any tables.
I'm at a loss as to what to do.
Here are some psql commands I've done:
> psql -U mgd_dbo pub_mgd
psql (9.3.4)
Type "help" for help.
pub_mgd=# \d
No relations found.
pub_mgd=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
------------+------------+----------+------------------+-------+-------------------------
postgres | postgres | UTF8 | C | C | =CTc/postgres +
| | | | | postgres=CTc/postgres +
| | | | | mgd_dbo=CTc/postgres +
| | | | | mgd_public=CTc/postgres
pub_fe | postgres | LATIN9 | en_US.ISO8859-15 | C | =CTc/postgres +
| | | | | postgres=CTc/postgres +
| | | | | mgd_dbo=CTc/postgres +
| | | | | mgd_public=CTc/postgres
pub_mgd | postgres | LATIN9 | en_US.ISO8859-15 | C | =CTc/postgres +
| | | | | postgres=CTc/postgres +
| | | | | mgd_dbo=CTc/postgres +
| | | | | mgd_public=CTc/postgres
template0 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
(5 rows)
pub_mgd=# \dn+
List of schemas
Name | Owner | Access privileges | Description
--------+----------+----------------------+------------------------
mgd | mgd_dbo | mgd_dbo=UC/mgd_dbo +|
| | mgd_public=U/mgd_dbo |
public | postgres | postgres=UC/postgres+| standard public schema
| | =UC/postgres |
(2 rows)
pub_mgd is the database I've loaded. mgd_public and mgd_dbo are the two different users our scripts use (mgd_public is a read-only user).
I've tried both users, and even as the postgres user. Yet, the results are the same, no relations.
I even tried to see if phpPgAdmin was somehow hitting a different postgres sever, so I added a new database through it, and verified that the new database appears when I do \l in psql.
Any suggestions as to what to check next?
Ask for the list of relations in the mgd schema
\d mgd.*
To make the mgd schema the default place it at the beginning of the search path
set search_path to mgd, "$user", public;

Check if a role in PostgreSQL has a password set

I wonder how I can verify whether or not a role (users are just a specific kind of role) has a password set in PostgreSQL 9.1.
I tried the command \dg+ and \du+ but they don't show you anything password related. I also used the following query, but it doesn't help either because of its indifference (I'm quite sure that the postgresql user has no password set in this case):
SELECT * FROM pg_user;
usename | usesysid | usecreatedb | usesuper | usecatupd | userepl | passwd | valuntil | useconfig
----------+----------+-------------+----------+-----------+---------+----------+----------+-----------
postgres | 10 | t | t | t | t | ******** | |
aef | 16201 | t | t | t | t | ******** | |
Passwords are stored in pg_shadow
In documentation:
Password (possibly encrypted); null if none. See pg_authid for details
of how encrypted passwords are stored.
So you should select * from pg_shadow;
You should also check pg_authid table.

user with CREATEUSER can create database?

Team, I am using Redshift 8.0.2. I created one user as below:
create user user004 with password 'User123#' CREATEUSER valid until '2015-03-31';
redshift3=# select * from pg_user where usename = 'user004' ;
usename | usesysid | usecreatedb | usesuper | usecatupd | passwd | valuntil | useconfig
---------+----------+-------------+----------+-----------+----------+------------------------+-----------
user004 | 104 | f | t | f | ******** | 2015-03-31 00:00:00+00 |
(1 row)
I did not grant CREATEDB to this user. However, this user able to create a database.
redshift3=# create database mydb4 ;
CREATE DATABASE
redshift3=# \list
List of databases
name | owner | encoding | access privileges
----------------+---------+----------+-------------------
mydb4 | user004 | UNICODE |
In pg_user, property shows false. Please guide me. Is this default behaviour in Redshift ?
You gave the user the CREATEUSER privilege which makes the user into a superuser and therefore they can do whatever they want. See http://docs.aws.amazon.com/redshift/latest/dg/r_superusers.html for details.