/** 
 * <feature scope="SanteDB.Persistence.Data.ADO" id="20200105-01" name="Update:20200105-01" applyRange="1.1.0.0-1.2.0.0"  invariantName="npgsql">
 *	<summary>Update: Add relationship for devices</summary>
 *	<remarks>This allows devices to be tracked to facilities and users</remarks>
 *	<isInstalled>select ck_patch('20200105-01')</isInstalled>
 * </feature>
 */

BEGIN TRANSACTION ;

-- ASSIGNED ENTITY
INSERT INTO ENT_REL_VRFY_CDTBL (src_cls_cd_id, rel_typ_cd_id, trg_cls_cd_id, err_desc) VALUES ('1373ff04-a6ef-420a-b1d0-4a07465fe8e8', '455f1772-f580-47e8-86bd-b5ce25d351f9', 'FF34DFA7-C6D3-4F8B-BC9F-14BCDC13BA6C', 'Device=[DedicatedServiceDeliveryLocation]=>ServiceDeliveryLocation'); 
INSERT INTO ENT_REL_VRFY_CDTBL (src_cls_cd_id, rel_typ_cd_id, trg_cls_cd_id, err_desc) VALUES ('1373ff04-a6ef-420a-b1d0-4a07465fe8e8', '77b7a04b-c065-4faf-8ec0-2cdad4ae372b', '9de2a846-ddf2-4ebc-902e-84508c5089ea', 'Device=[AssignedEntity]=>Person'); 
INSERT INTO ENT_REL_VRFY_CDTBL (rel_typ_cd_id, src_cls_cd_id, trg_cls_cd_id, err_desc) VALUES ('f3ef7e48-d8b7-4030-b431-aff7e0e1cb76','9de2a846-ddf2-4ebc-902e-84508c5089ea','79DD4F75-68E8-4722-A7F5-8BC2E08F5CD6', 'Person ==[Birthplace]==> CityOrTown');
INSERT INTO ENT_REL_VRFY_CDTBL (rel_typ_cd_id, src_cls_cd_id, trg_cls_cd_id, err_desc) VALUES ('f3ef7e48-d8b7-4030-b431-aff7e0e1cb76','9de2a846-ddf2-4ebc-902e-84508c5089ea','48B2FFB3-07DB-47BA-AD73-FC8FB8502471', 'Person ==[Birthplace]==> Country');
INSERT INTO ENT_REL_VRFY_CDTBL (rel_typ_cd_id, src_cls_cd_id, trg_cls_cd_id, err_desc) VALUES ('f3ef7e48-d8b7-4030-b431-aff7e0e1cb76','9de2a846-ddf2-4ebc-902e-84508c5089ea','D9489D56-DDAC-4596-B5C6-8F41D73D8DC5', 'Person ==[Birthplace]==> CountyOrParish');
INSERT INTO ENT_REL_VRFY_CDTBL (rel_typ_cd_id, src_cls_cd_id, trg_cls_cd_id, err_desc) VALUES ('f3ef7e48-d8b7-4030-b431-aff7e0e1cb76','9de2a846-ddf2-4ebc-902e-84508c5089ea','8CF4B0B0-84E5-4122-85FE-6AFA8240C218', 'Person ==[Birthplace]==> State');
INSERT INTO ENT_REL_VRFY_CDTBL (rel_typ_cd_id, src_cls_cd_id, trg_cls_cd_id, err_desc) VALUES ('f3ef7e48-d8b7-4030-b431-aff7e0e1cb76','bacd9c6f-3fa9-481e-9636-37457962804d','79DD4F75-68E8-4722-A7F5-8BC2E08F5CD6', 'Patient ==[Birthplace]==> CityOrTown');
INSERT INTO ENT_REL_VRFY_CDTBL (rel_typ_cd_id, src_cls_cd_id, trg_cls_cd_id, err_desc) VALUES ('f3ef7e48-d8b7-4030-b431-aff7e0e1cb76','bacd9c6f-3fa9-481e-9636-37457962804d','48B2FFB3-07DB-47BA-AD73-FC8FB8502471', 'Patient ==[Birthplace]==> Country');
INSERT INTO ENT_REL_VRFY_CDTBL (rel_typ_cd_id, src_cls_cd_id, trg_cls_cd_id, err_desc) VALUES ('f3ef7e48-d8b7-4030-b431-aff7e0e1cb76','bacd9c6f-3fa9-481e-9636-37457962804d','D9489D56-DDAC-4596-B5C6-8F41D73D8DC5', 'Patient ==[Birthplace]==> CountyOrParish');
INSERT INTO ENT_REL_VRFY_CDTBL (rel_typ_cd_id, src_cls_cd_id, trg_cls_cd_id, err_desc) VALUES ('f3ef7e48-d8b7-4030-b431-aff7e0e1cb76','bacd9c6f-3fa9-481e-9636-37457962804d','8CF4B0B0-84E5-4122-85FE-6AFA8240C218', 'Patient ==[Birthplace]==> State');


