Присвоение переменных выходным параметрам в триггере PostgreSQL - PullRequest
1 голос
/ 18 августа 2011

Я начинаю проект PostgreSQL, после многих лет работы с Sybase и SQL Server, и я новичок в этом, но до сих пор был впечатлен.

Я написал небольшую функцию, которая возвращает параметры OUT. Я передаю четкий пароль, и функция возвращает зашифрованный пароль и соль. Вот оно:

CREATE OR REPLACE FUNCTION iSecGenPwdSalt(pwdin text, out salt text, out userpassword text)
AS 
$BODY$
DECLARE 

    BEGIN
        -- Generate a salt...
        salt = gen_salt('bf', 10);

        -- Now generate the password hash...
        userpassword = crypt(pwdin, salt);
    END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION iSecGenPwdSalt(text, out text , out text ) OWNER TO postgres; 

Я хотел бы вызвать эту функцию в триггере и использовать значения для заполнения пароля и столбцов соли, которые у меня есть, но я не могу понять синтаксис. Я пробовал несколько вариантов темы, но у меня все еще есть проблемы.

select salt, userpassword from iSecGenPwdSalt('mymagicpassword');

Прекрасно возвращает результаты, поэтому я хотел бы вызвать эту функцию и получить результаты в виде пары переменных в триггере, чтобы я мог присвоить значения.

Вот мой код:

SELECT usersalt=salt, userpwd=userpassword 
  FROM iSecGenPwdSalt(NEW.systemuserpassword);

Я получаю такую ​​ошибку:

ОШИБКА: у запроса нет места назначения для данных результата СОВЕТ: Если вы хотите отменить результаты SELECT, используйте PERFORM. КОНТЕКСТ: PL / pgSQL функция "isec_trg_systemuser" строка 13 в операторе SQL

Поэтому простое назначение пары переменных для набора результатов, полученного из параметров OUT в триггере, вызывает у меня горе.

Ответы [ 2 ]

1 голос
/ 19 августа 2011

В вашей конкретной ситуации я думаю, что вы могли бы использовать оператор SELECT INTO с несколькими столбцами:

CREATE OR REPLACE FUNCTION userAuth_insert_f() RETURNS TRIGGER AS $$
BEGIN
    SELECT INTO NEW.usersalt, NEW.userpwd
        salt, userpassword FROM iSecGenPwdSalt(NEW.systemuserpassword);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Полный пример (используя pgcrypto модуль contrib):

DROP TABLE IF EXISTS userAuth; 
CREATE TABLE userAuth (
    id serial,
    systemuserpassword text,
    usersalt text,
    userpwd text
);

CREATE OR REPLACE FUNCTION iSecGenPwdSalt
    (pwdin text, OUT salt text, OUT userpassword text)
AS $$ BEGIN
    salt = gen_salt('bf', 10);
    userpassword = crypt(pwdin, salt);
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS userAuth_insert_t ON userAuth;
CREATE TRIGGER userAuth_insert_t
    BEFORE INSERT ON userAuth
    FOR EACH ROW
    EXECUTE PROCEDURE userAuth_insert_f();

Вызов:

=>INSERT INTO userAuth (systemuserpassword) VALUES ('mymagicpassword');
INSERT 0 1
=>SELECT usersalt, userpwd FROM userauth;
           usersalt            |                           userpwd                
-------------------------------+---------------------------------------------------
 $2a$10$7FqU5a/DrM.CWGlQPlGz6e | $2a$10$7FqU5a/DrM.CWGlQPlGz6eLjuN3UTwh/hlgxTnogS..
(1 row)
1 голос
/ 18 августа 2011

Это не совсем ответ на вопрос, который вы задаете, но недавно мне пришлось сделать нечто подобное, и я сделал это с помощью представления и правил вместо триггеров.Я надеюсь, что это полезно, несмотря на то, что я не являюсь ответом на вопрос, который вы задали напрямую.

Вот моя таблица пользователей и представление:

CREATE TABLE users_t (
  username VARCHAR(16) PRIMARY KEY,
  email    VARCHAR UNIQUE,
  password CHAR(60),
  salt     CHAR(29)
);

CREATE VIEW users AS SELECT username, email, '********'::varchar AS password FROM users_t;

Правило вставки заботится о генерации солизначение:

CREATE RULE users_insert_rule AS ON INSERT TO users DO INSTEAD
INSERT INTO users_t (username, email, password, salt) 
  SELECT new.username, new.email, crypt(new.password, salt.salt), salt.salt 
  FROM (SELECT gen_salt('bf') as salt) salt
RETURNING username, email, '********'::varchar;

Для полноты картины я включаю правила обновления и удаления, хотя они не особенно актуальны:

CREATE RULE users_update_rule AS ON UPDATE TO users DO INSTEAD
  UPDATE users_t 
  SET 
    username = new.username, 
    email = new.email, 
    password = crypt(new.password, salt)
RETURNING username, email, '********'::varchar;

CREATE RULE users_delete_rule AS ON DELETE TO users DO INSTEAD
  DELETE FROM users_t WHERE username = old.username
RETURNING username, email, '********'::varchar;

Затем я написал функцию аутентификации, которая позаботитсяиспользования соли:

CREATE OR REPLACE FUNCTION authenticate(varchar, varchar) RETURNS setof users AS $$
SELECT * FROM users 
WHERE username =
  (SELECT username FROM users_t 
   WHERE username = $1 AND crypt($2, salt) = password)
$$ LANGUAGE sql;

Таким образом, вы можете войти в систему, запросив SELECT * FROM authenticate('username', 'password');, и ваши пользователи будут созданы путем вставки в представление users.Вы можете установить разрешения таким образом, чтобы у вас была непривилегированная роль, которая может видеть представление пользователей, но не базовую таблицу users_t.Таким образом, вы можете легко взаимодействовать с таблицей пользователей с посоленными паролями, не имея дело с солью в приложении.

...