Roles permissions and user roles, proper mapping? - postgresql

I have a basic role based access control. Single table for permission (name, description), single table for role (name, description). Question is, how to make proper mapping with the users? So in my mind comes 1 solution:
role_permission_set - table
(role_permission_id PK, role_id (many-to-one), permission_id (many-to-one))
user_role - table
(user_role_id PK, user_id (many-to-one), role_permission_id (many-to-one))
Two tables for mapping roles permissions and users roles. Is there a better way of doing this?

You have this right.
Break it down into two independent many-to-many relationships. One is between user and role. The other is between role and permission.
Each of those two many-to-many relationships will need a join table similar to what you described.
The only mistake I see in your post is that user_role should contain the role_id instead of the role_permission_id.
create table user_role (
user_role_id int serial primary key,
user_id int not null references user(user_id),
role_id int not null references role(role_id),
constraint uq_user_role unique(user_id, role_id)
);
When you want to get a list of permission values for a user:
select distinct p.name
from user_role ur
join role_permission rp on rp.role_id = ur.role_id
join permission p on p.permission_id = rp.permission_id
where ur.user_id = 123;

Related

Postgres relations for owner and users

I'm doing an exercise to learn database designs. This is some kind of fantasy soccer teams. I have this initial tables, users and teams. One team can have many users and a user can join and belongs to one team.
USERS
PK id
FK team_id -> TEAMS.id
username
TEAMS
PK id
name
But I also want to allow users to create their own teams if they don't want to join another team. What if I added owner_id in teams table?
TEAMS
PK id
FK owner_id -> USERS.id
name
I feel like this is not the right way to do. What is the best way to do this?
You could have a flag in users that indicates if the user owns the team they're a member of and a partial unique index to restrict this flag only being set once for a team.
CREATE TABLE teams
(id serial,
owner_id integer,
PRIMARY KEY (id));
CREATE TABLE users
(id serial,
team_id integer
NOT NULL,
is_owner boolean
NOT NULL
DEFAULT FALSE,
PRIMARY KEY (id),
FOREIGN KEY (team_id)
REFERENCES teams
(id));
CREATE UNIQUE INDEX users_team_id_is_owner
ON users
(team_id)
WHERE is_owner;
db<>fiddle
The index only includes the row that satisfy the WHERE clause, thus all of them have the flag is_owner set to true. Within that subset of rows the team_id must be unique which follows that there can not be any two rows with the same team_id and is_owner set to true.
To ensure a user must be a member of a team, set the team_id to be NOT NULL.
There can be teams without owners in that schema though. But as I understood your question, that is OK.

How would I associate users with different company tables?

In the below diagram you can see I have profiles for users. A user means the profile is associated with a login. If they don't have a login account they are merely a "contact" at the company tables you see on the right.
I am creating a CRM that needs to be able to store many facilities, customers, vendors, etc.
My question is how would I associate that profile with the company it belongs too? If I went to there profile page, for example, I should see what company they work for, regardless of what table the company is in.
I have 2 ideas but both seem flawed. One is to make a separate profile table for contacts for each. Example. vendor_profiles, customer_profiles, etc. This seems messy.
My other idea was to make a vendor_id, customer_id, etc column in the profile table, set them all to NULL by default, and if they belong to a vendor then my application code would check each for a number, and I think you get where I am going from there. Again, seems very messy, and inefficient.
I also considered grouping the Vendors, Customers, so on into one table and just setting a "type" but they all store very different info and are used in different, and often, relational ways.
Introduce a new organization table which unifies those aspects of vendor, customer,
facility and our_company that they all share in common:
CREATE TABLE organization (
organization_id serial4
type text);
CREATE TABLE profile (
profile_id serial4
user_id int4 REFERENCES user
organization_id int4 REFERENCES organization
...);
Then add an organization_id foreign key to vendor, customer, facility and our_company
so these tables can be joined to organization when the extra info in those tables are needed.
CREATE TABLE vendor (
vendor_id serial4
organization_id int4 REFERENCES organization
vendor_specific_field text
...);
Note that if you use "unique" names for id fields (such as organization_id instead of id) which have a consistent meaning across all tables, then you can JOIN ... USING syntax
SELECT *
FROM profile p
INNER JOIN organization o USING (organization_id)
INNER JOIN vendor v USING (organization_id)
WHERE o.type = 'vendor'
instead of
SELECT *
FROM profile p
INNER JOIN organization o ON p.organization_id = o.id
INNER JOIN vendor v ON v.organization_id = o.id
WHERE o.type = 'vendor'
which may improve readability and reduce potential errors in join conditions. (You'll never have to wonder what field in one table connects to the id field in another table.)

Multiple entries in PostgreSQL

I'm using a PostgreSQL 9.3 Database to create a small Userdatabase. One user can be a part of different groups.
There are 2 Tables in the Database:
user and group
The data type of all columns is text
Every row in user is a single user, every row in group is a single group.One column in user should be groups
How may i configure the groups-column to add multiple entries of group to a single user
What you are describing is a standard many-to-many relationship in a relational database.
You solve this problem by creating a mapping table between users and groups:
If you have something like this currently:
create table users (id integer primary key, user_name text);
create table groups (id integer primary key, group_name text);
The mapping table would look something like this:
create table user_groups
(
user_id not null references users,
group_id not null references groups,
primary key (user_id, group_id)
);
By using (user_id, group_id) as the primary key, you make sure that a user can only be mapped to the same group once.

Insert multiple rows in role table based on condition

I have two tables one is role and another one is user_role. User_role table contains all users of particular role. Now, i added two extra new roles into the role table. I want to insert the rows into user_role table who has role_id=4 with the new role_ids. I cannot update exsting rows because i want exsting rows also means the users who have role_id=4 also belongs to new role_ids.
I tried in this way
INSERT INTO table2 (all_links, fields_one, fields_two)
select URI, fields, details FROM table1
WHERE date > "12-11-2013 00-00-00";
But here two tables are there, but in my case only one table is there. And in user_role table id is sequene so cant select from another table dirctely and do insert.
Please help on these.
You can select and insert records into the same table.
If I understood your use case correctly,
INSERT INTO user_role (role_id, <other columns>)
SELECT <new_role_id>, <other columns>
FROM
user_role
WHERE
role_id = 4.
This will insert all the existing records in user_role table with role_id as 4, into the same table with a different role_id. HTH.

Postgresql: How to relate data between two tables?

I've searched this question for Postgresql 9.3 and seen no solutions with adequate answers. The Psql documentation also wasn't helpful.
Say I have two tables:
users (username, password, id)
data (title, description, id)
I've created a foreign key, but I'm not exactly clear on how that works. I want users to be able to see and edit their data and I want the data to be related to the user. It's the kind of thing we see all the time on blogs or facebook or twitter.
How do I create a relationship between the tables so that each piece of data a user submits is associated with them?
Assuming id on both tables is the id of the user, what you need to get all users data is:
SELECT u.username, d.title, d.description
FROM users u
LEFT JOIN data d ON d.id = u.id
If there is no data, d.titlea and d.description will be NULL. If you only want users with data just replace the LEFT JOIN with a JOIN
EDIT: (based on comments)
If you want users to have ownership of the data and if the data only belongs to an user at a time you just need to have the user_id in the data table. I was assuming the id in table was of the user. In that case when creating the table you need to do something like:
CREATE TABLE data (
id SERIAL,
title varchar(50) NOT NULL,
description varchar(255),
user_id references users(id) -- this is your foreign key
);
and then to query is once again with user_id adjustements:
SELECT u.username, d.title, d.description
FROM users u
LEFT JOIN data d ON d.user_id = u.id