Lần trước mình đã cùng tìm hiểu về cách tạo user, database và phân quyền cho MySQL/MariaDB rồi. Hôm nay vẫn là chủ đề cũ nhưng với một DB khác cũng cực kì phổ biến là PostgreSQL nhé.
Phần trước ở đây nếu bạn cần nhé.
Tạo user, DB và phân quyền cho MySQL/MariaDB, tất cả những gì bạn cần biết
Tạo user
User trong Postgres được gọi là ROLE
. Có 2 loại role là login role và non-login role.
Nghe tên thì cũng đoán được một cái thì login được còn cái kia thì không rồi. Khi bạn tạo USER
trong postgres thì tức
là bạn đang tạo một login role đó.
Để tạo một user (hay login role) mới thì bạn dùng CREATE ROLE
. Ví dụ để tạo user myuser thì bạn làm thế này.
CREATE ROLE myuser WITH LOGIN;
Mặc định thì một role sẽ là no-login role nên bạn cần thêm LOGIN
để tạo một login role.
Bạn cũng có thể dùng CREATE USER
thay vì ROLE
như này.
CREATE USER myuser;
Để set password thì bạn thêm option PASSWORD
như này.
CREATE USER myuser WITH PASSWORD 'mypassword';
-- Hoặc là như này
CREATE ROLE myuser WITH LOGIN PASSWORD 'mypassword';
Ngoài LOGIN
và PASSWORD
thì còn nhiều option khác nữa, bạn có thể xem tất cả ở đây.
Bạn có thể thấy là không có option IF NOT EXISTS
nào cả. Nên để viết script tạo user chỉ khi user chưa tồn tại sẽ hơi
phức tạp hơn chút như này.
DO $$
BEGIN
CREATE ROLE myuser WITH LOGIN PASSWORD 'mypassword';
EXCEPTION WHEN DUPLICATE_OBJECT THEN
RAISE NOTICE 'Role myuser already exists, skipping';
END
$$;
Postgres thì không có set được authenticate theo từng host như MySQL/MariaDB. Tuy nhiên, bạn vẫn có thể set một số
config tương tự như chỉ cho phép login hoặc yêu cầu password/certificate tùy theo host ở file pg_hba.conf
.
Document đầy đủ ở đây nhé.
Tạo DB
Để tạo DB thì chúng ta dùng query CREATE DATABASE
quen thuộc. Các object trong postgres đều có owner, thường thì
người nào tạo ra nó sẽ là owner luôn. Với DB thì chỉ có user với quyền createdb
mới tạo được nên để tạo DB cho một
user khác chúng ta sẽ thêm option OWNER
.
Ví dụ để tạo DB mydatabase
với owner là myusser
thì query của chúng ta sẽ như thế này.
CREATE DATABASE mydatabase OWNER myuser;
Bạn có thể xem tất cả các option ở đây nhé.
Tương tự như CREATE USER
, CREATE DATABASE
cũng không có option IF NOT EXISTS
. Tuy nhiên, khác với query trước,
CREATE DATABASE
còn không được phép chạy trong một transaction khác nữa, vậy nên chúng ta không thể dùng cách trên
để tạo DB chỉ khi nó chưa tồn tại được.
Nếu chạy từ shell script thì bạn có thể dùng psql
để tạo DB như thế này.
echo "SELECT 'CREATE DATABASE mydb' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec" | psql
Nếu phải chạy trong SQL script thì sẽ hơi phức tạp một chút. Chúng ta sẽ cần đến extension dblink
.
Bạn sẽ phải tự connect đến server DB hiện tại để chạy query thay vì chạy trực tiếp 🤣.
DO $$
BEGIN
PERFORM dblink_exec('password=yourpassword', 'CREATE DATABASE mydb OWNER myuser');
EXCEPTION WHEN DUPLICATE_DATABASE THEN
RAISE NOTICE 'Database mydb already exists, skipping';
END
$$;
Ngoài ra trong mỗi database còn có các schema nữa. Mặc định thì mỗi DB sẽ được tạo với một schema public. Ai có quyền
truy cập DB cũng sẽ truy cập được schema này. Bạn có thể tạo schema với query CREATE SCHEMA
.
CREATE SCHEMA myschema;
Bạn có thể xem tất cả option ở đây nhé.
Phân quyền cho user
Hệ thống phân quyền của Postgres khá là phức tạp 🙄. Chúng ta sẽ có quyền với DB, với schema, thậm chí tới từng object (table,...).
Ngoài owner và superuser ra thì các user khác sẽ không có quyền gì với các object mới được tạo ra.
Để cấp quyền cho user thì chúng ta dùng query GRANT
.
Có các quyền như sau
CONNECT
: connect tới DBUSAGE
: cái này hơi bị phức tạp, bạn chịu khó xem docs nhé 😜CREATE
/SELECT
/INSERT
/UPDATE
/DELETE
/TRUNCATE
: được chạy các query tương ứngEXECUTE
: gọi functionREFERENCES
/TRIGGER
/TEMP
/TEMPORARY
: quyền tạo foreign key, trigger, bảng tạm thờiALL PRIVILEGES
: tất cả mọi quyền có thểGRANT
Chi tiết thì ở đây nhé.
Để thêm quyền xem (readonly) cho một user chúng ta cần những query như thế này.
GRANT CONNECT ON DATABASE mydb TO my_readonly;
GRANT USAGE ON SCHEMA public TO my_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO my_readonly;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO my_readonly;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO my_readonly;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO my_readonly;
Bạn có thể thấy là quyền phải được GRANT
theo tận schema chứ không phải cả DB. Bạn có thể thay public
bằng schema
khác.
Ngoài ra còn một tính năng hay ho nữa. Mỗi role trong postgres vừa có thể là user mà cũng có thể là một group nữa.
Các user trong cùng group sẽ được "kế thừa" những quyền của group đó. Ví dụ sau khi đã có user my_readonly ở trên rồi,
mình muốn tạo một user my_readwrite với cả quyền ghi nữa, thì thay vì phải lặp lại các quyền như trên, mình chỉ cần
GRANT
cho nó các quyền của my_readonly thôi 😉.
GRANT my_readonly TO my_readwrite;
Sau đó thêm vài quyền nữa là xong;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO my_readwrite;
Privilege cho các object được tạo về sau
Nhưng không phải thêm quyền thôi là xong đâu nhé 🙄. Như mình có nói ở trên thì mặc định chỉ có superuser và owner mới có quyền với các object mới được tạo ra thôi. Nghĩa là dù đã cấp quyền cho schema và tất cả table bên trong nó thì khi một table mới được tạo ra, các user đã được cấp quyền trước đó đều không có quyền với các table mới.
Để giải quyết vấn đề này bạn cần thay đổi quyền mặc định (DEFAULT PRIVILEGES
) của các object trong schema để khi
có object mới được tạo nó sẽ có ngay quyền cho các user mà bạn muốn.
Ví dụ để thêm quyền readonly mặc định cho các table mới được tạo ra thì mình dùng các query thế này.
ALTER DEFAULT PRIVILEGES FOR ROLE myuser IN SCHEMA public GRANT SELECT ON TABLES TO my_readonly;
ALTER DEFAULT PRIVILEGES FOR ROLE myuser IN SCHEMA public GRANT SELECT ON SEQUENCES TO my_readonly;
ALTER DEFAULT PRIVILEGES FOR ROLE myuser IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO my_readonly;
Như bạn thấy thì default prvileges cũng chỉ áp dụng khi object được tạo bởi một user nhất định thôi 🤣.
Vậy nên bạn cần thay đổi nó cho tất cả các user có quyền CREATE
trong schema của bạn nhé.
Phân quyền theo group
Ở phần đầu tiên thì mình có nhắc đến một loại role khác là no-login role. Chúng ta đã biết user là alias cho login role rồi. Và một role thì ngoài user ra còn có thể là một group nữa. Vậy no-login role hẳn là được dùng để tạo group rồi 😃.
Mình có thể tạo group với các quyền như readonly, readwrite, hay thậm chí là cả owner để assign cho các user khác. Ví dụ mình có thể tạo db với nhiều owner bằng cách assign quyền my_owner cho các user khác như thế này.
CREATE ROLE my_owner;
CREATE DATABASE mydb OWNER my_owner;
GRANT my_owner TO john_smith;
GRANT my_owner TO whoever;
Các DEFAULT PRIVILEGES
cấp quyền cho cả group nên các user trong group cũng sẽ được kế thừa quyền luôn nhé.
Ví dụ với default privileges như thế này.
ALTER DEFAULT PRIVILEGES FOR ROLE myuser IN SCHEMA public GRANT SELECT ON TABLES TO my_readonly;
Thì các user của group my_readonly sẽ có quyền select trên tất cả table mới được tạo bời myuser. Tuy nhiên, trong trường hợp ngược lại, nếu table được tạo bởi một user khác trong group myuser thì default privileges sẽ không có tác dụng đâu nhé.
Hết rồi. Cũng không khó tí nào phải không 😉. Chúc bạn thành công nhé.