1- When do we create the authorized users/groups bitmasks for a row? Is that done when the row is created? If so, then which user's bitmask is assumed to have the authority to fetch this row (the user that created the row or all the groups that the user "who created the row" belongs to?
When the object is first created we set the default access levels to this object. This is determined by our business logic.
For example, Accounting always has access to all customer data for reading. So when a new invoice is created by a salesperson access is granted to our Admins, Accounting, and the salesperson that created the order. I beleive it is largely determined by business logic, but every row should be given read/update access to an Admin role and the person who created it by default.
2- Do you check for the authority bitmask at the server or you have to bring the data-rows to the BL and there filter the rows the user is authorized to see.
This depends on whether we are retrieving a group or a single entity. If it is a group we use our hacked substring predicate and filter on the server by selecting the correct byte and doing a bitwise & operation to see what rows we have access to.
ex:
SELECT * FROM Customer WHERE (SUBSTRING(SecurityMask, 5, 1) & BitMask) > 0
SecurityMask is a binary 20 and BitMask is the RoleKey.
if RoleKey = 1, check 1st bit
if RoleKey = 2, check 2nd bit
etc.
If it's a single entity we retrieve first then do a check. I suppose for consistency we could do a check on the sql server even for a single entity, but then we didn't want to write extra code for the entity collection, PredicateBucket, and type casting if we already have the Primary Key for the Entity.
3- The authority bitmasks are only concerned with seeing (fetching) the row or they can also decide if a user CAN see a row but CANNOT edit or delete the row?
Our Role table has something called RoleAccessMask and each bit represents a different OPERATION the Role has access to. This is seperate from the authority bitmask that we have on each row in our tables.
RoleAccessMask
ex:
Bit 1: Customer Create Access
Bit 2: Customer Read Access
Bit 3: Customer Update Access
Bit 4: Customer Delete Access //we never really delete, only set our IsActive bit to 0.
Bit 5: Invoice Create Access
...
So when a CustomerEntity must be fetched we first do a check to see if this role even has access to read from the customer table with our RoleAccessMask, then we do a check to see if the Role even has access to entity they want to access with the row's autority mask.
We also have more complex queries where we must do several checks at a time, but that is a requirement of the business logic.
I hope that answered your questions, below is just some comments about how we went about doing it.
The downside of this, for us, is that it has added an extra db hit every time we need to do any operation because we need to retrieve the data for the role after each request for our web apps.
Also, we implemented Sessions because we have other companies accessing our data through webservices or using our BL objects. Basically, our security object has a static method that takes a username and a password and IP Address and returns a SessionId. That SessionId is valid for 2 hours from creation.
We did this so that the username and password were not passed back and forth over http and other developers using our BL objects just passed in the sessionkey to all of our accessor/modifier methods instead of having access to the entire Security object and being able to spoof a rolekey.
It really works great and occasionally you run into a snafu where someone didn't give someone the correct access, but those are solved quickly. You will take a performance hit.