Has anyone dealt with column-level security "policies" or how to best manage/implement them as they don't exist yet?
In my mind we have great tools for database administrator users to have column level security with grants, but not application users in a manner akin to RLS.
My current solution is to leverage a trigger with a whenClause that checks the permissions. Imagine creating a publishing flow with authors and publishers on the same object
CREATE TABLE posts (
id serial primary key,
title text,
content text,
published boolean DEFAULT FALSE,
author_id uuid NOT NULL DEFAULT get_curent_user_id(),
publisher_id uuid NOT NULL DEFAULT '85d770e6-7c18-4e98-bbd5-160b512e6c23'
);
CREATE TRIGGER ensure_only_publisher_can_publish
AFTER UPDATE ON posts
FOR EACH ROW
WHEN (
NEW.publisher_id <> get_curent_user_id ()
AND
OLD.published IS DISTINCT FROM NEW.published
)
EXECUTE PROCEDURE throw_error ('OWNED_COLUMNS', 'published');
CREATE TRIGGER ensure_only_publisher_can_publish_insert
AFTER INSERT ON posts
FOR EACH ROW
WHEN (
NEW.publisher_id <> get_curent_user_id ()
AND
NEW.published IS TRUE
)
EXECUTE PROCEDURE throw_error ('OWNED_COLUMNS', 'published');
If you want to run the example I've included a gist here that wraps all deps in a tx: https://gist.github.com/pyramation/2a7b836ab47a2450b951a256dfe7cbde
It works! The author can create posts, and only the publisher can "publish" them. However it has some disadvantages.
uses triggers, cannot use BYPASSRLS and have to use replication role
Behavior for INSERT to my knowledge requires an understanding of valid or default values
#1 I could manage, I can imagine using the replication role if needed in some places. #2 however, feels clunky and closely coupled to the data model given it requires default or whitelisted values.
Thoughts? Any other solutions out there I should be aware of?
Related
I'm developing a call ticketing system for a hospital, where multiple users from different branches do CRUD activity on one table. This table has relationship restricted value column called, let say [BranchId]. There are three different type of users,
A. Ticket Manager (where they can CRUD without restriction),
B. Ticket Issuer (where they can issue/view/delete tickets but they can't resolve)
C. Ticket Resolver (where they can view/resolve/delete tickets but they can't issue).
So for example,
A patient, who has an appointment with [BranchId] 1, calls to the hospital, and wants to cancel their appointment,
A Ticket Issuer answers the phone and adds this event onto the ticket table and assigns it to [BranchId]=1.
Then the colleagues who work on [BranchId]=1 (they can only see dataset with [BranchId]=1) views the data and resolves (sets the [IsResolved] column to 1)
The patient gets a phone call to notify that their appointment was cancelled.
Solutions so far I can think of
Table Triggers:-
The Ticket Manager adds all users into a table, let say called [BranchUsers] and this table as a column called [Username] where this is populated with Domain\Username, [IsIssuer], [IsResolver] and [BranchId]. The ticket table now has a CRUD trigger when it checks if SYSTEM_USER can issue/resolve and their branch. A separate View table would be used to select from the ticket table with INNER JOIN on [BranchId] for the SYSTEM_USER. However, the problem with this approach is that users can select data directly from the ticket table and so be able to read everything in the table and the [BranchUsers] table may be hard to maintain but that is ok.
Using Active Directory Group:-
So in the Windows Server AD Group, create a group called, let say [Branch1Users], [Branch2Users] etc..., and create multiple ticket tables... you see now it got complex as if one table need some changing such as renaming a column, then the .NET Core API references need too and going back to the Trigger approach above seems easier.
So what would you think be best, please advice anything even if you think it might not help as it may help create a light bulb ideas, I will start the trigger approach for now as this project has deadline soon :-)
Specs that might help:-
Microsoft SQL Server 2019
Windows Server 2019
I'm creating an Account table in my project's database. Each account has A LOT of properties:
login
email
password
birthday
country
avatarUrl
city
etc.
Most of them are nullable. My question is, how should I design this in database?
Should it be one table with all those properties? Or maybe should I create two tables, like AccountSet, and AccountInfoSet, where I would store all those 'advanced' user's settings? And last, but not least: if this should be two tables, what kind of relation should be between those tables?
If this is a relational database, then I definitely would not store those properties as fields in the Account table. Some reasons why:
Once your application goes to production (or maybe it's already there), the schema maintenance will become a nightmare. You will absolutely add more properties and having to constantly touch that table in production will be painful.
You will most likely end up with orphaned fields. I've seen this many times where you'll introduce a property and then stop using it, but it's baked into your schema and you might be too scared to remove it.
Ideally you want to avoid having such sparse data in a table (lots of fields with lots of nulls).
My suggestion would be to do what you're already thinking about and that's to introduce a property table for Accounts. You called it AccountInfoSet.
The table should look like this:
AccountId int,
Property nvarchar(50),
Value nvarchar(50)
(Of course you'll set the data types and sizes as you see fit.)
Then you'll join to the AccountInfoSet table and maybe pivot on the "advanced" properties - turn the rows into columns with a query.
In .NET you can also write a stored procedure that returns two queries with one call and look at the tables in the DataSet object.
Or you could just make two separate calls. One for Account and one for the properties.
Lots of ways to get the information out, but make sure you don't just add fields to Account if you're using a relational database.
Is it better to create tables that store a lot of data that are related to an entity (User for example) or many tables to store said data?
For example:
User Table
Name
Email
Subscription Id
Email Notifications
Permissions
Or
User Table
Name
Email
Subscription Table
User ID
Subscription ID
Notification Table
User ID
Receives?
... etc
Please consider code in this as well, or I would have posted to ServerVault.
From a relational design standpoint what is important is the normal form you're aiming for. In general, if the "column" would require multiple values (subscription_id1, subscription_id2, etc) then it is a repeating group, and that would indicate to you that it needs to be moved to a related table. You've provided very general table and column notes, but taking a cue from the fact that you named "Email Notifications" and "Permissions" with plurals, I'm going to assume that those require related tables.
I was working with one product where almost every table had those columns. As developers we constantly had to join to Users table to get Id of who created record and it's just a mess in a code.
I'm designing new product and thinking about this again. Does it have to be like this? Obviously, it is good to know who created record and when. But having 300+ tables reference same User table doesn't seem to be very good..
How do you handle things like this? Should I create CreatedBy column only on major entities where it's most likely needed on UI and than deal with joining? Or should I go and put it everywhere? Or maybe have another "Audit" table where I store all this and look it up only on demand(not every time entity displayed on UI)
I'm just worrying about performance aspect where every UI query will hit User table..
EDIT: This is going to be SQL Server 2008 R2 database
The problem with that approach is that you only know who created the row and who changed the row last. What if the last person to update the row was correcting the previous updater's mistake?
If you're interested in doing full auditing for compliance or accountability reasons, you should probably look into SQL Server Audit. You can dictate which tables you're auditing, can change those on the fly without having to mess with your schema, and you can write queries against this data specifically instead of mixing the auditing logic with your normal application query logic (never mind widening every row of the table itself). This will also allow you to audit SELECT queries, which other potential solutions (triggers, CDC, Change Tracking - all of which are either more work or not complete for true auditing purposes) won't let you do that.
I know that this is an older post, but one way to avoid the lookup on the user table is to de-normalize the audit fields.
So instead of a userid in the CreatedBy field you insert a username itself. This will allow for a review of the table without the user look and also allow for any changes in your user table not reflect in the audit fields. Such as deleted users.
I usually add the following to the end of a table
IsDeleted bit default 0
CreatedBy varchar(20)
CreatedOn datetime2 default getdate()
UpdatedBy varchar(20)
UpdatedOn datetime2 default getdate()
I'm working on a multi-user internet database-driven website with SQL Server 2008 / LinqToSQL / custom-made repositories as the DAL. I have run across a normalization problem which can lead to an inconsistent database state if exploited correctly and I am wondering how to deal with the problem.
The problem: Several different companies have access to my website. They should be able to track their Projects and Clients at my website. Some (but not all) of the projects should be assignable to clients.
This results in the following database schema:
**Companies:**
ID
CompanyName
**Clients:**
ID
CompanyID (not nullable)
FirstName
LastName
**Projects:**
ID
CompanyID (not nullable)
ClientID (nullable)
ProjectName
This leads to the following relationships:
Companies-Clients (1:n)
Companies-Projects (1:n)
Clients-Projects(1:n)
Now, if a user is malicious, he might for example insert a Project with his own CompanyID, but with a ClientID belonging to another user, leaving the database in an inconsistent state.
The problem occurs in a similar fashion all over my database schema, so I'd like to solve this in a generic way if any possible. I had the following two ideas:
Check for database writes that might lead to inconsistencies in the DAL. This would be generic, but requires some additional database queries before an update and create queries are performed, so it will result in less performance.
Create an additional table for the clients-Projects relationship and make sure the relationships created this way are consistent. This also requires some additional select queries, but far less than in the first case. On the other hand it is not generic, so it is easier to miss something in the long run, especially when adding more tables / dependencies to the database.
What would you do? Is there any better solution I missed?
Edit: You might wonder why the Projects table has a CompanyID. This is because I want users to be able to add projects with and without clients. I need to keep track of which company (and therefore which website user) a clientless project belongs to, which is why a project needs a CompanyID.
I'd go with with the latter, having one or more tables that define the allowable relationships between entities.
Note, there's no circularity in the references you have, so the title is misleading.
What you have is the possibility of conflicting data, that's different.
Why do you have "CompanyID" in the project table? The ID of the company involved is implicitly given by the client you link to. You don't need it.
Remove that column and you've removed your problem.
Additionally, what is the purpose of the "name" column in the client table? Can you have a client with one name, differing from the name of the company?
Or is "client" the person at that company?
Edit: Ok with the clarification about projects without companies, I would separate out the references, but you're not going to get rid of the problem you're describing without constraints that prevent multiple references being made.
A simple constraint for your existing tables would be that not both the CompanyID and ClientID fields of the project row could be non-null at the same time.
If you want to use the table like this and avoid the all the new queries just put triggers on the table and when user tries to insert row with wrong data the trigger with stop him.
Best Regards,
Iordan
My first thought would be to create a special client record for each company with name "No client". Then eliminate the CompanyId from the Project table, and if a project has no client, use the "No client" record rather than a "normal" client record. If processing of such no-client's is special, add a flag to the no-client record to explicitly identify it. (I'd hate to rely on the name being "No Client" or something like that -- too fuzzy.)
Then there would be no way to store inconsistent data so the problem would go away.
In the end I implemented a completely generic solution which solves my problem without much runtime overhead and without requiring any changes to the database. I'll describe it here in case someone else has the same problem.
First off, the approach only works because the only table that other tables are referencing through multiple paths is the Companies table. Since this is the case in my database, I only have to check whether all n:1 referenced entities of each entity that is to be created / updated / deleted are referencing the same company (or no company at all).
I am enforcing this by deriving all of my Linq entities from one of the following types:
SingleReferenceEntityBase - The norm. Only checks (via reflection) if there really is only one reference (no matter if transitive or intransitive) to the Companies table. If this is the case, the references to the companies table cannot become inconsistent.
MultiReferenceEntityBase - For special cases such as the Projects table above. Asks all directly referenced entities what company ID they are referencing. Raises an exception if there is an inconsistency. This costs me a few select queries per CRUD operation, but since MultiReferenceEntities are much rarer than SingleReferenceEntities, this is negligible.
Both of these types implement a "CheckReferences" and I am calling it whenever the linq entity is written to the database by partially implementing the OnValidate(System.Data.Linq.ChangeAction action) method which is automatically generated for all Linq entities.