TP TIW4 2020-2021 "autorisation" : rapport
==========================================
Introduction
------------
- Numéro de binôme
* Molinares Diogenes 12019196
* Bunel Maxime 11914012
- Nom DNS de la VM fournie
* 192.168.237.138
Prise en main
-------------
### Identification
#### Indiquer quelle sont les machines qui peuvent se connecter au serveur
Les machines sur sous-reseaux 192.168.237.128/27
#### Quel est le mode d'authentification imposé par PostgreSQL
Authentification avec utilisateur / mot de passe
### Création de bases et utilisateurs
#### Donner les requêtes SQL pour vérifier que les utilisateurs sont créés
```sql
-- ici les définitions
create user lambda1 with encrypted password 'lambda1';
create user lambda2 with encrypted password 'lambda2';
create user adjoint with encrypted password 'adjoint';
create user responsable with encrypted password 'responsable';
```
```sql
-- ici les vérifications
SELECT * FROM pg_roles;
SELECT 1 FROM pg_roles WHERE rolname='lambda1';
SELECT 1 FROM pg_roles WHERE rolname='lambda2';
SELECT 1 FROM pg_roles WHERE rolname='adjoint';
SELECT 1 FROM pg_roles WHERE rolname='responsable';
```
#### Montrer que vous avez vérifié que ces utilisateurs peuvent s'authentifier depuis une autre VM
```sql=
Avec ces commandes noux pouvons nous connecter au utilisateur
psql -U lambda1 -W -p 5432 -h 192.168.237.146 -d test
psql -U lambda2 -W -p 5432 -h 192.168.237.146 -d test
psql -U responsable -W -p 5432 -h 192.168.237.146 -d test
psql -U adjoint -W -p 5432 -h 192.168.237.146 -d test
```
Le modèle de contrôle d'accès _applicatif_
------------------------------------------
### Schéma de départ
#### Commenter chacune des lignes du début du script ci-dessous
```sql
--Creation d'un schéma nommé rbac si il n'existe pas déjà
CREATE SCHEMA IF NOT EXISTS rbac;
--Défini le chemin de recherche de la table test
ALTER DATABASE "test" SET search_path TO "$user", rbac, public;
-- Défini le chemin de recherche de la session actuelle
SET search_path TO "$user", rbac, public;
-- Supprime le type de données http_method si il existe
DROP TYPE IF EXISTS http_method;
-- Créer le type de données http_method qui est un enum qui peut avoir comme valeur (get,post,put,delete)
CREATE TYPE http_method AS ENUM ('GET', 'POST', 'PUT', 'DELETE', 'PATCH');
```
#### Expliquez ce qui change sur la hiérarchie de rôle selont qu'on prenne pour PK de `rbac.inherits` une des 4 possibilités suivantes
```sql
-- Evite les doublons dans la table, un enfant peut avoir plusieurs parents et un parent peut avoir plusieurs enfants
PRIMARY KEY (role_id_child, role_id_parent) --celle du script
-- un enfant ne peux avoir qu'un seul parent et un parent peut avoir plusieurs enfants
PRIMARY KEY (role_id_child)
-- un parent ne peux avoir qu'un seul enfant et un enfant peut avoir plusieurs parents
PRIMARY KEY (role_id_parent)
-- Possibilité d'avoir des doublons
-- pas de PK déclarée
```
### Calcul des décisions d'accès
#### Donner la définition et les tests de la vue `rbac.rbac_inherits_trans`
```sql
CREATE OR REPLACE RECURSIVE VIEW rbac.inherits_trans(role_id_child, role_id_parent) AS (
SELECT inherits.role_id_child, inherits.role_id_parent FROM rbac.inherits inherits
UNION SELECT role_id, role_id FROM rbac.role
UNION SELECT inherits.role_id_child, trans.role_id_parent FROM rbac.inherits inherits
INNER JOIN inherits_trans trans ON inherits.role_id_parent = trans.role_id_child
);
```
```sql
-- ici les tests sur le jeu d'essai
SELECT * FROM rbac.inherits_trans;
```
#### Donner la définition et les tests de la vue `rbac.rbac_matrix`
```sql
CREATE OR REPLACE VIEW rbac.rbac_matrix(usr_id, obj_id, act_id) AS(
SELECT usr_id, obj_id, act_id
FROM rbac.ura ura
INNER JOIN rbac.pra pra ON ura.role_id = pra.role_id
);
```
```sql
-- ici les tests sur le jeu d'essai
SELECT * FROM rbac.rbac_matrix;
```
#### Donner la définition et les tests de la fonction `rbac.authorized_actions`
```sql
CREATE OR REPLACE FUNCTION rbac.authorized_actions(u_name text, o_name text)
RETURNS TABLE(action_id http_method) AS
$$
SELECT DISTINCT act_id
FROM rbac.rbac_matrix
INNER JOIN rbac.usr usr ON usr.usr_name = u_name and usr.usr_id = rbac.rbac_matrix.usr_id
INNER JOIN rbac.obj obj ON obj.obj_name = o_name and obj.obj_id = rbac.rbac_matrix.obj_id
$$
LANGUAGE SQL
STABLE
RETURNS NULL ON NULL INPUT;
```
```sql
-- ici les tests sur le jeu d'essai
-- Cette commande va retourner que l'utilisateur 0 sur l'objet 0 à l'authorisation GET
select * from rbac.authorized_actions('U0', 'O0');
action_id
═══════════
GET
(1 row)
-- Cette commande retour aucun resultat
select * from rbac.authorized_actions('U0', 'O1');
```
#### Donner la définition et les tests de la fonction `rbac.count_auth_users`
```sql
CREATE OR REPLACE FUNCTION rbac.count_auth_users(r_id integer)
RETURNS bigint AS
$$
SELECT count(*)
FROM ( -- roles fils de r_id
SELECT DISTINCT trans.role_id_child
FROM rbac.inherits_trans trans
WHERE trans.role_id_parent = r_id
UNION
SELECT DISTINCT ura.role_id
FROM rbac.ura ura
WHERE ura.role_id = r_id
)
childs
INNER JOIN rbac.ura ura ON ura.role_id = childs.role_id_child
$$
LANGUAGE SQL STABLE RETURNS NULL ON NULL INPUT;
```
```sql
-- ici les tests sur le jeu d'essai
-- On applique le fonction sur l'utilisateur 3
SELECT * FROM rbac.count_auth_users(3);
count_auth_users
══════════════════
1
(1 row)
```
### Extension aux sessions
#### Donner les définitions des nouvelles tables et le trigger
```sql
-- ici les définitions
CREATE TABLE IF NOT EXISTS rbac.session(
usr_id integer REFERENCES rbac.usr(usr_id),
role_id integer REFERENCES rbac.role(role_id),
PRIMARY KEY (usr_id, role_id)
);
CREATE FUNCTION session_trigger() RETURNS trigger AS $session_trigger$
BEGIN
IF NEW.usr_id IS NULL THEN
RAISE EXCEPTION 'usr_id ne peut pas etre NULL.';
END IF;
IF NEW.role_id IS NULL THEN
RAISE EXCEPTION 'role_id ne peut pas etre NULL.';
END IF;
IF NEW.usr_id NOT IN (SELECT DISTINCT usr_id FROM rbac.usr) THEN
RAISE EXCEPTION 'utilisateur nexiste pas';
END IF;
IF NEW.role_id NOT IN (SELECT DISTINCT role_id FROM rbac.role) THEN
RAISE EXCEPTION 'Le role nexiste pas.';
END IF;
IF (NEW.usr_id, NEW.role_id) NOT IN (SELECT DISTINCT usr_id, role_id FROM rbac.ura) THEN
RAISE EXCEPTION 'utilisateur ne peut pas posséder ce role.';
END IF;
RETURN NEW;
END;
$session_trigger$ LANGUAGE plpgsql;
CREATE TRIGGER session_trigger BEFORE INSERT OR UPDATE ON rbac.session
FOR EACH ROW EXECUTE PROCEDURE session_trigger();
CREATE OR REPLACE FUNCTION rbac.authorized_actions_session(u_name text, o_name text)
RETURNS TABLE(action_id http_method) AS
$$
select distinct act_id
from rbac.rbac_matrix
INNER JOIN rbac.usr usr ON usr.usr_name = u_name and usr.usr_id = rbac.rbac_matrix.usr_id
INNER JOIN rbac.obj obj ON obj.obj_name = o_name and obj.obj_id = rbac.rbac_matrix.obj_id
INNER JOIN rbac.session session ON session.usr_id = usr.usr_id
$$
LANGUAGE SQL
STABLE
RETURNS NULL ON NULL INPUT;
```
#### Donner les tests du trigger
```sql
-- ici les tests sur le jeu d'essai
insert into rbac.session values (0,0);
-- INSERT 0 1
insert into rbac.session values (0,3);
ERROR: P0001: utilisateur ne peut pas posséder ce role.
CONTEXT: PL/pgSQL function session_trigger() line 16 at RAISE
insert into rbac.session values (NULL, NULL);
ERROR: P0001: usr_id ne peut pas etre NULL.
CONTEXT: PL/pgSQL function session_trigger() line 4 at RAISE
insert into rbac.session values (0, NULL);
ERROR: P0001: role_id ne peut pas etre NULL.
-- l'utilisateur 1 n'existe pas sur la table session, meme s'il a le droit d'acceder sur l'objet 1
select * from authorized_actions_session('U1', 'O1');
action_id
═══════════
(0 rows)
-- l'utilisateur 0 apparait sur la table session
select * from authorized_actions_session('U0', 'O0')
test-# ;
action_id
═══════════
GET
(1 row)
```
Gestion des droits sur le modèle
--------------------------------
### Modèle d'administration, base
#### Donner les `GRANT` et `REVOKE`
```sql
-- connection sur la bdd test
GRANT CONNECT ON DATABASE test TO public
REVOKE ALL PRIVILEGES ON SCHEMA RBAC from PUBLIC ;
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA RBAC from PUBLIC ;
GRANT USAGE ON SCHEMA RBAC to public;
GRANT SELECT ON ALL TABLES IN SCHEMA RBAC to public;
```
### Délégation à l'adjoint
#### Modification de la table
```sql
ALTER TABLE rbac.role ADD delegate BOOL DEFAULT 0 NOT NULL;
```
#### Donner le ROW LEVEL SECURITY pour `rbac.pra`
```sql
-- ici la définition de la politique RLS
CREATE POLICY policy_adjoint_insert ON rbac.pra
FOR INSERT
TO adjoint
WITH check (rbac.pra.role_id IN (
SELECT role_id FROM rbac.role WHERE delegate is true)
);
CREATE POLICY policy_adjoint_update ON rbac.pra
FOR UPDATE
TO adjoint
USING (rbac.pra.role_id IN (
SELECT role_id FROM rbac.role WHERE delegate is true)
);
CREATE POLICY policy_adjoint_delete ON rbac.pra
FOR DELETE
TO adjoint
USING (rbac.pra.role_id IN (
SELECT role_id FROM rbac.role WHERE delegate is true)
);
```
```sql
-- ici les tests sur le jeu d'essai
insert into rbac.pra values (5,1,'POST');
```
#### Donner le ROW LEVEL SECURITY pour `rbac.inherits`
```sql
-- ici la définition de la politique RLS
CREATE POLICY policy_adjoint_in_insert ON rbac.inherits
FOR INSERT
TO adjoint
WITH CHECK (rbac.inherits.role_id_parent IN (
SELECT role_id FROM rbac.role WHERE delegate is true
));
CREATE POLICY policy_adjoint_in_update ON rbac.inherits
FOR UPDATE
TO adjoint
WITH CHECK (rbac.inherits.role_id_parent IN (
SELECT role_id FROM rbac.role WHERE delegate is true
));
CREATE POLICY policy_adjoint_in_delete ON rbac.inherits
FOR DELETE
TO adjoint
USING (rbac.inherits.role_id_parent IN (
SELECT role_id FROM rbac.role WHERE delegate is true
));
```
```sql
-- ici les tests sur le jeu d'essai
select * from rbac.inherits;
```