I have a web app that allows users to edit configuration tables in my database.
For audit purposes, I would like to capture the user_name of the authenticated user rather than the account that the web app uses to execute the queries.
Previously my database was on MSSQL. We could pass the user_name into context_info and the trigger will then reflect the user's user_name.
Is there a way to achieve similar results on Snowflake? I have explored Streams + Query_history but that only tells me that a query was fired through the web app account.
Snowflake captures all queries executed and users can view those queries via either
the SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY view or via the information_schema
(e.g. information_schema.query_history_by_user).
If your web app is using a single Snowflake user (sometimes called service accounts, or batch ids, or similar),
and you need to find out what queries "application end users" are causing your single service account to execute,
I'd suggest leveraging Snowflake's "query tags" for this.
To leverage query tags, simply execute a command like the following, before each query or set of queries performed by your application, by your service account, on behalf of the end user.
ALTER SESSION
SET QUERY_TAG = '{"application":"Awesome Web App", "endUser":"Rich Murnane"}';
Note: You don't need to use JSON, but I think it's cool :-)
Once you start logging your queries performed by end users,
you can find out what end users are doing by querying by the QUERY_TAG field in the SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY view, or the information_schema query_history/query_history_by* table functions.
Links:
https://docs.snowflake.com/en/sql-reference/account-usage/query_history.html
https://docs.snowflake.com/en/sql-reference/functions/query_history.html
https://docs.snowflake.com/en/sql-reference/sql/alter-session.html
Related
Good afternoon,
I am writing a front-end for a research database that holds sensitive health information. My institution has a policy that user actions be logged by the SQL server so that they can perform audits on the server log files in the event of a breach.
Because of this policy, I cannot connect Django to the db as a system user (otherwise, all users of the front-end actions would be logged by the server as the Django system user instead as the actual user individually).
Is there a way to connect to the DB using per user credentials so that actions performed on the front end will be logged as that user on the db server? I have been able to find a lot of information about using multiple databases, but nothing about per user authentication of those databases.
Thank you in advanced!
I don't think you can do that, the user that connect to the database need to have access to all the tables.
I had a similar issue when I wanted to use Django models outside Django and restrict access to certain models for certain users.
I ended up using SQLAlchemy and its automap feature on the existing Django database. Then you can connect to the database using your SQL users.
However, if you don't mind all the users accessing all the tables and are only concerned about the logs, maybe you can use a different settings.py or at least a different DATABASES configuration for each user?
I was able to accomplish this by giving the SQL user the IMPERSONATE permission and performing EXECUTE AS prior to the DB queries that I needed to have logged in models.py.
cursor = self.connection.cursor()
try:
cursor.execute("EXECUTE AS " + get_current_user()
except DatabaseError as e:
cursor.close()
raise e
ALTER LOGIN allows one to change the CHECK_EXPIRATION property associated with an account, but how does one get the existing value of this property for an arbitrary user?
You can get this data through the LOGINPROPERTY() system function:
select loginproperty('your_login_name', 'daysuntilexpiration');
If you want to see if the SQL logins is subject to expiration, just check sys.sql_logins:
select name, is_expiration_checked
from sys.sql_logins;
Note: As per the documentation on CHECK_EXPIRATION, this only applies to SQL logins, not Windows logins. If you need to get this expiration for Windows accounts, then I recommend you create programmatic logic (outside of SQL Server) to grab the login(s) from SQL Server, and then make AD calls to get expiration date. To do this with PowerShell, this seems to be a good blog post on a quick methodology.
In SQL Server I have a many to many relationship between items and active directory groups. I want to build a query, that based on a supplied active directory user, I would be able to query for all items associated to an active directory group if the user is a member of the group.
I went down the road of using IS_Member, but that only works for the currently connected user. The stored procedure will be called by an asp.net web app, which currently connects with a specific sql user account. I don't think I can connect using integrated authentication and impersonation in the web app, because I don't beleive our infrastructure configuration will allow delegation from the user machine, through the web server, then to the db server (3 hop issue).
What can I do here?
Write a C# or VB.NET .exe that queries AD and populates a table in the database with all the users/groups and call it from a SQL job that you execute daily. Then just use the synched up table data to do the comparisons. This way you can avoid all the other complexity of trying to do it on the fly. Group membership doesn't change that often. Even if something changed in AD you can just manually run your "sync job" and things would be ok. You can use Windows.Identity() or whatever it is from ASP.NET to check the username.
The issue you describe is a classic double-hop scenario, which can be (eventually) resolved through the painstaking process known as Kerberos configuration. A lazier workaround would involve passing the credentials from the asp.net application as a variable to a SQL query on your database.
If the SQL Server has the LDAP Server configured as a linked server, you could rewrite your stored procedures to accept the user as an input variable and check to see if the user is a member of an AD group before proceeding. Consider incorporating OPENQUERY into your stored procedures as shown below:
CREATE PROCEDURE CheckAccess
#CurrentUser varchar(max)
AS
IF #CurrentUser IN
(
SELECT CN
FROM OPENQUERY(ADSI,'<LDAP://DC=Your,DC=DomainComponent,DC=com>;(&(CN=*)
(memberOf=CN=YourADGroupName,OU=Your,OU=OrganizationalUnit,OU=Name,DC=Your,DC=DomainComponent,DC=com));CN')
)
THEN
SELECT 'Authorized User'
ELSE
SELECT 'Unauthorized User'
END
If you can, consult with your LDAP admins to make sure you get the group's correct domainComponents and organizationalUnits to tweak the OPENQUERY. One drawback to this is that it can take a while to query your AD group, obviously depending on the size of membership. It can be a pain, but as long as your app can pass the user as a variable, you can leverage OPENQUERY or even query sys.database_principals to check their access.
My team has a service deployed internally, and part of this service is a list of client accounts stored in a sql table. SSRS is hosted on another server and we have integration jobs which [will eventually] pull these client accounts (along with additional info) from our 3 production environments to this SSRS database.
Also on this SSRS database, I’m creating a new table that will be a mapping of domain accounts and client accounts. I need this table so I can filter my report based on which client accounts the logged on user is allowed to see.
Pretty simple so far.
The next requirement of this is that I need to restrict access to the report itself. I understand I could normally use a security group to do this, but that would result in two separate locations to manage permissions for one resource and this is what I want to avoid.
The solution I’m looking into is to create a security extension to validate the logged in user against the database, allowing them access to the folder/report if they exist in the table. Once in, I can then use that same table again to filter their results.
What I’m not sure of is 1) if this is the best solution and 2) can I use a security extension for just MY portion of the site. There are many other users and reports on this site that I don’t deal with and don’t want to conflict with those.
Could you fill the DB table automatically from AD? Then you can use the standard windows security, but still only do the administration in Active Directory.
link text
You could set up an internal report parameter, called something like UserID, and set its default value to be the non-queried expression =User!UserID . (This user ID can be selected from the list of globals in the Edit Expression dialog.)
You could then add a cartesian/cross join to your users table in your query, with a selection condition based on your internal report parameter - eg. ...and UserTable.ID = #UserID . This would ensure that no records were returned if an unauthorised user was running the report.
Note that the User!UserID field will only return the user for interactively-run reports - in scheduled reports, this will be the account for the scheduling service.
Can't you restrict access to the report by using a security group (either in it's own folder or report level permissions). Use windows authentication in your datasource connection and filter you report retrieving your username using the sql function ORIGINAL_LOGIN?
We are developing a service layer for a new system that will handle all interactions with the MSSQL (2005) database. We are a bit perplexed as to how to capture all of the 'who done it' information that is required by our users in some of our legacy audit tables. While we could pass in the users name that was modifying data and log the call, we have some legacy tables that we will be using which have triggers to capture the system_user on record inserts, updates, and deletes. We also enlist some row level security in some places that we would also like to leverage without changing the code if possible. I have read that some are using contextinfo to store the user, but that seems a little less than secure in this situation.
The option that I like best is using the execute as user on a per stored procedure call basis
execute sp_myproc #foo as user = 'username'
The problem that we are running into is that within the entity framework it does not appear to be possible to add the execute as commands to the stored procedure calls.
Thanks for any input.
"EXECUTE AS" does not support procedure calls (it would be in the actual proc definition). Only remote or dynamic SQL.
Options:
Any middle tier has to pass the end user as a parameter.
We do this for our web services and GUIs where there is no direct connection to the database
Enable server delegation so each server can pass through the end user credentials. Basically "impersonation" in asp.net.
Issue a separate command first
Have you tried SETUSER?
http://msdn.microsoft.com/en-us/library/ms188315.aspx
Doug,
I assume that the mid tier on a different system, and that you need the user information on the database (i.e., not in the mid-tier). If this isn't correct, please let me know.
If you using Windows Authentication, you will need to set up delegation. This will allow you to impersonate the end user in the database. Are you using Windows Authentication?
Erick