ALTER TABLE ASGN_AUT_TBL ADD POL_ID UUID;
ALTER TABLE ASGN_AUT_TBL ADD UPD_UTC TIMESTAMPTZ;
ALTER TABLE ASGN_AUT_TBL ADD UPD_PROV_ID UUID;
ALTER TABLE ASGN_AUT_TBL ADD CONSTRAINT CK_ASGN_AUT_UPD CHECK (UPD_UTC IS NULL OR UPD_UTC IS NOT NULL AND UPD_PROV_ID IS NOT NULL);
ALTER TABLE SEC_USR_CLM_TBL ADD EXP_UTC TIMESTAMPTZ;

CREATE TABLE SEC_CHL_TBL (
	CHL_ID UUID NOT NULL DEFAULT uuid_generate_v1(),
	CHL_TXT VARCHAR(128) NOT NULL,
	CRT_UTC TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, -- THE TIME THAT THE CHALLENGE WAS CREATED
    CRT_PROV_ID UUID, -- THE USER WHICH CREATED THIS CHALLENGE
    OBSLT_UTC TIMESTAMPTZ, -- THE TIME THAT THE CHALLENGE RECORD WAS OBSOLETED
    OBSLT_PROV_ID UUID, -- THE USER WHICH OBSOLETED THIS CHALLENGE 
    UPD_UTC TIMESTAMPTZ, -- THE TIME WHEN THIS CHALLENGE RECORD WAS LAST UPDATED
    UPD_PROV_ID UUID, -- THE USER ID WHICH WAS RESPONSIBLE FOR THE UPDATE
	CONSTRAINT PK_SEC_CHL_TBL PRIMARY KEY (CHL_ID),
    CONSTRAINT FK_SEC_CHL_OBSLT_PROV_ID FOREIGN KEY(OBSLT_PROV_ID) REFERENCES SEC_PROV_TBL(PROV_ID),
    CONSTRAINT FK_SEC_CHL_UPD_PROV_ID FOREIGN KEY (UPD_PROV_ID) REFERENCES SEC_PROV_TBL(PROV_ID),
    CONSTRAINT FK_SEC_CHL_CRT_PROV_ID FOREIGN KEY (CRT_PROV_ID) REFERENCES SEC_PROV_TBL(PROV_ID),
    CONSTRAINT CK_SEC_CHL_OBSLT_USR CHECK (OBSLT_PROV_ID IS NOT NULL AND OBSLT_UTC IS NOT NULL OR OBSLT_PROV_ID IS NULL AND OBSLT_UTC IS NULL),
    CONSTRAINT CK_SEC_CHL_UPD_USR CHECK (UPD_PROV_ID IS NOT NULL AND UPD_UTC IS NOT NULL OR UPD_PROV_ID IS NULL AND UPD_UTC IS NULL)
);
CREATE TABLE SEC_USR_CHL_ASSOC_TBL (
	CHL_ID UUID NOT NULL,
	USR_ID UUID NOT NULL,
	CHL_RSP VARCHAR(128) NOT NULL,
	EXP_UTC DATE NOT NULL,
	CONSTRAINT PK_SEC_USR_CHL_ASSOC_TBL PRIMARY KEY (CHL_ID, USR_ID),
	CONSTRAINT FK_SEC_USR_CHL_USR_TBL FOREIGN KEY (USR_ID) REFERENCES SEC_USR_TBL(USR_ID),
	CONSTRAINT FK_SEC_USR_CHL_CHL_TBL FOREIGN KEY (CHL_ID) REFERENCES SEC_CHL_TBL(CHL_ID)
);
ALTER TABLE SEC_USR_TBL ADD PWD_EXP_UTC DATE;
ALTER TABLE SEC_USR_TBL ADD TFA_MECH UUID;

