|
| 1 | +-- Sequence aanmaken; als een nieuw login wordt aangemaakt door de WH-kant, zal een oneven ID worden uitgegeven. |
| 2 | +-- Als een nieuw ID wordt gegenereerd aan de datamart-kant, dan een even ID worden uitgegeven. |
| 3 | +CREATE SEQUENCE wh.login__login_id_seq |
| 4 | + INCREMENT 2 |
| 5 | + START 1 |
| 6 | + MINVALUE 1 |
| 7 | + MAXVALUE 2147483647 |
| 8 | + CACHE 1; |
| 9 | +ALTER SEQUENCE wh.login__login_id_seq OWNER TO dwh_owner; |
| 10 | + |
| 11 | + |
| 12 | +-- Table: wh.login |
| 13 | +-- DROP TABLE wh.login; |
| 14 | +CREATE TABLE wh.login ( |
| 15 | + login_id INTEGER NOT NULL DEFAULT NEXTVAL('wh.login__login_id_seq'::REGCLASS), |
| 16 | + login CHARACTER VARYING(33) NOT NULL, |
| 17 | + login_type_id INTEGER NOT NULL, |
| 18 | + naam CHARACTER VARYING(100) NOT NULL, |
| 19 | + is_vrouw BOOLEAN NOT NULL DEFAULT TRUE, |
| 20 | + emailadres CHARACTER VARYING(254) NOT NULL DEFAULT 'user@example.com', |
| 21 | + salt CHARACTER VARYING(29) NOT NULL DEFAULT generic.GEN_SALT('BF',13), |
| 22 | + wachtwoord CHARACTER VARYING(60) NOT NULL, |
| 23 | + secret_key CHARACTER VARYING(32) NOT NULL DEFAULT generic.gen_rnd_base32(32), |
| 24 | + geldig_vanaf_ts TIMESTAMP NOT NULL DEFAULT CLOCK_TIMESTAMP(), |
| 25 | + CONSTRAINT pk_login PRIMARY KEY (login), |
| 26 | + CONSTRAINT fk_login__login_type FOREIGN KEY (login_type_id) REFERENCES wh.login_type (login_type_id) ON UPDATE NO ACTION ON DELETE NO ACTION |
| 27 | +); |
| 28 | +ALTER TABLE wh.login OWNER TO dwh_owner; |
| 29 | +GRANT SELECT ON TABLE wh.login TO dwh_readonly; |
| 30 | + |
| 31 | +-- Index: idx_login__login_naam |
| 32 | +-- DROP INDEX idx_login__login_naam; |
| 33 | +CREATE UNIQUE INDEX idx_login__login_id ON wh.login (login_id) INCLUDE(login, naam); |
| 34 | + |
| 35 | +CREATE POLICY plc_rls_login ON wh.login USING (login = SESSION_USER); |
| 36 | + |
| 37 | +REVOKE SELECT ON TABLE wh.login FROM xxxxx; |
| 38 | +GRANT SELECT (login_id, login, login_type_id, naam, is_vrouw, emailadres, salt, wachtwoord, geldig_vanaf_ts) ON TABLE wh.login TO xxxxxxx; |
| 39 | + |
| 40 | + |
| 41 | + /* */ |
| 42 | +CREATE OR REPLACE FUNCTION wh.fnc_sync_login_role() |
| 43 | +RETURNS TRIGGER AS |
| 44 | + |
| 45 | +$BODY$ |
| 46 | +DECLARE |
| 47 | + -- INSERT-waarden: |
| 48 | + _login TEXT; |
| 49 | + _login_id INTEGER; |
| 50 | + _wachtwoord TEXT; |
| 51 | + |
| 52 | + _aantal INT; |
| 53 | + _aantal_versies INT; |
| 54 | + _aantal_dagen_valid INT; |
| 55 | + |
| 56 | + -- Einddatum geldigheid wachtwoord bepalen |
| 57 | + _geldig_tot TEXT; |
| 58 | + |
| 59 | +BEGIN |
| 60 | +-- Aantal versies van het wachtwoord 'onthouden' |
| 61 | +_aantal_versies := 5; |
| 62 | +-- Aantal dagen dat een wachtwoord geldig blijft |
| 63 | +-- Eventueel vanuit een meta-tabel ophalen, waarmee 'dynamisch' |
| 64 | +_aantal_dagen_valid := 60; |
| 65 | + |
| 66 | +-- Corrigeren |
| 67 | +NEW.login := LOWER(BTRIM(NEW.login)); |
| 68 | + |
| 69 | +IF TG_OP = 'INSERT' THEN |
| 70 | + -- Voor een INSERT, zou het login_id NULL moeten zijn en login NIET NULL |
| 71 | + IF NEW.login ~ '^_[\da-f]{32}$' THEN |
| 72 | + -- Bestaat de login reeds in wh.login? |
| 73 | + SELECT login_id, login INTO _login_id, _login |
| 74 | + FROM wh.login |
| 75 | + WHERE login = NEW.login; |
| 76 | + |
| 77 | + IF NOT FOUND THEN |
| 78 | + -- Deze mogen we toevoegen als ROLE, vermits deze niet al bestaat. |
| 79 | + -- Bestaat deze ROLE dus al? |
| 80 | + PERFORM 1 |
| 81 | + FROM pg_catalog.pg_roles |
| 82 | + WHERE LOWER(rolname) = NEW.login; |
| 83 | + |
| 84 | + -- ROLE bestaat dus niet - aanmaken: |
| 85 | + IF NOT FOUND THEN |
| 86 | + -- Salt genereren |
| 87 | + NEW.salt = generic.GEN_SALT('BF',13); |
| 88 | + |
| 89 | + -- Feitelijk de ROLE aanmaken |
| 90 | + IF NEW.wachtwoord IS NULL THEN |
| 91 | + -- Geldig-tot bepalen ; dit is de geldigheidsduur van het account / ROLE. Als het wachtwoord leeg wordt gelaten, deze dan gelijk nemen aan het login ('_'+MD5(emailadres)) |
| 92 | + -- Zodra het wachtwoord gewijzigd wordt, word ook de geldigheidsduur aangepast. |
| 93 | + SELECT (CURRENT_TIMESTAMP + INTERVAL '7 DAY')::TEXT INTO _geldig_tot; --Inclusief het huidige tijdstip... |
| 94 | + -- Wachtwoord is niet ingevuld, dus default op de gebruikersnaam instellen |
| 95 | + EXECUTE FORMAT('CREATE ROLE %s ENCRYPTED PASSWORD %s NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL %s;' , NEW.login, QUOTE_LITERAL(NEW.login), QUOTE_LITERAL(_geldig_tot) ); |
| 96 | + |
| 97 | + -- Nu het 'wachtwoord' omwerken naar de encrypted variant |
| 98 | + NEW.wachtwoord = generic.CRYPT(NEW.login,NEW.salt); |
| 99 | + ELSE |
| 100 | + -- Geldig-tot bepalen ; 60 dagen voor een net, ingevuld en goedgekeurd wachtwoord |
| 101 | + EXECUTE FORMAT ('SELECT (CURRENT_TIMESTAMP + INTERVAL ''%s DAY'')::TEXT', _aantal_dagen_valid) INTO _geldig_tot; |
| 102 | + EXECUTE FORMAT('SET password_encryption = ''SCRAM-SHA-256'';'); |
| 103 | + EXECUTE FORMAT('CREATE ROLE %s ENCRYPTED PASSWORD %s NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL %s;' , NEW.login, QUOTE_LITERAL(NEW.wachtwoord), QUOTE_LITERAL(_geldig_tot) ); |
| 104 | + |
| 105 | + -- Nu het opgegeven wachtwoord omwerken naar de encrypted variant |
| 106 | + NEW.wachtwoord = generic.CRYPT(NEW.wachtwoord,NEW.salt); |
| 107 | + |
| 108 | + END IF; |
| 109 | + |
| 110 | + -- Nieuwe wachtwoord / salt kopieren naar wh.login_wachtwoord |
| 111 | + EXECUTE FORMAT ('INSERT INTO wh.login_wachtwoord (login_id, login, salt, wachtwoord, geldig_vanaf_ts) VALUES(%s,%s,%s,%s,CLOCK_TIMESTAMP());', |
| 112 | + NEW.login_id, NEW.login, QUOTE_LITERAL(NEW.salt), QUOTE_LITERAL(NEW.wachtwoord)); |
| 113 | + |
| 114 | + -- Rechten |
| 115 | + EXECUTE FORMAT ('GRANT web_app_login TO %s ;', NEW.login); |
| 116 | + EXECUTE FORMAT ('GRANT web_login TO %s ;', NEW.login); |
| 117 | + |
| 118 | + -- Het wachtwoord-type zetten op 'SCRAM-SHA-256' |
| 119 | + EXECUTE FORMAT ('ALTER ROLE %s SET password_encryption = ''SCRAM-SHA-256'';', NEW.login); |
| 120 | + RETURN NEW; |
| 121 | + ELSE |
| 122 | + -- ROLE bestaat al |
| 123 | + RAISE NOTICE 'ROLE % bestaat al - niet aangemaakt en geen INSERT gedaan!', _login; |
| 124 | + RETURN NULL; |
| 125 | + END IF; |
| 126 | + ELSE |
| 127 | + -- Gevonden, dus bestaat al |
| 128 | + RAISE NOTICE 'Login % bestaat al (geregistreerd onder id: %)', NEW.login, _login_id; |
| 129 | + RETURN NULL; |
| 130 | + END IF; |
| 131 | + ELSE |
| 132 | + -- Melding doen |
| 133 | + RAISE NOTICE 'Login % voldoet niet aan de regels van een login ("_"+MD5(emailadres))', NEW.login; |
| 134 | + |
| 135 | + -- TODO: eventueel nog een ROLE aanmaken (default apotheker) op basis van emailadres... (tenzij emailadres leeg) |
| 136 | + RETURN NULL; -- Verder niets meer doen, dus de INSERT feitelijk annuleren |
| 137 | + END IF; |
| 138 | + |
| 139 | + -- ------------------------------------------------------------------------------------------------ |
| 140 | +ELSEIF TG_OP = 'UPDATE' THEN |
| 141 | + -- Is wachtwoord gewijzigd? |
| 142 | + IF OLD.wachtwoord != NEW.wachtwoord THEN |
| 143 | + IF NEW.salt = OLD.salt OR NEW.salt !~ '^\$2a\$13\$' OR LENGTH(NEW.salt) != 29 THEN |
| 144 | + -- Nieuwe SALT voldoet niet |
| 145 | + NEW.salt = generic.GEN_SALT('BF',13); |
| 146 | + END IF; |
| 147 | + |
| 148 | + -- LOGIN (=ROLE) bepalen |
| 149 | + SELECT login, login_id INTO _login, _login_id |
| 150 | + FROM wh.login |
| 151 | + WHERE login_id = NEW.login_id; |
| 152 | + |
| 153 | + -- Nu de controle of dit wachtwoord 'mag' - oftewel, het is niet gelijk aan één |
| 154 | + -- van de laatste 5 wachtwoorden. |
| 155 | + -- LET OP: Dit duurt wat langer, vanwege het bepalen van maximaal _aantal_versies de Blowfish(13) |
| 156 | + PERFORM TRUE |
| 157 | + FROM wh.login_wachtwoord AS lw |
| 158 | + WHERE lw.login_id = _login_id AND |
| 159 | + generic.CRYPT(NEW.wachtwoord,salt) = lw.wachtwoord |
| 160 | + ; |
| 161 | + |
| 162 | + IF NOT FOUND THEN |
| 163 | + -- JA, dit wachtwoord 'mag' |
| 164 | + |
| 165 | + -- Wachtwoord van de ROLE wijzigen voordat deze wordt ge-encrypt |
| 166 | + EXECUTE FORMAT('SET password_encryption = ''SCRAM-SHA-256'';'); |
| 167 | + EXECUTE FORMAT('ALTER ROLE %s PASSWORD %s;', _login, QUOTE_LITERAL(NEW.wachtwoord)); |
| 168 | + |
| 169 | + -- En de geldigheid aanpassen |
| 170 | + EXECUTE FORMAT ('SELECT (CURRENT_TIMESTAMP + INTERVAL ''%s DAY'')::TEXT', _aantal_dagen_valid) INTO _geldig_tot; |
| 171 | + EXECUTE FORMAT('ALTER ROLE %s VALID UNTIL %s;', _login, QUOTE_LITERAL(_geldig_tot)); |
| 172 | + |
| 173 | + -- De Blowfish-variant: |
| 174 | + NEW.wachtwoord = CRYPT(NEW.wachtwoord,NEW.salt); |
| 175 | + |
| 176 | + -- Nieuwe wachtwoord / salt kopieren naar wh.login_wachtwoord |
| 177 | + EXECUTE FORMAT ('INSERT INTO wh.login_wachtwoord (login_id, login, salt, wachtwoord, geldig_vanaf_ts) VALUES(%s,%s,%s,%s,CLOCK_TIMESTAMP());', |
| 178 | + _login_id, NEW.login, QUOTE_LITERAL(NEW.salt), QUOTE_LITERAL(NEW.wachtwoord)); |
| 179 | + |
| 180 | + ELSE |
| 181 | + -- NEE, dit wachtwoord mag niet; melding maken |
| 182 | + RAISE NOTICE 'Het wachtwoord "%" (login_id: %) is gelijk aan één van de laatste 5 wachtwoorden - niet toegestaan; wachtwoord (en salt) NIET gewijzigd.', NEW.wachtwoord, NEW.login_id; |
| 183 | + -- Laten zoals het was: |
| 184 | + NEW.wachtwoord = OLD.wachtwoord; |
| 185 | + NEW.salt = OLD.salt; |
| 186 | + END IF; |
| 187 | + |
| 188 | + END IF; -- Einde wachtwoord gewijzigd |
| 189 | + |
| 190 | + -- Is LOGIN gewijzigd? |
| 191 | + IF NEW.login != OLD.login THEN |
| 192 | + -- Het wachtwoord is NIET langer 'afhankelijk' is van de ROLE (zoals in het geval van het MD5-wachtwoord) |
| 193 | + -- (vanwege gebruik SCRAM-SHA-256) en hoeft daarom ook niet gewijzigd te worden. |
| 194 | + EXECUTE FORMAT('ALTER ROLE %s RENAME TO %s;', OLD.login, NEW.login); |
| 195 | + END IF; -- Einde Login gewijzigd |
| 196 | + |
| 197 | + RETURN NEW; -- Update gewoon uitvoeren |
| 198 | + -- ------------------------------------------------------------------------------------------------ |
| 199 | +ELSEIF TG_OP = 'DELETE' THEN |
| 200 | + -- ROLE bepalen: |
| 201 | + SELECT login_id, login INTO _login_id, _login |
| 202 | + FROM wh.login |
| 203 | + WHERE login = OLD.login; |
| 204 | + |
| 205 | + -- 'Gewoon' verwijderen |
| 206 | + EXECUTE FORMAT('DROP USER IF EXISTS %s;',LOWER(OLD.login)); |
| 207 | + RAISE NOTICE 'Verwijderen ROLE "%" (delete id: %)', _login, _login_id; |
| 208 | + RETURN NEW; -- En de delete gewoon laten uitvoeren |
| 209 | + -- ------------------------------------------------------------------------------------------------ |
| 210 | +ELSEIF TG_OP = 'TRUNCATE' THEN |
| 211 | + -- Dus een 'truncate'; alle users conform wh.login verwijderen |
| 212 | + FOR _login IN SELECT login FROM wh.login LOOP |
| 213 | + RAISE NOTICE 'Verwijderen gebruiker "%", vanwege truncate', _login; |
| 214 | + EXECUTE 'DROP USER IF EXISTS ' || LOWER(_login) || ';'; |
| 215 | + END LOOP; |
| 216 | + RETURN OLD; -- ... de truncate gewoon laten uitvoeren |
| 217 | + -- ------------------------------------------------------------------------------------------------ |
| 218 | +END IF; |
| 219 | + |
| 220 | +END; |
| 221 | + |
| 222 | +$BODY$ |
| 223 | +LANGUAGE plpgsql; |
| 224 | + |
| 225 | +ALTER FUNCTION wh.fnc_sync_login_role() OWNER TO dwh_owner; |
| 226 | + |
| 227 | +-- Trigger: trg_login_bijwerken on login |
| 228 | +-- DROP TRIGGER trg_login_bijwerken ON login; |
| 229 | +CREATE TRIGGER trg_login_bijwerken |
| 230 | + BEFORE INSERT OR UPDATE OR DELETE |
| 231 | + ON wh.login |
| 232 | + FOR EACH ROW |
| 233 | + EXECUTE PROCEDURE wh.fnc_sync_login_role(); |
| 234 | +CREATE TRIGGER trg_login_truncate |
| 235 | + BEFORE TRUNCATE |
| 236 | + ON wh.login |
| 237 | + EXECUTE PROCEDURE wh.fnc_sync_login_role(); |
| 238 | + |
| 239 | + |
| 240 | + /* ------------------------------------------------------------------------------------------------------------------------------------------------------------- */ |
| 241 | + /* ------------------------------------------------------------------------------------------------------------------------------------------------------------- */ |
| 242 | + |
| 243 | +-- Table: login_wachtwoord |
| 244 | +-- DROP TABLE login_wachtwoord; |
| 245 | +CREATE TABLE wh.login_wachtwoord( |
| 246 | + login_id INTEGER NOT NULL, |
| 247 | + login CHARACTER VARYING(33) NOT NULL, |
| 248 | + geldig_vanaf_ts TIMESTAMP(6) WITH TIME ZONE NOT NULL DEFAULT CLOCK_TIMESTAMP(), |
| 249 | + salt CHARACTER VARYING(29) NOT NULL DEFAULT generic.GEN_SALT('BF',13), |
| 250 | + wachtwoord CHARACTER VARYING(60) NOT NULL, |
| 251 | + CONSTRAINT pk_login_wachtwoord PRIMARY KEY (login_id, login, geldig_vanaf_ts), |
| 252 | + CONSTRAINT fk_login_wachtwoord__login FOREIGN KEY (login) REFERENCES wh.login (login) ON UPDATE CASCADE ON DELETE CASCADE INITIALLY DEFERRED |
| 253 | +); |
| 254 | +ALTER TABLE wh.login_wachtwoord OWNER TO dwh_owner; |
| 255 | +GRANT SELECT ON TABLE wh.login_wachtwoord TO GROUP dwh_readonly; |
| 256 | +CREATE POLICY plc_rls_login_wachtwoord ON wh.login_wachtwoord USING (login = SESSION_USER); |
| 257 | + |
| 258 | + /* Functie voor het maximeren van het aantal 'onthouden' wachtwoorden */ |
| 259 | +CREATE OR REPLACE FUNCTION wh.fnc_prune_pw_history() |
| 260 | +RETURNS TRIGGER AS |
| 261 | + |
| 262 | +$BODY$ |
| 263 | +DECLARE |
| 264 | + _aantal_versies INT; -- Aantal versies van het wachtwoord dat bewaard moet blijven |
| 265 | +BEGIN |
| 266 | + -- Vijf historische en het huidige (5+1) |
| 267 | + _aantal_versies := 6; |
| 268 | + |
| 269 | + DELETE FROM wh.login_wachtwoord AS lw |
| 270 | + USING ( SELECT login_id, |
| 271 | + geldig_vanaf_ts |
| 272 | + FROM ( SELECT login_id, |
| 273 | + geldig_vanaf_ts, |
| 274 | + ROW_NUMBER() OVER (PARTITION BY login_id ORDER BY geldig_vanaf_ts DESC) AS rijnr |
| 275 | + FROM wh.login_wachtwoord |
| 276 | + ) AS l |
| 277 | + WHERE rijnr = _aantal_versies |
| 278 | + ) AS d -- Alleen de _aantal_versies-de rij bewaren; deze datum is het afkap-punt |
| 279 | + WHERE lw.login_id = d.login_id AND |
| 280 | + lw.geldig_vanaf_ts < d.geldig_vanaf_ts /* _aantal_versies = 5, dus datums verwijderen ouder dan de 5e */ |
| 281 | + ; |
| 282 | + |
| 283 | + RETURN NEW; |
| 284 | +END; |
| 285 | +$BODY$ |
| 286 | +LANGUAGE plpgsql; |
| 287 | + |
| 288 | +ALTER FUNCTION wh.fnc_prune_pw_history() OWNER TO dwh_owner; |
| 289 | + |
| 290 | +CREATE TRIGGER trg_fnc_prune_pw_history |
| 291 | + AFTER INSERT |
| 292 | + ON wh.login_wachtwoord |
| 293 | + EXECUTE FUNCTION wh.fnc_prune_pw_history(); |
0 commit comments