My Snowflake user has the SYSADMIN role; however, I would like to be able to change my RSA_PUBLIC_KEY and RSA_PUBLIC_KEY_2 properties myself. Is there a combination of Roles and privileges that could be created to allow a user to modify only their RSA_PUBLIC_KEY and RSA_PUBLIC_KEY_2 properties?
These are not one of the properties that a user can change themselves:
https://docs.snowflake.net/manuals/sql-reference/sql/alter-user.html
More specifically, securityadmin (or accountadmin) role is required: https://docs.snowflake.net/manuals/user-guide/security-access-control-overview.html
Also discussed here:
https://snowflakecommunity.force.com/s/question/0D50Z00009Q8cYtSAJ/createalter-user
* UPDATE *
As a workaround, create a stored procedure owned by the SECURITYADMIN role, with the EXECUTE AS OWNER option in the definition, and grant access to to your users:
use role securityadmin;
create or replace procedure p_set_rsa_key_for_user_x(RSA_KEY string)
returns text
language javascript
execute as owner
as
$$
const stmt = snowflake.createStatement({
sqlText: "alter user user_x set RSA_PUBLIC_KEY = ?",
binds: [RSA_KEY]
})
const rs = stmt.execute()
return 'SUCCESS'
$$
;
grant usage on procedure p_set_rsa_key_for_user_x(string) to role analyst;
use role analyst;
call p_set_rsa_key_for_user_x('MIIBIjANBgkqh...');
Where 'user_x' is the username in question, and 'analyst' is a role they've been granted.
Hope that's helpful...
It isn't technically needed but might be an idea to add a check to see if the user executing the Stored Procedure is the person(s) for which this SP has been provided. Add "select current_user()" into the code and test against a user name to improve the security perhaps...
There is an example in case if you want one general procedure which allow rotate only user owned key.
use role securityadmin;
CREATE OR REPLACE PROCEDURE SET_RSA_PUBLIC_KEY(RSA_PUBLIC_KEY VARCHAR)
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
EXECUTE AS OWNER
AS
$$
DECLARE
user VARCHAR DEFAULT (SELECT CURRENT_USER());
BEGIN
alter user IDENTIFIER(:user) set RSA_PUBLIC_KEY = :RSA_PUBLIC_KEY;
return 'Success';
END;
$$;
grant usage on procedure SET_RSA_PUBLIC_KEY(string) to role public;
call SET_RSA_PUBLIC_KEY('MIIBIjANBgkqh...');
Related
Create procedure test.access
#principal_id int
with execute as owner
as
set nocount on;
begin
select * from sys.database_principals where principal_id = #principal_id
end;
I create the above procedure and execute but it returns nothing. However if I declare #principal_id as a variable, set it then run this code outside a procedure it returns the correct rows.
Why isn't it working inside the procedure?
If the owner of the test schema does not have elevated permissions, then the rows that can be seen in sys.database_principals will be limited. From the documentation
Any user can see their own user name, the system users, and the fixed database roles. To see other users, requires ALTER ANY USER, or a permission on the user. To see user-defined roles, requires ALTER ANY ROLE, or membership in the role.
In the following script I create the user test, as well as a schema test. I make the test user the owner of the test schema.
I then create a procedure in that schema with execute as owner. It will therefore execute in the context of the test user.
But the test user does not have ALTER ANY USER permission, so the rows visible in sys.database_principals are limited:
create login test with password = 'test';
create user test for login test;
go
create schema test authorization test;
go
create procedure test.access with execute as owner as
begin
select * from sys.database_principals;
end
go
exec test.access; -- returns a limited set principals
If I grant the test user the alter any user permission and then re-execute the procedure, I will get all of the users:
grant alter any user to test;
exec test.access; -- returns all users (but not user defined roles)
grant alter any role to test;
exec test.access; -- returns everyone and all roles
If the test schema is currently not owned by the correct user, you can change that:
alter authorization on schema::test to [user];
I have created a Procedure in snowflake
I want to give read-privileges of that stored procedure in snowflake to some other user/role.
GRANT USAGE ON PROCEDURE
get_column_scale(float)
TO ROLE other_role_name_here;
I believe the above command would also give the write access, which I don't want. Is there a way in which only read access can be granted
The only privilege to assign to a procedure is usage, so if you want a role to be able to read the definition for the procedure but not run it, then you can use a stored procedure defined to execute with owner's rights:
create or replace procedure read_procedure(PROCEDURE_PATH string)
returns string
language javascript
execute as owner
as
$$
let rs = snowflake.execute({sqlText: `select get_ddl('procedure', ?) as DDL`, binds:[PROCEDURE_PATH]});
rs.next();
return rs.getColumnValue('DDL');
$$;
create or replace procedure FOO(S string)
returns string
language javascript
as
$$
return S;
$$;
grant usage on procedure READ_PROCEDURE(string) to role MY_ROLE;
call read_procedure('FOO(string)');
In this setup, make sure that MY_ROLE has usage on procedure read_procedure(string) but does not have usage on procedure foo(string). That way the role will be able to see the definition of foo(string) without the privilege required to execute it (usage).
Note that since the read_procedure(string) procedure runs with owner's rights, anyone calling it will be able to read any procedure that the owner's role can read.
Edit: You can also read the SP in parts in the INFORMATION_SCHEMA.PROCEDURES view. You can grant select on that view without granting USAGE on the procedure.
I have a user who is a member of the db_DataReader role (and no other roles apart from public), and has been granted explicit execute permission on a scalar function, but when they use the function
select hbp_plant.CatComments(42)
they get
The EXECUTE permission was denied on the object 'CatComments', database 'HBDevSIMCOA', schema 'HBP_Plant'*.
How do I give them permission to call the function without giving them any ability to modify the database?
Does the function access tables in different schemas, other than hbp_plant?
Instead of adding the db user to the db_datareader role, grant SELECT (for the whole db) and execute permissions on the function:
--db user = myreadonlyuser
grant select to myreadonlyuser; --can read from tables, table valued functions, views..in all schemas
grant execute on hbp_plant.CatComments to myreadonlyuser;
Give Exec Permission on tablename. Try this.
USE HBDevSIMCOA;
GRANT EXEC ON hbp_plant.CatComments TO PUBLIC
you can refer below link
The EXECUTE permission was denied on the object 'xxxxxxx', database 'zzzzzzz', schema 'dbo'
Try this:
CREATE SCHEMA [hbp_plant];
GO
CREATE FUNCTION [hbp_plant].[CatComments] (#A INT)
RETURNS INT
AS
BEGIN
RETURN #A
END;
GO
CREATE USER [StackOverflow] WITHOUT LOGIN;
-- do not work
EXECUTE AS USER = 'StackOverflow';
SELECT [hbp_plant].[CatComments](5) ;
REVERT;
GRANT EXECUTE ON [hbp_plant].[CatComments] TO [StackOverflow];
-- work
EXECUTE AS USER = 'StackOverflow';
SELECT [hbp_plant].[CatComments](5) ;
REVERT;
DROP USER [StackOverflow];
DROP FUNCTION [hbp_plant].[CatComments]
DROP SCHEMA [hbp_plant];
I can't find an answer to this. Problem is that I need to use master database and then I don't know how to specify that I am granting this select to a role in another database. When using "use master" it doesn't work because the principal is in another database and you can't add e.g. Database.dbo.role prefixes to principles. How do I do that? Even granting through SSMS UI doesn't work.
I need this: GRANT SELECT ON Syf.sys.server_principals to Syf.dbo.usersys
What am I missing? Am I thinking about this in a wrong way?
Even when I try it with the user on master database and call "execute as user = 'user'" and then select from sys.server_principals it still return only a few records. I apparently don't understand how these permissions work, it's beyond my logic. It seems there are some other objects that I need to grant permissions for.
I need to use "with execute as 'privilegedUser'", when I do that then we are in user context of that SP database and that user can't have permission to access sys.server_principals.? I need this because that SP deletes user and login if login with the same name exists. The reason I need to execute with the privileged user is because my database has multitenancy and every user is bound to one TenantId and when he or other user goes to delete the user, security policy complains that he has no right for that.
I found a way to do this, I have to grant select to sys.server_principals to guest user on master db, that gives guest some more privileges.
From the documentation on sys.server_principals, the needed permissions are:
Any login can see their own login name, the system logins, and the fixed server roles. To see other logins, requires ALTER ANY LOGIN, or a permission on the login. To see user-defined server roles, requires ALTER ANY SERVER ROLE, or membership in the role.
The visibility of the metadata in catalog views is limited to securables that a user either owns or on which the user has been granted some permission. For more information, see Metadata Visibility Configuration.
But all is not lost. We can use module signing to create a stored procedure that can allow you to do what you need to do.
use master;
create master key encryption by password = 'an unguessable password!'
alter master key add encryption by service master key
create certificate [CodeSigningCert]
with expiry_date = '2018-12-31',
subject = 'Code signing'
go
create login [CodeSigningLogin] from certificate [CodeSigningCert]
grant alter any login to [CodeSigningLogin]
go
SELECT 'CREATE CERTIFICATE ' + QUOTENAME([name])
+ ' AUTHORIZATION ' + USER_NAME([c].[principal_id])
+ ' FROM BINARY = ' + CONVERT(VARCHAR(MAX), CERTENCODED([c].[certificate_id]), 1)
+ ' WITH PRIVATE KEY (BINARY = '
+ CONVERT(VARCHAR(MAX), CERTPRIVATEKEY([c].[certificate_id], 'f00bar!23'), 1)
+ ', DECRYPTION BY PASSWORD = ''f00bar!23'')'
FROM [sys].[certificates] AS [c]
WHERE [name] = 'CodeSigningCert'
use tempdb
go
create master key encryption by password = 'foobar!23'
-- c/p the create certificate code generated above here
-- to create the same certificate in tempdb
create user CodeSigningUser from certificate CodeSigningCert
go
create login [foobar] with password = 'foobar!23'
create user [foobar]
go
create procedure dbo.listServerPrincipals
as
begin
select *
from sys.server_principals
end
go
grant execute on dbo.listServerPrincipals to foobar
go
execute as login = 'foobar'
go
exec dbo.listServerPrincipals
revert
go
add signature to dbo.listServerPrincipals by certificate [CodeSigningCert]
go
execute as login = 'foobar'
go
exec dbo.listServerPrincipals
revert
go
It seems like a lot, but in essence you're doing the following:
creating a certificate in master
creating a login from that certificate
creating that same certificate in your user database (I used tempdb as a standin here)
create a user for that certificate
create a login/user to represent your application user
create a procedure that does the select
try to execute it as the app login. it "works", but looks no different than if you'd done the select yourself
add a signature to the procedure
try to execute the procedure again. this time, it should return all the data
I've figured out a solution where I don't need to use "with execute as owner" in this first procedure, but in the second one that this first one calls. In the first one, I can select everything I need from sys tables and pass the info to a second one that has "with execute as owner" which is in the schema forbidden to a user.
Even better solution:
alter trigger [dbo].[AfterInsertUpdateTenant_Korisnici]
on [dbo].[Korisnici]
with execute as owner
for insert
as
execute as user = original_login();
declare #TenantId int = dbo.GetCurrentTenantId();
revert;
update dbo.Korisnici
set TenantId = #TenantId
from Inserted i
where dbo.Korisnici.Id = i.Id;
I have 2 schemas and one of the objects in the first schema needs to access an object in the other schema. For example:
CREATE VIEW I.ITest
AS
SELECT 1 as TestColumn
GO
CREATE VIEW O.OTest
AS
SELECT * FROM I.ITest
GO
EXEC ('SELECT * FROM O.OTest') AS USER = 'TestUser'
DROP VIEW O.OTest
DROP VIEW I.ITest
In the above example, TestUser only has access to the 'O' Schema. So the select itself works fine, but because the view is doing a select from another schema 'I' then it fails with the error:
The SELECT permission was denied on the object 'ITest', database 'MyDB', schema 'I'.
To get around this I can give the 'O' schema permission to access the 'I' schema, but this doesn't sound right and looks to be bypassing the schema permissions.
What can be done? Am I doing this all wrong? Whats the best practice in this scenario?
Thanks
UPDATE: My schemas were owned by different db roles so I got around this problem by simply changing the owner of both to dbo and then giving the db role permission to access the schema. This meant that the owner dbo could see everything and I could then give specific permission to the db role only and the rest of the db objects were not available unless via this schema. Thanks for your help
You should wrap the selection of data from the view on the "other" Schema, within a stored procedure. Then grant execute rights on the stored procedure. Although the user will not have direct access to the view they are permitted access via the stored procedure.
Here is an example walkthrough for you demonstrating the security principles as work:
USE DATABASE SANDBOX;
--Create Logins
CREATE LOGIN UserOne WITH Password='Hello123';
CREATE LOGIN UserTwo WITH Password='Hello123';
--Create Database Users
CREATE USER UserOne;
CREATE USER UserTwo;
--Create the Test Schemas
CREATE SCHEMA SchemaOne AUTHORIZATION UserOne;
CREATE SCHEMA SchemaTwo AUTHORIZATION UserTwo;
--Create a View on SchemaOne
CREATE VIEW SchemaOne.ViewOne
AS SELECT 1 as TestColumn;
--Create a View on SchemaTwo
CREATE VIEW SchemaTwo.ViewTwo
AS SELECT * FROM SchemaOne.ViewOne;
--Test that the SchemaOne
EXEC('select * from SchemaOne.ViewOne') AS USER = 'UserOne'
--1
EXEC('select * from SchemaTwo.ViewTwo') AS USER = 'UserOne'
--The SELECT permission was denied on the object 'ViewTwo', database 'SANDBOX', schema 'SchemaTwo'.
--Create a stored procedure to safely expose the view within SchemaTwo to UserOne who's default Schema is
--SchemaOne.
CREATE PROCEDURE SchemaTwo.proc_SelectViewTwo
AS
select * from SchemaTwo.ViewTwo;
--Grant execute rights on the procedure
GRANT EXECUTE ON SchemaTwo.proc_SelectViewTwo TO UserOne;
--Test the
EXECUTE AS LOGIN='UserOne';
Exec SchemaTwo.proc_SelectViewTwo;
revert;
An alternative approach as suggeted in my comments would be to use a Database Role to control access to multiple schemas. Using the principals as defined in the solution above, you could use Database Roles like so:
EXEC sp_addrole 'CrossSchemaRole';
EXEC sp_addrolemember 'CrossSchemaRole','UserOne';
GRANT SELECT ON SCHEMA::SchemaOne TO CrossSchemaRole;
GRANT SELECT ON SCHEMA::SchemaTwo TO CrossSchemaRole;
EXECUTE AS LOGIN='UserOne';
select * from SchemaTwo.ViewTwo;
revert;
Some suggested further reading:
Securing SQL Server
SQL Server Permission Hierarchy
White Paper: Security Overview for
Administrators
Surely you give permissions to users not objects and that's it. If you or the owner of the other schema wants to allow other users to access objects (whether they are tables, views or whatever) within it then it is up to the owner of the other schema. Just because you as the developer can write a procedure that accesses objects in other schemas it does not follow that anyone running your procedure should be allowed to do so too. Roles are the way to go.