I want to implement an audit table and I have no idea how am I supposed to get the username.
I am using C# and Sql Server. I have a Users table in my database. When I log in my windows form application I verify if the correct combination of username and password is used. But how do I inform the database of the current user? I thought of adding an extra column to my Users table in which to set on 1 the logged username. Is that a solution for single-user? But my application in supposed to support multi-user. What could be done in this case?
Depending on your authentication scheme, you need to get the the User name.
for thick client applications,
Environment.Username
and
System.Security.Principal.WindowsIdentity.GetCurrent()
are a couple of options.
typically for audit tables, there is a column called 'ModifiedByUser' where you can log the user name provided by the win form app.
create the nvarchar and datetime columns (if not already) in your audit table.
one will stored the user name and the other the datetime of the audit action.
in your code, whenever you want to add an entry to the audit table, get Environment.Username or System.Security.Principal.WindowsIdentity.GetCurrent(), along with DateTime.UtcNow and pass it on to be saved to the DB into the Audit table.
SQL Server knows who you are. You can simply use SUSER_SNAME() or/and ORIGINAL_LOGIN() function as a default value for the username column in your audit table. Same for the time of audit event, use GetDate() function. There is no need to send this information from the client.
This is a very open-ended question but I think I understand what you are trying to do. You have application-specfic users that are defined in a Users table (as opposed to using database users or active directory users) and you need to log specific information for auditing purposes or drive security based off of the logins. Is that correct?
This can be done, but the logic for it will need to be written in your application.
Let’s pretend we are writing a program to send out an invoice to a customer.
I used role based security where you can give users access to do specific tasks by granting them a role. For example, “Create New Invoice” could be a role. I usually have 2 tables for this:
SecuirtyRoleDefintion
SecurityRoleUsers
The fist table, Security Role Definition will have an ID column, the Description (“Create New Invoice”), and I usually have a Audit column to indicate if this action needs to be logged for Audit.
The second table, SecurityRoleUsers, is where I define if a user has permission to execute that role. Columns are usually something like this: a unique ID, User ID (foreign key to the Users table), RoleID (foreign key to SecurityRoleDefintion)
Now in your application we need a class to check if a user has a role. It needs to take in the role ID (or name) and the user ID. Example: public bool IsUserAuthorized(int RoleID, int UserID)
This method can run a query on your SecurityRoleUsers table to see if the user is in the table for that role. If so, it returns true. If not, it returns false.
Now back in the application when user click the “Create New Invoice” button it runs the IsUserAuthorized() method to check if a user can perform the operation.
If creating an audit log is necessary, you could do something similar. After the security check is done for “Create New Invoice” you can check to see if the Role needs to be audit logged, if so then write to an Audit table.
DECLARE #username varchar(128)
SET #username = CONVERT(VarChar(128), CONTEXT_INFO());
PRINT #username
DECLARE #ID_User int
SET #ID_User = ( SELECT Users.ID_User
FROM Users
WHERE Users.Username=#username )
PRINT #ID_User
This is how I solved it. I inserted this piece of code in each update trigger.
Related
I want to register and track user activities in the program. What is your suggestion? Through the database or on the program side?
On the database side, you can create triggers for changes to each table, but how can you use the username as a data retrieval record?
The program is being written with WpF and the database with SQL 2014
For better understanding, consider following scenario.
I have a table named "product" and a table named "companies". From the business logic we want to assign products to companies, which we ended up in a table "company_product". Now when a user insert new product and simultaneously assign it's companies, 2 table will be affected (the same goes for delete and update): "product" and "company_product" and we want to know:
what's inserted?
what's deleted?
what's updated to what?
And who has made these changes?
I always set up a "TransactionLog" table that has columns like:
TransactionId
UserId
Context
Details
TransactionDatetime
The combination of the User ID, Context and Details fields will allow you to capture all the details about what was done by what user. Keep Context shorter, something like a varchar(50), that is used for grouping but you can make Details varchar(max) if needed.
You can also add an additional context field if needed. Something like "Action" can be used to capture "update", "delete" or "create". For this, I would use "Context" to capture the name of the object that was being saved.
I Have a User table, where it has userFirstname, emailid, organisation as columns. I have to filter the users based on the user that have logged into the system. I have the Email ID of the user who logs into the system. I need a sql statement so that the users are filtered based on the organisation which is same as the logged in user.
something like this,
(get the logged on users organisation, then find all users in that organisation):
SELECT *
FROM USER
WHERE organisation IN (SELECT organisation
FROM USER
WHERE emailid = loggedon_emailid)
I don't think it's a big deal here, because this sounds like a homework question. But, in an application, you will most likely need to frequently reference the data associated with a given emailid. Thus, you would probably want to run
SELECT * FROM User WHERE emailid = loggedon_emailid
at the beginning of your codepath and save that data in memory. Then, to execute the query you need, you could simply run:
SELECT * FROM User WHERE organisation = $loggedon_organisation
Then, when you need to print the user's name or organisation on the screen, you will not need an additional SQL query. And, if you have other tables, you could run more queries without repeatedly looking up the user's row in the User table. For example (this is purely hypothetical):
SELECT organisation_address FROM Organisation WHERE organisation = $loggedon_organisation
How would you design a database to manage multi organisation?
(ie 1 user can own/manage more than 1 organisation)
Example
Xero (www.xero.com), you can login to xero then select the company on the list that you want to manage. I think freshbook has something simular.
USER_ACCESS
Id
CompanyUserId (UserId of company)
UserId (UserId that will manages company)
USER_PERMISSION
Id
UserAccessId
CanViewM
CanEditM
....
CanViewN
CanEditN
You should not mix users and logins. They should be kept treated as seperate tables/objects. As ones role in one company might not be the same role as in the other company.
Also do not create a permission table with one column for each possible permission. Instead you should create one row per allowed permission. (and if needed one table defining all permissions)
Hence you should have tables like:
USER_ACCOUNT (used to define logins)
Id
UserName
Password
USER
Id
AccountId (account used for login)
CompanyId (company that the user belongs to)
PERMISSIONS
Id
Name
USER_ALLOWED_PERMISSIONS
UserId
PermissionId
When logging in, simply check the USER table if more than one row is returned for the account and display a select user form if needed.
I am new to SQL and been given a task. Following are the details:
What I have:
A C# desktop application for User login and view user status (only two options: user logs in and check status of all users that are dummy created)
A table named USER containing
id
username
datecreated
A table named LOGINSTAT containing
id
username
Logtime
logDate
What I have to implement
I have to save time and date when ever user logs in in LOGINSTAT table using SQL.
My question
My question is how can I implement that. I can do the coding part but I am interested in getting some good advice to implement it. I think of it as a formal way as I know to do it:
when user logs in insert values into the login table giving all the required values.
BUT
I think that might be a bit odd. Some of my friends said you may be able to implement it by use of foreign key and primary keys, but the problem lies that the user may log in many time in a day. How to keep track of login time and date in that case?
You don't need username in your LOGINSTAT table.
You'll probably want the LOGINSTAT to include:
id
u_id
loginDateTime
id is the unique ID of every login
u_id is a foreign key from the id in users that matches your log event to a user
loginDateTime is a datetime that will give you both your log date and log time in one column
What is unique in LOGINSTAT? Not user by itself, but ID+LogDate+LogTime should be. That would be your primary key.
The only foreign key is in LOGINSTAT: ID, which references the ID in the USER table.
Values in a PRIMARY KEY column (eg. USER.id) must be unique from one another.
Values in a FOREIGN KEY column in another table referencing that primary key (eg. LOGINSTAT.id referencing USER.id) do not need to be unique - you can have multiple records in a table have the same foreign key column reference the same primary key.
Suppose you have the following... An ASP.NET web application that calls a stored procedure to delete a record. The table has a trigger on it that will insert an audit entry each time a record is deleted. I want to be able to record in the audit entry the username of who deleted the record. What would be the best way to go about achieving this? I know I could remove the trigger and have the delete stored procedure insert the audit entry prior to deleting but are there any other recommeded alternative?
If a username was passed as a parameter to the delete stored procedure, is there anyway to get this value in the trigger that's excuted when the record is deleted? I'm just throwing this out there...
The trigger context has access to the current user, as seen by the SQL Server (ie. the user used by the ASP to connect to SQL):USER_NAME().
If you don't impersonate the Web user when calling SQL, then the usual trick is to set the current user in the session context, from where it can be retrieved by the trigger code, see Using Session Context Information.
The advantage of using the trigger is that you can be sure it will be universal - any modification to the table will be audited. In terms of how to communicate that to the trigger, you can use a field on the table, say a "LastModifiedBy." The trigger itself could clear that field when executed so that if an update happens without setting the field the audit table would pick up that a user was not supplied.
A trigger always captures audit information: you may have several update paths onto the table and the trigger will deal with them all
Use SET CONTEXT_INFO to pass in the user name to the trigger and CONTEXT_INFO() to read it
Related questions:
Pass a variable into a trigger
What’s the best way to audit log DELETEs?
Getting the Windows login name from server
Have your application pass down the username of the deleter.
In your deleted sproc:
UPDATE Customer
SET DeletedBy = #DeletedBy
WHERE ID = #ID
In your trigger, use that same value:
INSERT INTO MyDeletedLog (ID, DeletedBy)