Postgres query about the roles of each user - sql

I'm trying to make a query that shows the users and all the roles that they have. I already know how to ask about the roles of one particular user:
SELECT oid, rolname FROM pg_roles WHERE
pg_has_role( 'name_of_user', oid, 'member');
Any idea how to do it?

run psql -E to see statements behind meta commands in psql:
vao=# \du
********* QUERY **********
SELECT r.rolname, r.rolsuper, r.rolinherit,
r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,
r.rolconnlimit, r.rolvaliduntil,
ARRAY(SELECT b.rolname
FROM pg_catalog.pg_auth_members m
JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)
WHERE m.member = r.oid) as memberof
, r.rolreplication
, r.rolbypassrls
FROM pg_catalog.pg_roles r
ORDER BY 1;
**************************
List of roles
Role name | Attributes | Member of
-----------+------------------------------------------------------------+-----------
ro | Cannot login | {}
rw | Cannot login | {}
vao | Superuser, Create role, Create DB, Replication, Bypass RLS | {ro,rw}

Related

Why is my SQL Left Join not working?

I am trying to execute a basic left join.
Structure of the tables are :
AGR_USERS : User (UNAME) - > AGR_NAME (Role) relationship - 1 to many
AGR_AGRS : Role (AGR_NAME) - > Child Role (CHILD_AGR) relationship - 1 to many
However the results are not accurate, since the query is not identifying the child roles(CHILD_AGR) present within the role (AGR_NAME) for users (UNAME).
Could you please help me to resolve the same. Below is the query which i am using.
SELECT
agr_users.UNAME,
agr_users.AGR_NAME,
AGR_AGRS.CHILD_AGR,
FROM AGR_USERS LEFT JOIN AGR_AGRS
ON AGR_AGRS.AGR_NAME=agr_users.AGR_NAME
**** MS-SQL server
Below is table data
Table 1 :AGR_AGRS ( role - child role table )
AGR_NAME CHILD_AGR
abc x
abc y
Table 2 : AGR_USERS ( User - role table)
UNAME AGR_NAME
nik abc
Expected result : ( User - role - child role )
UNAME AGR_NAME CHILD_AGR
nik abc x
nik abc y
This is a very straightforward query. The below should get you the full list:
select u.UNAME
,a.AGR_NAME
,a.CHILD_AGR
from AGR_USERS as u
left join AGR_AGRS as a
on u.AGR_NAME = a.AGR_NAME
If it does not, your environment must be different to how you have represented it within your question.

PostgreSQL - additional condition over the given in WHERE clause in dependency on user's role

I have a table with such columns as a int, b boolean. All users in database have privileged or non-privileged role. Privileged users have access to all rows from table, non-privileged - only to those rows where b is true.
So when non-privileged user executes SELECT, UPDATE or DELETE query it must save it's WHERE condition but also filter all rows what aren't b.
Example: if we have in table:
a | c
--+--
1 | T
2 | T
3 | F
4 | F
and privileged user executes SELECT FROM table WHERE a > 1, he must get
a | c
--+--
2 | T
3 | F
4 | F
whilst non-privileged user on the same query must get
a | c
--+--
2 | T
Is there any ways to implement it using triggers or something?
if you have version 9.5 and higher - use
https://www.postgresql.org/docs/current/static/ddl-rowsecurity.html
In addition to the SQL-standard privilege system available through
GRANT, tables can have row security policies that restrict, on a
per-user basis, which rows can be returned by normal queries or
inserted, updated, or deleted by data modification commands. This
feature is also known as Row-Level Security.
One method users a view:
create view v_table as
select t.*
from table t
where c = 'F' or
exists (select 1 from users u where u.user = current_user and u.role = 'privileged');
Then, access the table only through the view.

How to select all the offers a user earned?

I am trying to do a SELECT to get the offers a certain user have earned. However, I cannot figure how to do it.
I have three tables
user: id | name
offer: id | name
user_offer: id | user_id | offer_id
How do I select all offer the user 1 has?
This should work:
SELECT offer.name
FROM offer INNER JOIN user_offer
ON user_offer.offer_id=offer.id
WHERE user_offer.user_id='1'

emulate group_concat in MSSQL, ambiguous column name?

I am currently trying to simulate the group_concat function in MySQL to MSSQL. I have followed code formats found in here and here. The problem is, when I try to execute the query, I get the message Ambiguous column name for my column RoleID. Here is my query:
select UserName, RoleID from tb_UserInRoles
cross apply(
select RoleName from tb_Roles
where tb_UserInRoles.RoleID = tb_Roles.RoleID
for XML Path('')
) fxMerge (RoleID) group by UserName, RoleID
I'd like to know why this particular code format present the Ambiguous column name error message. I need to make this query work and understand it. Thanks in advance for the help.
I plan on using this in a many-to-many relationship table, wherein users of a system can have multiple roles, like this:
| User | Role |
|--------|---------|
| JamesP | Maker |
| JamesP | Approver|
| JamesP | Admin |
I want the result query to be like this:
| User | Role |
|--------|--------------------------|
| JamesP | Maker, Approver, Admin |
Try this:
SELECT UIR.UserName, MAX(STUFF(fxMerge.RoleID, 1, 1, ''))
FROM tb_UserInRoles UIR
CROSS APPLY(
SELECT ',' + RoleName
FROM tb_UserInRoles UIR1
INNER JOIN tb_Roles RM ON UIR1.RoleID = RM.RoleID
WHERE UIR.UserName = UIR1.UserName
FOR XML PATH('')
) fxMerge (RoleID)
GROUP BY UIR.UserName

Postgresql - Copy row and set uid to new column

I'm working on a user management system and I need to copy a user to a "backup" table before the user will be deleted. How can I set the id to the new column userid while id on both tables are unique?
users
+----+------+-------------+--+
| id | lang | email | |
+----+------+-------------+--+
| 20 | en | test#ya.hoo | |
+----+------+-------------+--+
delusers
+----+--------+------+-------------+
| id | userid | lang | email |
+----+--------+------+-------------+
| 1 | 20 | en | test#ya.hoo |
+----+--------+------+-------------+
First, if an user cannot be deleted twice, the delusers.id could be the PRIMARY KEY of delusers table, and you could use the value of the id of the user itself. There is no need of id and userid on delusers table.
Then, you can just INSERT on delusers and DELETE on users (inside the same transaction, of course):
BEGIN;
INSERT INTO delusers(id,lang,email)
SELECT id,lang,email FROM users WHERE id = 20;
DELETE FROM users WHERE id = 20;
COMMIT;
You could also do that on same command (using CTE):
WITH deleted AS (
DELETE FROM users WHERE id = 20 RETURNING id, lang, email
)
INSERT INTO delusers(id,lang,email)
SELECT id,lang,email FROM deleted;
The last is good for some reasons:
You don't need to explicit open a transaction if this is all you are going to do (it would make no harm in opening it though);
You can delete lots of users at same time;
PostgreSQL does not need to find for the users at users table more than once (one for SELECT and other for DELETE). The same could be achieved with cursors though.