Managing Roles in PostgreSQL

In this exercise, some role managing and security operations are performed. Firstly, two accounts with login credentials are added, one of which will only be valid until a certain time.

CREATE ROLE carto_01 LOGIN PASSWORD 'xxxxxxxxxxxxxxxx';
CREATE ROLE intern_01 LOGIN PASSWORD 'xxxxxxxxxxxxxxxx' VALID UNTIL '2021-03-01'; 
Image 1: Screenshot of newly created roles in pgAdmin.

The usernames have been created now, both the passwords were set by the administrator. To force the user to set a new password, we could ask them to log on via console and run the password command. The user will be queried for a new password and asked to confirm the password, which will not be visible for the administrator. The admin can only access the md5 hash, which over the length of 10 characters are almost uncrackable if the password does not appear on any password lists.

postgres=# \password
Enter new password:
Enter it again:
postgres=# 
Image 2: pg_shadow table showing the created usernames, their password hashes and the duration of the account's validity.

The users are then given read-only permissions with the following command.

Now, when trying to perform database operations such as adding or deleting entries, the database throws an error because the user does not have the required permissions.

GRANT SELECT ON salzburg_buildings TO carto_01;
GRANT SELECT ON salzburg_buildings TO intern_01; 
sql_sec_3
Image 3: Error thrown by the QGIS Database manager.

To allow less privileged users to perform certain queries, a view with predefined operations is created. The columns which the users are allowed to see are chosen, therefore the other secret columns remain hidden in the view.

CREATE OR REPLACE VIEW intern_view AS
SELECT
b.id,
b.name,
b.geom,
b.osm_id,
b.code,
b.fclass,
b.type
FROM
salzburg_buildings AS b;

GRANT SELECT ON intern_view To intern_01; 
Image 4: Return of the view for the intern.

Next, the query is additionally restricted in order to only give the intern the possibility to view objects from the buildings dataset within a certain geographic area. A box is created and intersected with the whole database. In the end, only the buildings within the box are returned. 

CREATE OR REPLACE VIEW intern_view AS
SELECT
b.id,
b.name,
b.geom,
b.osm_id,
b.code,
b.fclass,
b.type
FROM
salzburg_buildings AS b,
(SELECT geom FROM security_geofence WHERE project = 'intern') AS i
WHERE
st_intersects(b.geom, i.geom); 
Image 5: Geometries of the objects returned for from the geofenced intern view query.

Now, the view is additionally restricted, returning only those buildings where the security_level column is set to ‘public’.

CREATE OR REPLACE VIEW intern_view AS
SELECT
b.id,
b.name,
b.geom,
b.osm_id,
b.code,
b.fclass,
b.type
FROM
salzburg_buildings AS b,
(SELECT geom FROM security_geofence WHERE project = 'intern') AS i
WHERE
st_intersects(b.geom, i.geom) AND b.security_level='public'; 
Image 6: Intern view, geofenced and only showing public records.

Performing this filtering of course also eliminates the building footprints. Since only the other columns are sensitive, we can keep the geometries but hide these columns from the interns.

The following command basically created two tables, once for all geometries containing only the id, name and geom and once for the columns the intern is allowed to see in general, which are then combined using the UNION statement. The table where all but the public rows are included is filled up with null values in order to have the same column numbers, which is necessary to complete the union statement.

The final result is a view that returns all building geometries of the geofenced area, but the table only contains the sensitive information of the public records. 

CREATE OR REPLACE VIEW intern_view AS
SELECT
b.*
FROM
(SELECT
b.id,
b.name,
b.geom,
b.osm_id,
b.code,
b.fclass,
b.type
FROM
salzburg_buildings AS b
WHERE
b.security_level = 'public'

UNION

SELECT
n.id, n.name, n.geom, null, null, null, null
FROM
salzburg_buildings AS n
WHERE
n.security_level != 'public') AS b,
(SELECT geom FROM security_geofence WHERE project = 'intern') AS i
WHERE
st_intersects(b.geom, i.geom); 

There are of course many scenarios where the measures implemented in this task are not sufficient, some of them are listed below.

  • For now we only defined viewing rights, which is not always helpful. If we want the interns create new records, we need to prevent them from altering or dropping unrelated entries.
  • Maybe applications need to and are allowed to view certain entries/tables without being signed in at all, for example app users.
  • When new tables are created, the view needs to be adapted or a new one created. Maybe the intern should be able to access all tables within a certain geofence?
Further Reading
Recent Updates