-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Restrict DROP USER/ROLE from non-dbo user #2859
Changes from 6 commits
b946273
ec5d2b5
149ad22
799df76
675f72e
4330cdd
64538e6
918f087
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3285,15 +3285,44 @@ bbf_ProcessUtility(PlannedStmt *pstmt, | |
{ | ||
RoleSpec *rolspec = lfirst(item); | ||
char *user_name; | ||
char *db_principal; | ||
const char *db_owner_name; | ||
int role_oid; | ||
int rolename_len; | ||
|
||
user_name = get_physical_user_name(db_name, rolspec->rolename, false); | ||
db_owner_name = get_db_owner_name(get_cur_db_name()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: simply use |
||
role_oid = get_role_oid(user_name, true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why missing_ok = true? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For if exist case which is already present in some testfiles |
||
rolename_len = strlen(rolspec->rolename); | ||
|
||
if (drop_user) | ||
db_principal = "user"; | ||
else | ||
db_principal = "role"; | ||
|
||
/* If user is dbo or role is db_owner, restrict dropping */ | ||
if ((drop_user && rolename_len == 3 && strncmp(rolspec->rolename, "dbo", 3) == 0) || | ||
(drop_role && rolename_len == 8 && strncmp(rolspec->rolename, "db_owner", 8) == 0)) | ||
ereport(ERROR, | ||
(errcode(ERRCODE_CHECK_VIOLATION), | ||
errmsg("Cannot drop the %s '%s'.", db_principal, rolspec->rolename))); | ||
|
||
/* | ||
* Check for current_user's privileges | ||
* must be database owner to drop user/role | ||
*/ | ||
if ((!stmt->missing_ok && !OidIsValid(role_oid)) || | ||
!is_member_of_role(GetUserId(), get_role_oid(db_owner_name, false))) | ||
ereport(ERROR, | ||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | ||
errmsg("Cannot drop the %s '%s', because it does not exist or you do not have permission.", db_principal, rolspec->rolename))); | ||
|
||
/* | ||
* If a role has members, do not drop it. | ||
* Note that here we don't handle invalid | ||
* roles. | ||
*/ | ||
if (drop_role && !is_empty_role(get_role_oid(user_name, true))) | ||
if (drop_role && !is_empty_role(role_oid)) | ||
ereport(ERROR, | ||
(errcode(ERRCODE_CHECK_VIOLATION), | ||
errmsg("The role has members. It must be empty before it can be dropped."))); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need separate test files? Let's reuse some existing test files. IIRC we should avoid creating new test files wherever possible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don’t have any such testfile for this usecase hence created one for this usecase testing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant wherever we have tests for restricting create login/user, we can use that file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add some tests which involves |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
-- tsql | ||
create login no_priv_login1 with password = '12345678' | ||
go | ||
|
||
create role dont_drop_role | ||
go | ||
|
||
create login no_priv_login2 with password = '12345678' | ||
go | ||
|
||
create user no_priv_user2 for login no_priv_login2 | ||
go | ||
|
||
create database restrict_user_db1 | ||
go | ||
|
||
use restrict_user_db1 | ||
go | ||
|
||
create role dont_drop_role | ||
go | ||
|
||
create user no_priv_user1 for login no_priv_login1 | ||
go | ||
|
||
use master | ||
go | ||
|
||
-- tsql user=no_priv_login1 password=12345678 | ||
-- case-1 - when current user is guest | ||
-- permission denied | ||
select current_user,db_name() | ||
go | ||
~~START~~ | ||
varchar#!#nvarchar | ||
guest#!#master | ||
~~END~~ | ||
|
||
|
||
drop role dont_drop_role | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the role 'dont_drop_role', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
drop user no_priv_user2 | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the user 'no_priv_user2', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
-- tsql | ||
select current_user,db_name() | ||
go | ||
~~START~~ | ||
varchar#!#nvarchar | ||
dbo#!#master | ||
~~END~~ | ||
|
||
|
||
create user no_priv_user1 for login no_priv_login1 | ||
go | ||
|
||
-- tsql user=no_priv_login1 password=12345678 | ||
-- case-2 - when current user has no privilege | ||
-- permission denied | ||
use master | ||
go | ||
|
||
select current_user | ||
go | ||
~~START~~ | ||
varchar | ||
no_priv_user1 | ||
~~END~~ | ||
|
||
|
||
drop role dont_drop_role | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the role 'dont_drop_role', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
drop user no_priv_user2 | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the user 'no_priv_user2', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
-- tsql | ||
|
||
-- case-3 - when current login has privilege | ||
-- 3.1 - when login is database owner | ||
Alter authorization on database::restrict_user_db1 to no_priv_login2 | ||
go | ||
|
||
-- tsql user=no_priv_login2 password=12345678 database=restrict_user_db1 | ||
-- allowed | ||
select current_user | ||
go | ||
~~START~~ | ||
varchar | ||
dbo | ||
~~END~~ | ||
|
||
|
||
drop role dont_drop_role | ||
go | ||
|
||
drop user no_priv_user1 | ||
go | ||
|
||
-- error | ||
-- try to drop non existing role/user | ||
drop role fake_role | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the role 'fake_role', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
drop user fake_user | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the user 'fake_user', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
drop user db | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the user 'db', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
drop role db_own | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the role 'db_own', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
drop user dbo_u1 | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the user 'dbo_u1', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
drop role db_owner_r1 | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the role 'db_owner_r1', because it does not exist or you do not have permission.)~~ | ||
|
||
|
||
-- should deny | ||
-- try to drop dbo user, db_owner role | ||
drop role db_owner | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the role 'db_owner'.)~~ | ||
|
||
|
||
drop user dbo | ||
go | ||
~~ERROR (Code: 33557097)~~ | ||
|
||
~~ERROR (Message: Cannot drop the user 'dbo'.)~~ | ||
|
||
|
||
|
||
create role dont_drop_role | ||
go | ||
|
||
create user no_priv_user1 for login no_priv_login1 | ||
go | ||
|
||
-- tsql | ||
-- 3.2 - when login is sysadmin | ||
-- allowed | ||
drop user no_priv_user1 | ||
go | ||
|
||
drop user no_priv_user2 | ||
go | ||
|
||
drop role dont_drop_role | ||
go | ||
|
||
-- psql | ||
-- Need to terminate active session before cleaning up the login | ||
SELECT pg_terminate_backend(pid) FROM pg_stat_get_activity(NULL) | ||
WHERE sys.suser_name(usesysid) = 'no_priv_login1' AND backend_type = 'client backend' AND usesysid IS NOT NULL; | ||
go | ||
~~START~~ | ||
bool | ||
t | ||
~~END~~ | ||
|
||
|
||
|
||
-- tsql | ||
drop login no_priv_login1 | ||
go | ||
|
||
-- psql | ||
-- Need to terminate active session before cleaning up the login | ||
SELECT pg_terminate_backend(pid) FROM pg_stat_get_activity(NULL) | ||
WHERE sys.suser_name(usesysid) = 'no_priv_login2' AND backend_type = 'client backend' AND usesysid IS NOT NULL; | ||
go | ||
~~START~~ | ||
bool | ||
t | ||
~~END~~ | ||
|
||
|
||
-- tsql | ||
drop login no_priv_login2 | ||
go | ||
|
||
drop database restrict_user_db1 | ||
go |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should pfree
db_principal
.db_owner_name
is a const char*, it can't be freed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not pfree db_principal as it is pointing to string literal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is it pointing to string literal then make it const char * here itself.
const char * db_principal = drop_user ? "user" : "role"