INSERT INTO sec_chl_tbl (chl_txt, crt_prov_id) VALUES ('security.challenge.text1', 'fadca076-3690-4a6e-af9e-f1cd68e8c7e8');
INSERT INTO sec_chl_tbl (chl_txt, crt_prov_id) VALUES ('security.challenge.text2', 'fadca076-3690-4a6e-af9e-f1cd68e8c7e8');
INSERT INTO sec_chl_tbl (chl_txt, crt_prov_id) VALUES ('security.challenge.text3', 'fadca076-3690-4a6e-af9e-f1cd68e8c7e8');

DROP FUNCTION AUTH_USR(text, text, int);

-- AUTHENTICATES THE USER IF APPLICABLE
CREATE OR REPLACE FUNCTION AUTH_USR (
	USR_NAME_IN IN TEXT,
	PASSWD_IN IN TEXT,
	MAX_FAIL_LOGIN_IN IN INT
) RETURNS TABLE (
    USR_ID UUID,
    CLS_ID UUID,
    USR_NAME VARCHAR(64),
    EMAIL VARCHAR(256),
    EMAIL_CNF BOOLEAN,
    PHN_NUM VARCHAR(128), 
    PHN_CNF BOOLEAN,
    TFA_ENABLED BOOLEAN,
    LOCKED TIMESTAMPTZ, -- TRUE IF THE ACCOUNT HAS BEEN LOCKED
    PASSWD VARCHAR(128),
    SEC_STMP VARCHAR(128),
    FAIL_LOGIN INT,
    LAST_LOGIN_UTC TIMESTAMPTZ,
    CRT_UTC TIMESTAMPTZ,
    CRT_PROV_ID UUID, 
    OBSLT_UTC TIMESTAMPTZ,
    OBSLT_PROV_ID UUID, 
    UPD_UTC TIMESTAMPTZ,
    UPD_PROV_ID UUID, 
	PWD_EXP_UTC DATE, 
	TFA_MECH UUID,
    ERR_CODE VARCHAR(128)
) AS $$
DECLARE
	USR_TPL SEC_USR_TBL;
