937 lines
28 KiB
MySQL
937 lines
28 KiB
MySQL
|
-- LegacyWorlds Beta 6
|
||
|
-- PostgreSQL database scripts
|
||
|
--
|
||
|
-- Functions and views to manage administrative accounts
|
||
|
--
|
||
|
-- Copyright(C) 2004-2010, DeepClone Development
|
||
|
-- --------------------------------------------------------
|
||
|
|
||
|
|
||
|
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- ADMINISTRATOR MANAGEMENT FUNCTIONS --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Creates an administrative user
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- u_addr User account address
|
||
|
-- a_name Administrative name
|
||
|
-- privs Administrative privileges
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- err_code Error code:
|
||
|
-- 0 success
|
||
|
-- 1 unknown user
|
||
|
-- 2 invalid user status
|
||
|
-- 3 name already in use
|
||
|
-- 4 user already has admin access
|
||
|
-- admin_id Administrator ID or NULL on failure
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.create_admin( IN u_addr TEXT , IN a_name TEXT , IN privs INT ,
|
||
|
OUT err_code INT , OUT admin_id INT )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
u_id INT;
|
||
|
u_stat TEXT;
|
||
|
p_sha1 TEXT;
|
||
|
p_md5 TEXT;
|
||
|
BEGIN
|
||
|
-- Get user ID, status and password hashes
|
||
|
SELECT INTO u_id , u_stat , p_sha1 , p_md5 id , status , pass_sha1 , pass_md5
|
||
|
FROM users.accounts_view
|
||
|
WHERE address = lower( u_addr );
|
||
|
IF NOT FOUND
|
||
|
THEN
|
||
|
err_code := 1;
|
||
|
RETURN;
|
||
|
ELSEIF u_stat IN ( 'UNCONFIRMED' , 'BANNED' )
|
||
|
THEN
|
||
|
err_code := 2;
|
||
|
RETURN;
|
||
|
END IF;
|
||
|
|
||
|
-- Try adding the admin's record
|
||
|
BEGIN
|
||
|
INSERT INTO admin.administrators ( appear_as , pass_md5 , pass_sha1 , privileges )
|
||
|
VALUES ( a_name , p_md5 , p_sha1 , privs )
|
||
|
RETURNING id INTO admin_id;
|
||
|
EXCEPTION
|
||
|
WHEN unique_violation THEN
|
||
|
err_code := 3;
|
||
|
RETURN;
|
||
|
END;
|
||
|
|
||
|
-- Add the admin <-> user relation
|
||
|
BEGIN
|
||
|
INSERT INTO admin.admin_credentials ( administrator_id , credentials_id )
|
||
|
VALUES ( admin_id , u_id );
|
||
|
err_code := 0;
|
||
|
PERFORM admin.write_log( admin_id , 'INFO'::log_level , 'Administrator ' || a_name || ' created' );
|
||
|
EXCEPTION
|
||
|
WHEN unique_violation THEN
|
||
|
DELETE FROM admin.administrators WHERE id = admin_id;
|
||
|
err_code := 4;
|
||
|
admin_id := NULL;
|
||
|
END;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.create_admin( TEXT, TEXT , INT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Type indicating the result of a connection attempt
|
||
|
--
|
||
|
|
||
|
CREATE TYPE admin_connection_result
|
||
|
AS ENUM( 'SUCCESS' , 'PASSWORD' , 'INACTIVE' );
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Log a connection attempt
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- c_res Connection attempt result
|
||
|
-- addr IP address
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.log_connection( a_id INT , c_res admin_connection_result , addr TEXT )
|
||
|
RETURNS VOID
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
BEGIN
|
||
|
IF c_res = 'SUCCESS'
|
||
|
THEN
|
||
|
PERFORM admin.write_log( a_id , 'DEBUG'::log_level , 'Successful login attempt from ' || addr );
|
||
|
ELSEIF c_res = 'PASSWORD'
|
||
|
THEN
|
||
|
PERFORM admin.write_log( a_id , 'WARNING'::log_level , 'Failed login attempt from ' || addr
|
||
|
|| ' (incorrect password)' );
|
||
|
ELSEIF c_res = 'INACTIVE'
|
||
|
THEN
|
||
|
PERFORM admin.write_log( a_id , 'WARNING'::log_level , 'Failed login attempt from ' || addr
|
||
|
|| ' (inactive administrator)' );
|
||
|
ELSE
|
||
|
RAISE EXCEPTION 'Unknown connection attempt result value: %' , c_res;
|
||
|
END IF;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.log_connection( INT , admin_connection_result , TEXT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Log a disconnection
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.log_disconnection( a_id INT )
|
||
|
RETURNS VOID
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
BEGIN
|
||
|
PERFORM admin.write_log( a_id , 'DEBUG'::log_level , 'Administrator disconnected' );
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.log_disconnection( INT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Modifies an administrator's password
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- p_sha1 SHA-1 hash of the new password
|
||
|
-- p_md5 MD5 hash of the new password
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- success Whether the operation was successful
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.set_password( a_id INT , p_sha1 TEXT , p_md5 TEXT , OUT success BOOLEAN )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
u_sha1 TEXT;
|
||
|
u_md5 TEXT;
|
||
|
BEGIN
|
||
|
SELECT INTO u_sha1 , u_md5 c.pass_sha1 , c.pass_md5
|
||
|
FROM admin.admins_view a
|
||
|
INNER JOIN users.credentials c ON c.address_id = a.account_id
|
||
|
WHERE a.administrator_id = a_id AND a.active;
|
||
|
success := ( FOUND AND u_sha1 <> p_sha1 AND u_md5 <> p_md5 );
|
||
|
|
||
|
IF success
|
||
|
THEN
|
||
|
UPDATE admin.administrators
|
||
|
SET pass_sha1 = p_sha1 , pass_md5 = p_md5
|
||
|
WHERE id = a_id;
|
||
|
END IF;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.set_password( INT , TEXT , TEXT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Resets an administrator's password to his/her player account password
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- su_id Superuser identifier
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- success Whether the operation was successful
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.reset_password( a_id INT , su_id INT , OUT success BOOLEAN )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
u_sha1 TEXT;
|
||
|
u_md5 TEXT;
|
||
|
a_name TEXT;
|
||
|
BEGIN
|
||
|
SELECT INTO u_sha1 , u_md5 , a_name c.pass_sha1 , c.pass_md5 , a.name
|
||
|
FROM admin.admins_view a
|
||
|
INNER JOIN users.credentials c ON c.address_id = a.account_id
|
||
|
WHERE a.administrator_id = a_id AND a.active;
|
||
|
success := FOUND;
|
||
|
|
||
|
IF success
|
||
|
THEN
|
||
|
UPDATE admin.administrators SET pass_sha1 = u_sha1 , pass_md5 = u_md5
|
||
|
WHERE id = a_id;
|
||
|
PERFORM admin.write_log( su_id , 'INFO'::log_level , 'Reset password of administrator ' || a_name );
|
||
|
END IF;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.reset_password( INT , INT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Modifies an administrator's privileges
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- su_id Superuser identifier
|
||
|
-- n_privs New privileges
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- success Whether the operation was successful
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.set_privileges( a_id INT , su_id INT , n_privs INT , OUT success BOOLEAN )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
a_name TEXT;
|
||
|
o_privs INT;
|
||
|
BEGIN
|
||
|
SELECT INTO a_name , o_privs a.name , a.privileges
|
||
|
FROM admin.admins_view a
|
||
|
WHERE a.administrator_id = a_id AND a.address IS NOT NULL;
|
||
|
success := FOUND;
|
||
|
|
||
|
IF success AND n_privs <> o_privs
|
||
|
THEN
|
||
|
UPDATE admin.administrators SET privileges = n_privs
|
||
|
WHERE id = a_id;
|
||
|
PERFORM admin.write_log( su_id , 'INFO'::log_level , 'Set privileges of administrator ' || a_name
|
||
|
|| ' from ' || o_privs || ' to ' || n_privs );
|
||
|
END IF;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.set_privileges( INT , INT , INT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- ADMINISTRATOR VIEWS --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Active administrators
|
||
|
--
|
||
|
|
||
|
CREATE VIEW admin.admins_view
|
||
|
AS SELECT adm.id AS administrator_id , adm.appear_as AS name , adm.privileges AS privileges ,
|
||
|
adm.pass_sha1 , adm.pass_md5 , addr.id AS account_id , addr.address AS address ,
|
||
|
( addr.id IS NOT NULL AND privileges <> 0 ) AS active ,
|
||
|
( adm.pass_sha1 = cred.pass_sha1 AND adm.pass_md5 = cred.pass_md5 ) AS pass_change_required
|
||
|
FROM admin.administrators adm
|
||
|
LEFT OUTER JOIN admin.admin_credentials ac ON ac.administrator_id = adm.id
|
||
|
LEFT OUTER JOIN users.credentials cred ON cred.address_id = ac.credentials_id
|
||
|
LEFT OUTER JOIN users.addresses addr ON addr.id = ac.credentials_id;
|
||
|
|
||
|
GRANT SELECT ON admin.admins_view TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- BANHAMMER FUNCTIONS --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Creates a ban request
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- u_id User identifier
|
||
|
-- r_txt Reason for the ban request
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- Whether the operation was successful or not
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.add_ban_request( a_id INT , u_id INT , r_txt TEXT )
|
||
|
RETURNS BOOLEAN
|
||
|
STRICT VOLATILE
|
||
|
SECURITY INVOKER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
ban_id INT;
|
||
|
BEGIN
|
||
|
INSERT INTO admin.ban_requests ( requested_by , reason )
|
||
|
VALUES ( a_id , r_txt )
|
||
|
RETURNING id INTO ban_id;
|
||
|
|
||
|
BEGIN
|
||
|
INSERT INTO admin.active_ban_requests( request_id , credentials_id )
|
||
|
VALUES( ban_id , u_id );
|
||
|
RETURN TRUE;
|
||
|
EXCEPTION
|
||
|
WHEN unique_violation THEN
|
||
|
DELETE FROM admin.ban_requests WHERE id = ban_id;
|
||
|
RETURN FALSE;
|
||
|
END;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Creates a ban request using an email address as the source
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- u_addr Target user's address
|
||
|
-- r_txt Reason for the ban request
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- err_code Error code:
|
||
|
-- 0 success
|
||
|
-- 1 user not found
|
||
|
-- 2 duplicate request
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.request_ban_on_address( a_id INT , u_addr TEXT , r_txt TEXT , OUT err_code INT )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
u_id INT;
|
||
|
BEGIN
|
||
|
SELECT INTO u_id id FROM users.accounts_view
|
||
|
WHERE address = u_addr;
|
||
|
IF NOT FOUND
|
||
|
THEN
|
||
|
err_code := 1;
|
||
|
RETURN;
|
||
|
END IF;
|
||
|
|
||
|
IF admin.add_ban_request( a_id , u_id , r_txt )
|
||
|
THEN
|
||
|
PERFORM admin.write_log( a_id , 'INFO'::log_level , 'Requested ban of user #' || u_id || ' (address: ' || u_addr || ')' );
|
||
|
err_code := 0;
|
||
|
ELSE
|
||
|
err_code := 2;
|
||
|
END IF;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.request_ban_on_address( INT , TEXT, TEXT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Creates a ban request using an empire name as the source
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- u_emp Target user's empire name
|
||
|
-- r_txt Reason for the ban request
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- err_code Error code:
|
||
|
-- 0 success
|
||
|
-- 1 user not found
|
||
|
-- 2 duplicate request
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.request_ban_on_empire( a_id INT , u_emp TEXT , r_txt TEXT , OUT err_code INT )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
u_id INT;
|
||
|
BEGIN
|
||
|
SELECT INTO u_id owner_id FROM naming.empire_names
|
||
|
WHERE lower(name) = lower(u_emp);
|
||
|
IF NOT FOUND
|
||
|
THEN
|
||
|
err_code := 1;
|
||
|
RETURN;
|
||
|
END IF;
|
||
|
|
||
|
IF admin.add_ban_request( a_id , u_id , r_txt )
|
||
|
THEN
|
||
|
PERFORM admin.write_log( a_id , 'INFO'::log_level , 'Requested ban of user #' || u_id || ' (empire name: ' || u_emp || ')' );
|
||
|
err_code := 0;
|
||
|
ELSE
|
||
|
err_code := 2;
|
||
|
END IF;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.request_ban_on_empire( INT , TEXT, TEXT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Rejects a ban request
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- b_id Ban request identifier
|
||
|
-- r_txt Rejection reason
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.reject_ban_request( a_id INT , b_id INT , r_txt TEXT )
|
||
|
RETURNS VOID
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
u_id INT;
|
||
|
BEGIN
|
||
|
-- Make sure the request exists and hasn't been validated
|
||
|
SELECT INTO u_id credentials_id FROM admin.active_ban_requests
|
||
|
WHERE request_id = b_id AND NOT validated
|
||
|
FOR UPDATE;
|
||
|
IF NOT FOUND
|
||
|
THEN
|
||
|
RETURN;
|
||
|
END IF;
|
||
|
PERFORM * FROM admin.ban_requests WHERE id = b_id FOR UPDATE;
|
||
|
|
||
|
-- Insert archive entry
|
||
|
INSERT INTO admin.archived_ban_requests( request_id , credentials_id )
|
||
|
VALUES ( b_id , u_id );
|
||
|
INSERT INTO admin.rejected_ban_requests( request_id , rejected_by , reason )
|
||
|
VALUES ( b_id , a_id , r_txt );
|
||
|
DELETE FROM admin.active_ban_requests WHERE request_id = b_id;
|
||
|
PERFORM admin.write_log( a_id , 'INFO'::log_level , 'Rejected ban of user #' || u_id );
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.reject_ban_request( INT , INT , TEXT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Confirms a ban request
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- b_id Ban request identifier
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- success Whether the operation was successful
|
||
|
-- addr User's email address
|
||
|
-- lang User's language
|
||
|
-- r_txt Reason for the ban
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.confirm_ban_request( a_id INT , b_id INT , OUT success BOOLEAN , OUT addr TEXT , OUT lang TEXT , OUT r_txt TEXT )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
u_id INT;
|
||
|
br_id INT;
|
||
|
BEGIN
|
||
|
-- Make sure there is an active, unvalidated ban request
|
||
|
SELECT INTO u_id credentials_id FROM admin.active_ban_requests
|
||
|
WHERE request_id = b_id AND NOT validated FOR UPDATE;
|
||
|
IF NOT FOUND
|
||
|
THEN
|
||
|
success := FALSE;
|
||
|
RETURN;
|
||
|
END IF;
|
||
|
|
||
|
-- Make sure the request was created by another admin
|
||
|
SELECT INTO br_id , r_txt requested_by , reason FROM admin.ban_requests
|
||
|
WHERE id = b_id AND requested_by <> a_id
|
||
|
FOR UPDATE;
|
||
|
IF NOT FOUND
|
||
|
THEN
|
||
|
success := FALSE;
|
||
|
RETURN;
|
||
|
END IF;
|
||
|
|
||
|
-- Mark request as validated
|
||
|
INSERT INTO admin.validated_ban_requests ( request_id , validated_by )
|
||
|
VALUES ( b_id , a_id );
|
||
|
UPDATE admin.active_ban_requests SET validated = TRUE
|
||
|
WHERE request_id = b_id;
|
||
|
|
||
|
-- Update account
|
||
|
PERFORM * FROM users.credentials WHERE address_id = u_id FOR UPDATE;
|
||
|
LOOP
|
||
|
UPDATE users.inactive_accounts SET since = now( ) , status = 'PROCESSED'
|
||
|
WHERE credentials_id = u_id;
|
||
|
EXIT WHEN FOUND;
|
||
|
|
||
|
BEGIN
|
||
|
INSERT INTO users.inactive_accounts ( credentials_id , since , status )
|
||
|
VALUES ( u_id , now() , 'PROCESSED' );
|
||
|
EXIT;
|
||
|
EXCEPTION
|
||
|
WHEN unique_violation THEN
|
||
|
-- Do nothing
|
||
|
END;
|
||
|
END LOOP;
|
||
|
|
||
|
-- Insert ban entry
|
||
|
INSERT INTO users.bans ( account_id , ban_id )
|
||
|
VALUES( u_id , b_id );
|
||
|
|
||
|
-- Set ban reason
|
||
|
LOOP
|
||
|
UPDATE users.reasons SET reason = r_txt WHERE account_id = u_id;
|
||
|
EXIT WHEN FOUND;
|
||
|
|
||
|
BEGIN
|
||
|
INSERT INTO users.reasons( account_id , reason )
|
||
|
VALUES ( u_id , r_txt );
|
||
|
EXIT;
|
||
|
EXCEPTION
|
||
|
WHEN unique_violation THEN
|
||
|
-- Do nothing
|
||
|
END;
|
||
|
END LOOP;
|
||
|
|
||
|
success := TRUE;
|
||
|
SELECT INTO addr , lang a.address , l.language
|
||
|
FROM users.credentials c
|
||
|
INNER JOIN users.addresses a ON a.id = c.address_id
|
||
|
INNER JOIN defs.languages l ON l.id = c.language_id
|
||
|
WHERE c.address_id = u_id;
|
||
|
|
||
|
PERFORM admin.write_log( a_id , 'INFO'::log_level , 'Confirmed ban request on user #' || u_id );
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.confirm_ban_request( INT , INT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Lifts an existing ban
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- b_id Ban identifier
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- success Whether the operation was successful
|
||
|
-- addr User's email address
|
||
|
-- lang User's language
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.lift_ban( a_id INT , b_id INT , OUT success BOOLEAN , OUT addr TEXT , OUT lang TEXT )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
u_id INT;
|
||
|
redeem BOOLEAN;
|
||
|
BEGIN
|
||
|
-- Find / lock the ban and user records
|
||
|
SELECT INTO u_id , redeem ab.credentials_id , ( aa.credentials_id IS NOT NULL )
|
||
|
FROM admin.active_ban_requests ab
|
||
|
INNER JOIN admin.ban_requests br ON br.id = ab.request_id
|
||
|
INNER JOIN users.credentials c ON c.address_id = ab.credentials_id
|
||
|
LEFT OUTER JOIN users.active_accounts aa ON aa.credentials_id = c.address_id
|
||
|
WHERE ab.request_id = b_id AND ab.validated
|
||
|
FOR UPDATE OF ab , br , c;
|
||
|
IF NOT FOUND
|
||
|
THEN
|
||
|
success := FALSE;
|
||
|
RETURN;
|
||
|
END IF;
|
||
|
|
||
|
-- Delete the ban and reason records, and redeem the account if possible
|
||
|
DELETE FROM users.bans WHERE account_id = u_id;
|
||
|
DELETE FROM users.reasons WHERE account_id = u_id;
|
||
|
IF redeem
|
||
|
THEN
|
||
|
DELETE FROM users.inactive_accounts WHERE credentials_id = u_id;
|
||
|
END IF;
|
||
|
|
||
|
-- Delete active and validated ban entries
|
||
|
DELETE FROM admin.validated_ban_requests WHERE request_id = b_id;
|
||
|
DELETE FROM admin.active_ban_requests WHERE request_id = b_id;
|
||
|
|
||
|
-- Insert archive records
|
||
|
INSERT INTO admin.archived_ban_requests( request_id , credentials_id )
|
||
|
VALUES ( b_id , u_id );
|
||
|
INSERT INTO admin.rejected_ban_requests( request_id , rejected_by , reason )
|
||
|
VALUES ( b_id , a_id , '(ban lifted)' );
|
||
|
|
||
|
success := TRUE;
|
||
|
SELECT INTO addr , lang a.address , l.language
|
||
|
FROM users.credentials c
|
||
|
INNER JOIN users.addresses a ON a.id = c.address_id
|
||
|
INNER JOIN defs.languages l ON l.id = c.language_id
|
||
|
WHERE c.address_id = u_id;
|
||
|
|
||
|
PERFORM admin.write_log( a_id , 'INFO'::log_level , 'Ban on user #' || u_id || ' has been lifted'
|
||
|
|| (CASE WHEN redeem THEN ' (empire redeemed)' ELSE '' END) );
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.lift_ban( INT , INT ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Causes ban requests to expire
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.expire_ban_requests( )
|
||
|
RETURNS VOID
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
b_id INT;
|
||
|
u_id INT;
|
||
|
BEGIN
|
||
|
FOR b_id , u_id IN SELECT br.id , ab.credentials_id FROM admin.ban_requests br
|
||
|
INNER JOIN admin.active_ban_requests ab
|
||
|
ON ab.request_id = br.id AND NOT validated
|
||
|
WHERE now() - br.requested >= ( floor( sys.get_constant( 'accounts.banExpiration') ) || 's' )::INTERVAL
|
||
|
FOR UPDATE
|
||
|
LOOP
|
||
|
INSERT INTO admin.archived_ban_requests ( request_id , credentials_id )
|
||
|
VALUES ( b_id , u_id );
|
||
|
DELETE FROM admin.active_ban_requests WHERE request_id = b_id;
|
||
|
PERFORM sys.write_log( 'Bans' , 'INFO'::log_level , 'Ban request #' || b_id
|
||
|
|| ' (user account #' || u_id || ') expired' );
|
||
|
END LOOP;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.expire_ban_requests( ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Checks for banned players who still have empires past the expiration delay
|
||
|
-- and deletes the empires.
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- Whether an empire was deleted
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.delete_banned_empires( )
|
||
|
RETURNS BOOLEAN
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
e_id INT;
|
||
|
a_id INT;
|
||
|
BEGIN
|
||
|
SELECT INTO e_id , a_id e.name_id , c.address_id
|
||
|
FROM emp.empires e
|
||
|
INNER JOIN naming.empire_names en ON en.id = e.name_id
|
||
|
INNER JOIN users.credentials c ON c.address_id = en.owner_id
|
||
|
INNER JOIN users.active_accounts aa ON aa.credentials_id = c.address_id
|
||
|
INNER JOIN users.inactive_accounts ia ON ia.credentials_id = c.address_id
|
||
|
INNER JOIN users.bans b ON b.account_id = ia.credentials_id
|
||
|
WHERE now() - ia.since >= ( floor( sys.get_constant( 'accounts.banDelay') ) || 's' )::INTERVAL
|
||
|
FOR UPDATE LIMIT 1;
|
||
|
IF NOT FOUND
|
||
|
THEN
|
||
|
RETURN FALSE;
|
||
|
END IF;
|
||
|
|
||
|
-- Delete empire and active account record
|
||
|
PERFORM emp.delete_empire( e_id );
|
||
|
DELETE FROM users.active_accounts WHERE credentials_id = a_id;
|
||
|
PERFORM sys.write_log( 'Bans' , 'INFO'::log_level , 'Deleted empire #' || e_id
|
||
|
|| ' (user account #' || a_id || ')' );
|
||
|
|
||
|
RETURN TRUE;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.delete_banned_empires( ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- VIEWS RELATED TO THE BANHAMMER --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Pending requests
|
||
|
--
|
||
|
|
||
|
CREATE VIEW admin.pending_bans
|
||
|
AS SELECT r.id , r.requested_by AS requested_by_id , a.appear_as AS requested_by_name ,
|
||
|
r.reason , r.requested , ab.credentials_id AS account_id , ad.address AS account_mail
|
||
|
FROM admin.ban_requests r
|
||
|
INNER JOIN admin.active_ban_requests ab
|
||
|
ON ab.request_id = r.id AND NOT ab.validated
|
||
|
INNER JOIN admin.administrators a ON a.id = r.requested_by
|
||
|
INNER JOIN users.addresses ad ON ad.id = ab.credentials_id;
|
||
|
|
||
|
GRANT SELECT ON admin.pending_bans TO :dbuser;
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Expired / rejected ban requests
|
||
|
--
|
||
|
|
||
|
CREATE VIEW admin.cancelled_bans
|
||
|
AS SELECT r.id , r.requested_by AS requested_by_id , a.appear_as AS requested_by_name ,
|
||
|
r.reason , r.requested , ab.credentials_id AS account_id , ad.address AS account_mail ,
|
||
|
ab.updated , ( rb.request_id IS NULL ) AS expired ,
|
||
|
rb.rejected_by AS rejected_by_id , ra.appear_as AS rejected_by_name ,
|
||
|
rb.reason AS rejection_reason
|
||
|
FROM admin.ban_requests r
|
||
|
INNER JOIN admin.administrators a ON a.id = r.requested_by
|
||
|
INNER JOIN admin.archived_ban_requests ab ON ab.request_id = r.id
|
||
|
INNER JOIN users.addresses ad ON ad.id = ab.credentials_id
|
||
|
LEFT OUTER JOIN admin.rejected_ban_requests rb ON rb.request_id = r.id
|
||
|
LEFT OUTER JOIN admin.administrators ra ON ra.id = rb.rejected_by;
|
||
|
|
||
|
GRANT SELECT ON admin.cancelled_bans TO :dbuser;
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Active bans
|
||
|
--
|
||
|
|
||
|
CREATE VIEW admin.active_bans
|
||
|
AS SELECT r.id , r.requested_by AS requested_by_id , a.appear_as AS requested_by_name ,
|
||
|
r.reason , r.requested , ab.credentials_id AS account_id , ad.address AS account_mail ,
|
||
|
vr.validated AS updated , ( ua.credentials_id IS NOT NULL ) AS redeemable ,
|
||
|
vr.validated_by AS validated_by_id , va.appear_as AS validated_by_name
|
||
|
FROM admin.ban_requests r
|
||
|
INNER JOIN admin.administrators a ON a.id = r.requested_by
|
||
|
INNER JOIN admin.active_ban_requests ab ON ab.request_id = r.id AND validated
|
||
|
INNER JOIN users.addresses ad ON ad.id = ab.credentials_id
|
||
|
INNER JOIN admin.validated_ban_requests vr ON vr.request_id = r.id
|
||
|
INNER JOIN admin.administrators va ON va.id = vr.validated_by
|
||
|
LEFT OUTER JOIN users.active_accounts ua ON ua.credentials_id = ab.credentials_id;
|
||
|
|
||
|
GRANT SELECT ON admin.active_bans TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- WARNING SYSTEM FUNCTIONS --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Adds a warning to a player's account, triggering an automatic ban request if necessary
|
||
|
--
|
||
|
-- Parameters:
|
||
|
-- a_id Administrator identifier
|
||
|
-- u_id Player identifier
|
||
|
--
|
||
|
-- Returns:
|
||
|
-- given Whether a warning was issued or not
|
||
|
-- c_count Current amount of warnings for this player
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.give_player_warning( a_id INT , u_id INT , OUT given BOOLEAN , OUT c_count INT )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY INVOKER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
last_rec TIMESTAMP WITHOUT TIME ZONE;
|
||
|
BEGIN
|
||
|
LOOP
|
||
|
SELECT INTO c_count , last_rec warnings, last_received
|
||
|
FROM admin.warnings WHERE credentials_id = u_id
|
||
|
FOR UPDATE;
|
||
|
IF FOUND
|
||
|
THEN
|
||
|
given := ( now() - last_rec >= ( floor(sys.get_constant('accounts.warnings.grace')) || 's' )::INTERVAL );
|
||
|
IF given
|
||
|
THEN
|
||
|
c_count := c_count + 1;
|
||
|
UPDATE admin.warnings SET last_received = now( ) , warnings = c_count
|
||
|
WHERE credentials_id = u_id;
|
||
|
END IF;
|
||
|
EXIT;
|
||
|
END IF;
|
||
|
|
||
|
BEGIN
|
||
|
INSERT INTO admin.warnings ( credentials_id ) VALUES ( u_id );
|
||
|
given := TRUE;
|
||
|
c_count := 1;
|
||
|
EXIT;
|
||
|
EXCEPTION
|
||
|
WHEN unique_violation THEN
|
||
|
-- Do nothing
|
||
|
END;
|
||
|
END LOOP;
|
||
|
|
||
|
IF given
|
||
|
THEN
|
||
|
PERFORM admin.write_log( a_id , 'INFO'::log_level , 'Warning given to user #' || u_id || ' ('
|
||
|
|| c_count || ' warning(s) total)' );
|
||
|
|
||
|
IF c_count >= sys.get_constant( 'accounts.warnings.autoBan' )
|
||
|
THEN
|
||
|
IF admin.add_ban_request( a_id , u_id , 'Automatic ban after ' || c_count || ' warnings' )
|
||
|
THEN
|
||
|
PERFORM admin.write_log( a_id , 'INFO'::log_level , 'Added automatic ban for user ' || u_id );
|
||
|
END IF;
|
||
|
END IF;
|
||
|
END IF;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Causes old warnings to expire
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.expire_warnings( )
|
||
|
RETURNS VOID
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
DECLARE
|
||
|
e_time INTERVAL;
|
||
|
g_time INTERVAL;
|
||
|
BEGIN
|
||
|
e_time := ( floor( sys.get_constant( 'accounts.warnings.expiration' )
|
||
|
* sys.get_constant( 'accounts.warnings.expiration.units' ) )
|
||
|
|| 's' ) :: INTERVAL;
|
||
|
g_time := ( floor(sys.get_constant('accounts.warnings.grace')) || 's' )::INTERVAL;
|
||
|
UPDATE admin.warnings SET last_received = now( ) - g_time , warnings = warnings - 1
|
||
|
WHERE now() - last_received >= e_time AND warnings > 0;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.expire_warnings( ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
-- Finds out if it is time to send the administrative recap e-mails
|
||
|
--
|
||
|
|
||
|
CREATE OR REPLACE FUNCTION admin.is_recap_time( OUT is_time TIMESTAMP WITHOUT TIME ZONE )
|
||
|
STRICT VOLATILE
|
||
|
SECURITY DEFINER
|
||
|
AS $$
|
||
|
BEGIN
|
||
|
SELECT INTO is_time last_admin_recap FROM sys.status
|
||
|
WHERE now() - last_admin_recap >= '12 hours'::INTERVAL
|
||
|
FOR UPDATE;
|
||
|
IF FOUND
|
||
|
THEN
|
||
|
UPDATE sys.status SET last_admin_recap = now( );
|
||
|
END IF;
|
||
|
END;
|
||
|
$$ LANGUAGE plpgsql;
|
||
|
|
||
|
GRANT EXECUTE ON FUNCTION admin.is_recap_time( ) TO :dbuser;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- USERS VIEW --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --
|
||
|
|
||
|
|
||
|
CREATE VIEW admin.user_empires
|
||
|
AS SELECT en.*
|
||
|
FROM emp.empires e
|
||
|
INNER JOIN naming.empire_names en ON e.name_id = en.id;
|
||
|
|
||
|
CREATE VIEW admin.users_list
|
||
|
AS SELECT av.* , ue.id AS current_empire_id , ue.name AS current_empire ,
|
||
|
a1.id AS ban_req_id , a1.appear_as AS ban_req_name ,
|
||
|
a2.id AS ban_val_id , a2.appear_as AS ban_val_name ,
|
||
|
( CASE WHEN w IS NULL THEN 0 ELSE w.warnings END ) AS warnings_count ,
|
||
|
w.last_received AS warnings_last
|
||
|
FROM users.accounts_view av
|
||
|
LEFT OUTER JOIN admin.user_empires ue ON ue.owner_id = av.id
|
||
|
LEFT OUTER JOIN admin.ban_requests br ON br.id = av.ban_request_id
|
||
|
LEFT OUTER JOIN admin.validated_ban_requests vbr ON vbr.request_id = br.id
|
||
|
LEFT OUTER JOIN admin.administrators a1 ON a1.id = br.requested_by
|
||
|
LEFT OUTER JOIN admin.administrators a2 ON a2.id = vbr.validated_by
|
||
|
LEFT OUTER JOIN admin.warnings w ON w.credentials_id = av.id
|
||
|
ORDER BY av.address;
|
||
|
|
||
|
GRANT SELECT ON admin.users_list TO :dbuser;
|