337 lines
8.8 KiB
PL/PgSQL
337 lines
8.8 KiB
PL/PgSQL
--
|
|
-- This file is part of TALER
|
|
-- Copyright (C) 2023, 2024 Taler Systems SA
|
|
--
|
|
-- TALER is free software; you can redistribute it and/or modify it under the
|
|
-- terms of the GNU General Public License as published by the Free Software
|
|
-- Foundation; either version 3, or (at your option) any later version.
|
|
--
|
|
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
--
|
|
-- You should have received a copy of the GNU General Public License along with
|
|
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
--
|
|
|
|
DROP FUNCTION IF EXISTS exchange_do_insert_aml_decision;
|
|
CREATE FUNCTION exchange_do_insert_aml_decision(
|
|
IN in_payto_uri TEXT, -- can be NULL!
|
|
IN in_h_normalized_payto BYTEA,
|
|
IN in_h_full_payto BYTEA, -- can be NULL!
|
|
IN in_decision_time INT8,
|
|
IN in_expiration_time INT8,
|
|
IN in_properties JSONB, -- can be NULL
|
|
IN in_kyc_attributes_enc BYTEA, -- can be NULL
|
|
IN in_kyc_attributes_hash BYTEA, -- can be NULL
|
|
IN in_kyc_attributes_expiration INT8, -- can be NULL
|
|
IN in_new_rules JSONB,
|
|
IN in_to_investigate BOOLEAN,
|
|
IN in_new_measure_name TEXT, -- can be NULL
|
|
IN in_jmeasures JSONB, -- can be NULL
|
|
IN in_justification TEXT, -- can be NULL
|
|
IN in_decider_pub BYTEA, -- can be NULL
|
|
IN in_decider_sig BYTEA, -- can be NULL
|
|
IN in_notify_s TEXT,
|
|
IN ina_events TEXT[],
|
|
IN in_form_name TEXT, -- can be NULL
|
|
OUT out_invalid_officer BOOLEAN,
|
|
OUT out_account_unknown BOOLEAN,
|
|
OUT out_last_date INT8,
|
|
OUT out_legitimization_measure_serial_id INT8,
|
|
OUT out_is_wallet BOOL) -- can be (left at) NULL
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
DECLARE
|
|
my_outcome_serial_id INT8;
|
|
my_legitimization_process_serial_id INT8;
|
|
my_kyc_attributes_serial_id INT8;
|
|
my_rec RECORD;
|
|
my_access_token BYTEA;
|
|
my_i INT4;
|
|
ini_event TEXT;
|
|
BEGIN
|
|
|
|
out_account_unknown=FALSE;
|
|
out_legitimization_measure_serial_id=0;
|
|
|
|
IF in_decider_pub IS NOT NULL
|
|
THEN
|
|
IF in_justification IS NULL OR in_decider_sig IS NULL
|
|
THEN
|
|
RAISE EXCEPTION 'Got in_decider_sig without justification or signature.';
|
|
END IF;
|
|
-- Check officer is eligible to make decisions.
|
|
PERFORM
|
|
FROM aml_staff
|
|
WHERE decider_pub=in_decider_pub
|
|
AND is_active
|
|
AND NOT read_only;
|
|
IF NOT FOUND
|
|
THEN
|
|
out_invalid_officer=TRUE;
|
|
out_last_date=0;
|
|
RETURN;
|
|
END IF;
|
|
END IF;
|
|
|
|
out_invalid_officer=FALSE;
|
|
|
|
-- Check no more recent decision exists.
|
|
SELECT decision_time
|
|
INTO out_last_date
|
|
FROM legitimization_outcomes
|
|
WHERE h_payto=in_h_normalized_payto
|
|
AND is_active
|
|
ORDER BY decision_time DESC, outcome_serial_id DESC;
|
|
|
|
IF FOUND
|
|
THEN
|
|
IF in_decider_pub IS NOT NULL AND out_last_date > in_decision_time
|
|
THEN
|
|
-- Refuse to insert older decision for officer decisions.
|
|
RETURN;
|
|
END IF;
|
|
UPDATE legitimization_outcomes
|
|
SET is_active=FALSE
|
|
WHERE h_payto=in_h_normalized_payto
|
|
AND is_active;
|
|
ELSE
|
|
out_last_date = 0;
|
|
END IF;
|
|
|
|
SELECT access_token
|
|
,is_wallet
|
|
INTO my_rec
|
|
FROM kyc_targets
|
|
WHERE h_normalized_payto=in_h_normalized_payto;
|
|
|
|
IF NOT FOUND
|
|
THEN
|
|
-- AML decision for previously unknown account; better includes
|
|
-- all required details about the account ...
|
|
IF in_payto_uri IS NULL
|
|
THEN
|
|
-- AML decision on an unknown account without payto_uri => fail.
|
|
out_account_unknown=TRUE;
|
|
RETURN;
|
|
END IF;
|
|
-- Well, fine, setup the account
|
|
out_is_wallet
|
|
= (LOWER (SUBSTRING (in_payto_uri, 22)) =
|
|
'payto://taler-reserve/') OR
|
|
(LOWER (SUBSTRING (in_payto_uri, 27)) =
|
|
'payto://taler-reserve-http/');
|
|
INSERT INTO kyc_targets
|
|
(h_normalized_payto
|
|
,is_wallet
|
|
) VALUES (
|
|
in_h_normalized_payto
|
|
,out_is_wallet
|
|
)
|
|
RETURNING access_token
|
|
INTO my_access_token;
|
|
INSERT INTO wire_targets
|
|
(wire_target_h_payto
|
|
,h_normalized_payto
|
|
,payto_uri
|
|
) VALUES (
|
|
in_h_full_payto
|
|
,in_h_normalized_payto
|
|
,in_payto_uri
|
|
)
|
|
ON CONFLICT DO NOTHING;
|
|
ELSE
|
|
my_access_token = my_rec.access_token;
|
|
out_is_wallet = my_rec.is_wallet;
|
|
END IF;
|
|
|
|
-- Did KYC measures get prescribed?
|
|
IF in_jmeasures IS NOT NULL
|
|
THEN
|
|
-- First check if a perfectly equivalent legi measure
|
|
-- already exists, to avoid creating tons of duplicates.
|
|
SELECT legitimization_measure_serial_id
|
|
INTO out_legitimization_measure_serial_id
|
|
FROM legitimization_measures
|
|
WHERE access_token=my_access_token
|
|
AND jmeasures=in_jmeasures
|
|
AND NOT is_finished;
|
|
|
|
IF NOT FOUND
|
|
THEN
|
|
-- Enable new legitimization measure
|
|
INSERT INTO legitimization_measures
|
|
(access_token
|
|
,start_time
|
|
,jmeasures
|
|
,display_priority
|
|
) VALUES (
|
|
my_access_token
|
|
,in_decision_time
|
|
,in_jmeasures
|
|
,1)
|
|
RETURNING
|
|
legitimization_measure_serial_id
|
|
INTO
|
|
out_legitimization_measure_serial_id;
|
|
END IF;
|
|
-- end if for where we had in_jmeasures
|
|
END IF;
|
|
|
|
RAISE NOTICE 'marking legi measures of % as finished except for %', my_access_token, out_legitimization_measure_serial_id;
|
|
|
|
-- AML decision: mark all other active measures finished!
|
|
UPDATE legitimization_measures
|
|
SET is_finished=TRUE
|
|
WHERE access_token=my_access_token
|
|
AND NOT is_finished
|
|
AND legitimization_measure_serial_id != out_legitimization_measure_serial_id;
|
|
|
|
UPDATE legitimization_outcomes
|
|
SET is_active=FALSE
|
|
WHERE h_payto=in_h_normalized_payto
|
|
-- this clause is a minor optimization to avoid
|
|
-- updating outcomes that have long expired.
|
|
AND expiration_time >= in_decision_time;
|
|
|
|
INSERT INTO legitimization_outcomes
|
|
(h_payto
|
|
,decision_time
|
|
,expiration_time
|
|
,jproperties
|
|
,new_measure_name
|
|
,to_investigate
|
|
,jnew_rules
|
|
) VALUES (
|
|
in_h_normalized_payto
|
|
,in_decision_time
|
|
,in_expiration_time
|
|
,in_properties
|
|
,in_new_measure_name
|
|
,in_to_investigate
|
|
,in_new_rules
|
|
)
|
|
RETURNING outcome_serial_id
|
|
INTO my_outcome_serial_id;
|
|
|
|
IF in_kyc_attributes_enc IS NOT NULL
|
|
THEN
|
|
IF in_kyc_attributes_hash IS NULL OR in_kyc_attributes_expiration IS NULL
|
|
THEN
|
|
RAISE EXCEPTION 'Got in_kyc_attributes_hash without hash or expiration.';
|
|
END IF;
|
|
IF in_decider_pub IS NULL
|
|
THEN
|
|
RAISE EXCEPTION 'Got in_kyc_attributes_hash without in_decider_pub.';
|
|
END IF;
|
|
-- Simulate a legi process for attribute insertion by AML Officer
|
|
INSERT INTO legitimization_processes
|
|
(h_payto
|
|
,start_time
|
|
,expiration_time
|
|
,provider_name
|
|
,provider_user_id
|
|
,finished
|
|
) VALUES (
|
|
in_h_normalized_payto
|
|
,in_decision_time
|
|
-- Process starts and finishes instantly
|
|
,in_decision_time
|
|
,'aml-officer'
|
|
,in_decider_pub
|
|
,TRUE
|
|
)
|
|
RETURNING legitimization_process_serial_id
|
|
INTO my_legitimization_process_serial_id;
|
|
-- Now we can insert the attribute!
|
|
INSERT INTO kyc_attributes
|
|
(h_payto
|
|
,collection_time
|
|
,expiration_time
|
|
,form_name
|
|
,by_aml_officer
|
|
,encrypted_attributes
|
|
,legitimization_serial
|
|
) VALUES (
|
|
in_h_normalized_payto
|
|
,in_decision_time
|
|
,in_kyc_attributes_expiration
|
|
,in_form_name
|
|
,TRUE
|
|
,in_kyc_attributes_enc
|
|
,my_legitimization_process_serial_id
|
|
)
|
|
RETURNING kyc_attributes_serial_id
|
|
INTO my_kyc_attributes_serial_id;
|
|
-- Wake up taler-exchange-sanctionscheck to check new attributes
|
|
-- This is value for TALER_DBEVENT_EXCHANGE_NEW_KYC_ATTRIBUTES.
|
|
NOTIFY XSX9Z5XGWWYFKXTAYCES63B62527JKNX9XD0131Z08THVV8YW5BZG;
|
|
END IF;
|
|
|
|
IF in_decider_pub IS NOT NULL
|
|
THEN
|
|
INSERT INTO aml_history
|
|
(h_payto
|
|
,outcome_serial_id
|
|
,justification
|
|
,decider_pub
|
|
,decider_sig
|
|
,kyc_attributes_hash
|
|
,kyc_attributes_serial_id
|
|
) VALUES (
|
|
in_h_normalized_payto
|
|
,my_outcome_serial_id
|
|
,in_justification
|
|
,in_decider_pub
|
|
,in_decider_sig
|
|
,in_kyc_attributes_hash
|
|
,my_kyc_attributes_serial_id
|
|
);
|
|
END IF;
|
|
|
|
-- Trigger events
|
|
FOR i IN 1..COALESCE(array_length(ina_events,1),0)
|
|
LOOP
|
|
ini_event = ina_events[i];
|
|
INSERT INTO kyc_events
|
|
(event_timestamp
|
|
,event_type
|
|
) VALUES (
|
|
in_decision_time
|
|
,ini_event);
|
|
IF (ini_event = 'ACCOUNT_OPEN')
|
|
THEN
|
|
UPDATE kyc_targets
|
|
SET open_time=in_decision_time
|
|
,close_time=NULL
|
|
WHERE h_normalized_payto=in_h_normalized_payto;
|
|
END IF;
|
|
IF (ini_event = 'ACCOUNT_IDLE')
|
|
THEN
|
|
UPDATE kyc_targets
|
|
SET close_time=in_decision_time
|
|
WHERE h_normalized_payto=in_h_normalized_payto;
|
|
END IF;
|
|
END LOOP;
|
|
|
|
-- wake up taler-exchange-aggregator
|
|
INSERT INTO kyc_alerts
|
|
(h_payto
|
|
,trigger_type
|
|
) VALUES (
|
|
in_h_normalized_payto
|
|
,1
|
|
)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
EXECUTE FORMAT (
|
|
'NOTIFY %s'
|
|
,in_notify_s);
|
|
|
|
|
|
END $$;
|
|
|
|
|
|
COMMENT ON FUNCTION exchange_do_insert_aml_decision(TEXT, BYTEA, BYTEA, INT8, INT8, JSONB, BYTEA, BYTEA, INT8, JSONB, BOOLEAN, TEXT, JSONB, TEXT, BYTEA, BYTEA, TEXT, TEXT[], TEXT)
|
|
IS 'Checks whether the AML officer is eligible to make AML decisions and if so inserts the decision into the table';
|