I'm creating a website and I only want certain users to be granted the privilege of creating a post.
I have a table named Accounts where each user's basic information is stored (Id, firstName, lastName, email). Should I include a createPrivilege attribute in the Accounts table?
Or should I create a separate table named Privileges where I have the columns (id, createPrivilege) and the Id is a foreign key referencing the Accounts table's Id attribute?
If "createPrivilege" is an attribute of "Account", and there is only one instance of that attribute for any given entity, the common pattern is to have that as a column in the Account.
However, that approach quickly becomes very messy - you're likely to have many privileges, and adding new columns for each will make the "account" table very messy.
The most common way to model privileges is to introduce the concept of "role". A single user typically has several roles. A role might be "anonymous user", "authenticated user", "moderator", etc.
So, my recommendation would be to have a table with "account", a table called "role", and a join table "account_role" with foreign keys to both.
Related
To comply with privacy guidelines, I want to binary obfuscate the contents of certain columns to other roles, including the administrative role/developers.
A table could look like this:
create table customer (
id serial primary key,
role text not null default session_user,
name text not null,
address text not null,
phone bigint default null
);
create policy customer_policy on customer
for all to public using (role = session_user);
In this example, the column contents such as name, address and phone should not be visible to other roles.
the policy only guarantees that roles of other database users cannot see the records, but the administrator with higher privileges, for example, can still see the data.
my idea is that a password is stored in another table that is created and changed by the respective role. the relevant columns are then encrypted and decrypted using this password.
How could this be implemented or does PostgreSQL already offer solutions for this?
I'm developing a web application with several roles for its users. The DB in use is Postgres 10. The roles are very different and have much non-overlapping data so that oftentimes a specific table should belong only to one role and not to another.
In the examples below, I'll use two roles: seller and buyer. The first role is assigned to a user which has some company and sells goods. The second role is assigned to a user who buys goods and can receive gifts. Seller cannot receive gifts and buyer cannot have a company. Both seller and buyer can have an avatar.
I'm considering the following four designs with their cons and pros.
Case 1
Here we have a role reference table which enumerates all the roles in the system (seller or buyer), user table with password and email of the user which references the role table, avatar, company and gifts_recieved tables reference user table.
PROs
No tables have NULL-able attributes
CONs
Trigger should be used to check referential integrity. For example, if a user with id=1 is a buyer, we should prevent insertion of a row to the company table with user_id=1. Thus, we should create a trigger on every role-specific table which references user table to guard referential integrity.
Case 2
Here, user table contains nullable role attributes: seller_id and buyer_id (or other roles which are not in the example). Only one of them is not NULL and it defines user's role. All role-specific tables reference either seller or buyer table. Avatar table which is common for both roles continues to reference the user table.
PROs
No need for triggers to guard referential integrity of role-specific tables: they all reference either seller or buyer table.
CONs
NULL-able role attributes.
A single trigger (not triggers for all tables as in case 1) is required to ensure only one of role attributes (seller_id or buyer_id) is not NULL and all other are NULL.
Case 3
To avoid NULL-able role attributes, we'll use Postgres' inheritance for tables (this feature is not supported by MySQL). Here, user table is 'swallowed' into seller and buyer tables. Avatar table has got duplicated and prefixed with role name.
PROs
No NULL-able role attributes.
No triggers at all.
CONs
Tables which are common for different roles are duplicated - a pure waste of resources.
Inheritance is Postgres-specific feature.
Case 4
To solve the duplication problem from Case 3, avatar_id was moved to user table from which seller and buyer inherit. Thus, for every table which is common to seller and buyer we'll add an attribute to their parent table (user).
PROs
No triggers for referential integrity
No table duplication
No NULL-able atrrributes
CONs
Postgres inheritance has its caveats: https://www.postgresql.org/docs/10/ddl-inherit.html
Question
What solution of the above fit the best DB design practices (if any)? Should user role be an attribute of user table or be a specific table(s)?
Clarifications
A seller can have only one company.
A user identified by email/password pair can be either a seller or a buyer.
I am partial to Case 2.
I would not consider Cases 3 and 4 because of the use of inheritance. I have not visited this topic in many years, but my biggest concern about it was whether developers using a database implementing inheritance would be able to wrap their minds around how to use it.
The trigger is the sticking point for Case 1 in my mind.
Are you aware that you do not need a trigger in Case 2?
create table exclusion_constraint (
user_id int,
seller_id int,
buyer_id int,
check (case
when coalesce(seller_id, buyer_id) is null then false
when seller_id is not null and buyer_id is not null then false
else true
end)
);
CREATE TABLE
insert into exclusion_constraint values (1, 100, 200);
ERROR: new row for relation "exclusion_constraint" violates check constraint "exclusion_constraint_check"
DETAIL: Failing row contains (1, 100, 200).
insert into exclusion_constraint values (2, null, 200);
INSERT 0 1
insert into exclusion_constraint values (3, 100, null);
INSERT 0 1
insert into exclusion_constraint values (4, null, null);
ERROR: new row for relation "exclusion_constraint" violates check constraint "exclusion_constraint_check"
DETAIL: Failing row contains (4, null, null).
I am an Oracle DBA, new to PostgreSQL. I have a requirement to separate some modules data, so that one module will not have access to read the data of another module.
I read about the use of schemas in PostgreSql which is somewhat different than the use of it in Oracle. However seems like it is recommended to use the method of different schemas for separation and management - just like in Oracle.
However, when I create 2 schemas - connected to the same database and same user - I can do a select from the other schema's table.
That is, for example, if I have schema A owning table a, and schema B owning table b, when I set the search path to schema B I can do a select on schema’s A table a without any problem.
I couldn't find a way to revoke this privilege from schema B.
The only I could find then to separate access to data, is using different roles, that is to create role A with schema A, and role B with schema B. then I can grant and revoke access from user B in order for it to see what I want from role's A tables.
Is this correct? Am I missing something?
if I have schema A owning table a
A schema does not "own" a table in Postgres - a user does. This is the same as in Oracle - the difference (and maybe where your confusion arises) is that in Oracle in a regular user can't create tables outside of a schema that has the same name as the user account.
So if user arthur creates a table in schema_a and one in schema_b, both tables are owned by arthur - not "the schema".
If you used different schemas/users in Oracle to separate data and to prevent access to the other user's tables, then you need two users and two schemas in Postgres as well.
The default search_path in Postgres is setup in a way, that (unqualified) tables are always first searched (and created) in a schema with the same name as the user running the statement.
So if you create two users and a schema with the user's name for each user, you pretty much have the same setup as in Oracle:
create user arthur with password 'foobar';
create schema arthur authorization arthur; --<< this is what Oracle does "automatically"
create user bob with password 'verysecret';
create schema bob authorization bob;
Now, if bob creates a table, that table is created in the schema bob and is owned by the user bob. User arthur has not privileges to access that table.
If you never need to share data between those users (or schemas), then you can create two databases, create two users and let both users create everything in the public schema of "their" database.
Pretend I have a users table where users are members of a specific tenant, and their e-mails are uniquely indexed to their tenant, like this:
User
id | tenant_id | email
1 1 person1#example.com
2 1 person2#example.com
This user is allowed because despite a duplicate e-mail, they are at a different tenant:
3 2 person1#example.com
This user is prevented because the e-mail is a duplicate at the same tenant:
4 2 person1#example.com <--- will throw an error
We have this much covered with a unique index -- that part is easy.
Now pretend that I want to be able to add a global user that can access all tenants, but only if the e-mail doesn't already exist in the table at all. Additionally, once the record exists, nobody else -- whether tenanted or not -- will be able to use the same e-mail.
For clarity, the global users could simply have a null tenant ID but we would likely also add a global boolean.
Is there a way to write constraints for this logic? You can't simply make e-mails globally uniquely constrained because they won't be able to be repeated across tenants, and if you index with a null tenant ID, postgres will allow an untenanted user if there are tenanted users with the same e-mail.
I've looked at exclusion constraints and checks but couldn't figure out how to combine them (uniquely constrain e-mail globally if tenant_id is null, and check for records with null tenant ID and matching e-mail when inserting any record).
Please don't ask why I'm doing things this way -- my table isn't actually users and we've considered and dismissed other architectures :)
Thanks in advance!
According to PostgreSQL Documentation you can create unique partial index which will be effectively the same as creating unique partial constraint on your table:
CREATE UNIQUE INDEX some_index ON some_table (col_a) WHERE (col_b is null);
Using this technique you can create 2 separate unique indexes for admin and non-admin users.
You can use a UNIQUE constraint for both fields:
create table myUsers
(
id int not null,
tenant int not null,
email varchar(200) not null,
UNIQUE(email, tenant)
);
insert into myUsers values
(1, 1, 'person1#example.com'),
(2, 1, 'person2#example.com');
insert into myUsers values
(3, 2, 'person1#example.com');
Next insert will throw an error:
insert into myUsers values
(4, 2, 'person1#example.com');
Error(s), warning(s):
23505: duplicate key value violates unique constraint
"myusers_email_tenant_key"
Check it here: http://rextester.com/AJZVI34616
For the second part of the question:
Now pretend that I want to be able to add a global user that can access all tenants, but only if the e-mail doesn't already exist in the table at all.
One solution could be to reserve a tenant for admin users:
tenant = 0 <-- admin users
But the UNIQUE constraint allow duplicated emails, I recommend you to add a rol field to this table, or have another table of admin users for this purpose.
In my case, we use two tables, and both have a rol field.
In my model I have a User table, Role table, and Organization table.
A User can have more Roles with more Organizations independently.
So far I saw in any tutorial that a link table has two foreign keys (left, right), but in my case I need a link table where there are User-ID, Role-ID, Organization-ID fields as foreign keys and primary keys as well.
some example:
user role organization
=== ==== ============
u1 admin orga1
u1 admin orga12
u1 reviewer orga3
u2 editor orga1
Thanks in advance!
I think what you actually want is something like a user table, and an OrgRole table. You then want a joint table User_OrgRole that joins them. You might want to place a constraint on the table to make sure Org and Role are unique together.
OrgRole might look like this:
OrgRoleID [KEY]
Org [FKey]
Role [FKey]
User_OrgRole might look like this:
UserID [Key]
OrgRoleID [Key]
You can certainly do the 3-way join manually, but you will not be able to create any many-to-many style navigational properties or relationships. You're instead creating what amounts to 3 one to many's.