BEGIN
	SELECT INTO USR_TPL * 
		FROM SEC_USR_TBL
		WHERE LOWER(SEC_USR_TBL.USR_NAME) = LOWER(USR_NAME_IN)
		AND SEC_USR_TBL.OBSLT_UTC IS NULL;

	IF (IS_USR_LOCK(USR_NAME_IN)) THEN
		USR_TPL.LOCKED = COALESCE(USR_TPL.LOCKED, CURRENT_TIMESTAMP) + ((USR_TPL.FAIL_LOGIN - MAX_FAIL_LOGIN_IN) ^ 1.5 * '30 SECONDS'::INTERVAL);
		UPDATE SEC_USR_TBL SET FAIL_LOGIN = SEC_USR_TBL.FAIL_LOGIN + 1, LOCKED = USR_TPL.LOCKED
			WHERE SEC_USR_TBL.USR_NAME = USR_NAME_IN;
		RETURN QUERY SELECT USR_TPL.*, ('AUTH_LCK:' || ((USR_TPL.LOCKED - CURRENT_TIMESTAMP)::TEXT))::VARCHAR;
	ELSE
		
		-- LOCKOUT ACCOUNTS
		IF(USR_TPL.FAIL_LOGIN > MAX_FAIL_LOGIN_IN) THEN 
			USR_TPL.LOCKED = COALESCE(USR_TPL.LOCKED, CURRENT_TIMESTAMP) + ((USR_TPL.FAIL_LOGIN - MAX_FAIL_LOGIN_IN) ^ 1.5 * '30 SECONDS'::INTERVAL);
			UPDATE SEC_USR_TBL SET FAIL_LOGIN = COALESCE(SEC_USR_TBL.FAIL_LOGIN, 0) + 1, LOCKED = USR_TPL.LOCKED
				WHERE SEC_USR_TBL.USR_NAME = USR_NAME_IN;
			RETURN QUERY SELECT USR_TPL.*, ('AUTH_LCK:' || ((USR_TPL.LOCKED - CURRENT_TIMESTAMP)::TEXT))::VARCHAR;
		ELSIF (USR_TPL.PASSWD = PASSWD_IN) THEN
			UPDATE SEC_USR_TBL SET 
				FAIL_LOGIN = 0,
				LAST_LOGIN_UTC = CURRENT_TIMESTAMP,
				UPD_PROV_ID = 'fadca076-3690-4a6e-af9e-f1cd68e8c7e8',
				UPD_UTC = CURRENT_TIMESTAMP
			WHERE LOWER(SEC_USR_TBL.USR_NAME) = LOWER(USR_NAME_IN);
			RETURN QUERY SELECT USR_TPL.*, NULL::VARCHAR LIMIT 1;
		ELSE
			UPDATE SEC_USR_TBL SET FAIL_LOGIN = COALESCE(SEC_USR_TBL.FAIL_LOGIN, 0) + 1 WHERE SEC_USR_TBL.USR_NAME = USR_NAME_IN;
			RETURN QUERY SELECT USR_TPL.*, ('AUTH_INV:' || USR_NAME_IN)::VARCHAR;
		END IF;
	END IF;
END	
$$ LANGUAGE PLPGSQL;

INSERT INTO SEC_POL_TBL (POL_ID, OID, POL_NAME, CRT_PROV_ID) VALUES ('e15b96ab-646c-4c00-9a58-ea09eee67dad', '1.3.6.1.4.1.33349.3.1.5.9.2.1.0.1', 'Login for Password Reassignment', 'fadca076-3690-4a6e-af9e-f1cd68e8c7e8');
INSERT INTO SEC_POL_TBL (POL_ID, OID, POL_NAME, CRT_PROV_ID) VALUES ('e15b96ab-646c-4c00-9a58-ea09eee67dae', '1.3.6.1.4.1.33349.3.1.5.9.2.600', 'Special Security Elevation', 'fadca076-3690-4a6e-af9e-f1cd68e8c7e8');
INSERT INTO SEC_POL_TBL (POL_ID, OID, POL_NAME, CRT_PROV_ID, IS_ELEV) VALUES ('e15b96ab-646c-4c00-9a58-ea09eee67daf', '1.3.6.1.4.1.33349.3.1.5.9.2.600.1', 'Change Security Challenge Question', 'fadca076-3690-4a6e-af9e-f1cd68e8c7e8', true);

INSERT INTO SEC_ROL_POL_ASSOC_TBL (POL_ID, ROL_ID, POL_ACT)
	SELECT 'e15b96ab-646c-4c00-9a58-ea09eee67dae', ROL_ID, 1
	FROM SEC_ROL_TBL;
SELECT REG_PATCH('20200105-01');
COMMIT;