0

I am logged in as sa (double checked it in my session with select suser_name()).

I have created a login cards_login_generator, and the same-named user on the database [cards]. I want to permit this user to create logins,users, and make them members to db_datareader and db_datawriter.

I've granted ALTER ANY LOGIN/USER and it works. Now the time comes for:

grant alter on role::[db_datareader] to cards_login_generator

which fails with:

Cannot find the role 'dbo.db_datareader', because it does not exist or you do not have permission.

I have searched exhaustively around but cannot imagine any reason for this message. The role is a (probably undeletable?) database role, and I am sa so no permission issue could be the case. Why does this fail?

Microsoft SQL Server 2017 (RTM-GDR) (KB5014354) - 14.0.2042.3 (X64)

Ronaldo
6,0272 gold badges14 silver badges43 bronze badges
asked Oct 13, 2022 at 9:16

2 Answers 2

2

As mentioned in this answer, modifying membership in a fixed database role requires the modifying principal to be a member of the db_owner fixed database role.

Here's a test-bed to show how this works. First off, we'll create a login that has permission to create other logins, then we'll add that login to our [Test] database where we'll create a user for the login, and grant that user permission to alter users and roles.

USE [master];
GO
CREATE LOGIN [user_creator_test] 
WITH 
 PASSWORD = N'SomeExceptionally
StrongPassword5%'
 , DEFAULT_LANGUAGE = us_english
 , DEFAULT_DATABASE = [Test];
GRANT ALTER ANY LOGIN TO [user_creator_test];
GO
USE [Test];
GO
CREATE USER [user_creator_test]
FOR LOGIN [user_creator_test]
WITH DEFAULT_SCHEMA = [dbo];
GRANT ALTER ANY USER TO [user_creator_test];
GRANT ALTER ANY ROLE TO [user_creator_test];
GO

Next, we'll impersonate the new login and attempt to create a new login and user, and add that user to the fixed database roles, db_datareader and db_datawriter:

EXECUTE AS LOGIN = N'user_creator_test';
CREATE LOGIN [test_login] 
WITH PASSWORD = N'SomeCrazyHardToGuessPasswords27%'
 , DEFAULT_DATABASE = [Test]
 , DEFAULT_LANGUAGE = us_english;
CREATE USER [test_login_user]
FOR LOGIN [test_login]
WITH DEFAULT_SCHEMA = [dbo]
ALTER ROLE db_datareader ADD MEMBER [test_login_user];
ALTER ROLE db_datawriter ADD MEMBER [test_login_user];
REVERT;

The ALTER ROLE statements above fail, reporting this error:

Msg 15151, Level 16, State 1, Line 83
Cannot alter the role 'db_datareader', because it does not exist or you do not have permission.
Msg 15151, Level 16, State 1, Line 84
Cannot alter the role 'db_datawriter', because it does not exist or you do not have permission.

However, if we make the [user_creator_test] a member of the db_owner role in our [Test] database, then re-run the test, it succeeds:

ALTER ROLE db_owner ADD MEMBER [user_creator_test];
GO
EXECUTE AS LOGIN = N'user_creator_test';
SELECT USER_NAME(), DB_NAME();
ALTER ROLE db_datareader ADD MEMBER [test_login_user];
ALTER ROLE db_datawriter ADD MEMBER [test_login_user];
REVERT;
SELECT USER_NAME();
GO

This query displays database role membership:

SELECT 
 [role] = dpr.[name]
 , [member] = dpm.[name]
FROM 
 [sys].[database_principals] dpm
 INNER JOIN [sys].[database_role_members] drm ON dpm.[principal_id] = drm.[member_principal_id]
 INNER JOIN [sys].[database_principals] dpr ON drm.[role_principal_id] = dpr.[principal_id]
ORDER BY 
 [role]
 , [member];

Results:

role member
db_datareader test_login_user
db_datawriter test_login_user
db_owner dbo
db_owner user_creator_test

Cleanup:

USE [master];
GO
IF EXISTS
(
 SELECT 1
 FROM [sys].[server_principals] sp
 WHERE sp.[name] = N'test_login'
)
BEGIN
 DROP LOGIN [test_login];
 PRINT N'Dropped login [test_login]';
END
GO
USE [Test];
GO
IF EXISTS
(
 SELECT 1
 FROM [sys].[database_principals] dp
 WHERE dp.[name] = N'test_login_user'
)
BEGIN
 DROP USER [test_login_user];
 PRINT N'Dropped user [test_login_user]';
END
GO
USE [master];
GO
IF EXISTS
(
 SELECT 1
 FROM [sys].[server_principals] sp
 WHERE sp.[name] = N'user_creator_test'
)
BEGIN
 DROP LOGIN [user_creator_test];
END
GO
USE [Test];
GO
IF EXISTS
(
 SELECT 1
 FROM [sys].[database_principals] dp
 WHERE dp.[name] = N'user_creator_test'
)
BEGIN
 DROP USER [user_creator_test];
END
GO
answered Oct 13, 2022 at 18:24
1

According to the ALTER ROLE doc, in order to be able to alter the membership of a fixed database role a user must also be member of the db_owner on the database:

Additionally, to change the membership in a fixed database role you need:

  • Membership in the db_owner fixed database role
answered Oct 13, 2022 at 11:09

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.