Question

I have implemented the stored procedure to check if a specific [schema].[procedure] exists.

CREATE PROCEDURE [BlobCheckExisting].[usp_DoesStoredProcedureExist] 
@schemaName varchar(128),
@procedureName varchar(128)
AS
SELECT IIF (EXISTS (SELECT 1 FROM sys.procedures p INNER JOIN sys.schemas s ON p.[schema_id] = s.[schema_id] 
    WHERE s.[name] = @schemaName  AND p.[name] = @procedureName), 1, 0)
GO

When I tested with sa it selected 1 as I expected but when I tried with a minimum permission account which only granted EXECUTE to the procedure, it selected 0.

My assumption of stored procedure mechanism was role/user only requires EXECUTE to perform the procedure. We don't need to concern the permission setting of each DB object like CRUD to table and so on.

But it seems sys tables are exceptions? Or did I miss anything?
Moreover, If I don't want to grant select sys.procedures/sys.schemas to the role. I just want my procedure to work as expected, what should I do?

Was it helpful?

Solution

My assumption of stored procedure mechanism was role/user only requires EXECUTE to perform the procedure. We don't need to concern the permission setting of each DB object like CRUD to table and so on.

This statement is true when ownership chaining applies. With an unbroken chain, users do not need permissions on indirectly referenced objects.

However ownership chaining does not apply metadata visibility when selecting from the catalog views. Users must be granted some permissions on an object in order for it to be visible when selecting from the catalog views.

Below is a script that shows this behavior for your use case. If you need to return objects users have no permissions to use, you'll need to use EXECUTE AS or certificate signing to elevate permissions for users of the BlobCheckExisting.usp_DoesStoredProcedureExist procedure.

CREATE SCHEMA BlobCheckExisting;
GO

CREATE PROCEDURE [BlobCheckExisting].[usp_DoesStoredProcedureExist] 
@schemaName varchar(128),
@procedureName varchar(128)
AS
SELECT IIF (EXISTS (SELECT 1 FROM sys.procedures p INNER JOIN sys.schemas s ON p.[schema_id] = s.[schema_id] 
    WHERE s.[name] = @schemaName  AND p.[name] = @procedureName), 1, 0)
GO

CREATE PROCEDURE dbo.usp_TestProc AS RETURN;
GO

CREATE LOGIN MinimallyPrivilegedUser WITH PASSWORD = 'sdf4$$$gFx&';
GO

CREATE USER MinimallyPrivilegedUser;
GO

GRANT EXECUTE ON BlobCheckExisting.usp_DoesStoredProcedureExist TO MinimallyPrivilegedUser;
GO

--test permissions without permissions on object
EXECUTE AS USER = 'MinimallyPrivilegedUser';
GO
--no rows returned because user has no permissions on dbo.usp_TestProc
EXEC [BlobCheckExisting].[usp_DoesStoredProcedureExist] @schemaName = N'dbo', @procedureName = N'usp_TestProc';
GO
REVERT;
GO
--grant permissions
GRANT EXECUTE ON dbo.usp_TestProc TO MinimallyPrivilegedUser;
GO

--test permissions with permissions on object
EXECUTE AS USER = 'MinimallyPrivilegedUser';
GO
--row returned because user has execute permissions on dbo.usp_TestProc
EXEC [BlobCheckExisting].[usp_DoesStoredProcedureExist] @schemaName = N'dbo', @procedureName = N'usp_TestProc';
GO
REVERT;
GO
Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top