I'm working on an application that has to manage permissions, and I've come up with a design I wanted some feedback on. I'm not sure if this is exactly the right place to post it but I guess we'll see.
A user in my application can be assigned permissions. They can have permissions on an individual level, or from being part of a group. Thus I have the following tables (I think the names are relatively self-explanatory but can add further information if needed):
Users
Permissions
User_Permissions
Groups
User_Groups
User_Group_Permissions
The design of the permissions table is this:
id int (permission id, primary key)
name varchar(255) (name of the permission, such as "ADMINISTRATION")
description varchar(1000) (A short description of the permission)
default_value tinyint (The default value to assign to users (Yes/No/Never))
However, when I check the permission in code, I didn't want to use a "Magic String" so I created an enum for the permissions in my permissions table, and then use it like so:
public enum EPermission
{
ADMINISTRATION = 1,
LOGIN = 2
}
public bool HasPermission(EPermission permission)
{
int permission_id = (int)permission;
//look up the permission in the database based on permission_id
}
The ids in my permissions table match the number values I've assigned in my enums. The application has no way of changing these. The permissions in the permissions table are inserted with those ids as part of the installation and should not be changed.
Is this kind of coupling acceptable? I'm not expecting the data to change, but it's always possible that it could, and I'm just wondering if there might be a better way to do things before I get too deep into this.
Yes it is "acceptable" and a fairly common practice.
EDIT: If you wanted to really minimize the chance that your DB and the enum will become out of sync, you can reflect the enum to seed your table with data during deployment.
As you do not change the Permissions you can also get away from the permissions table and only use the enum. You can then store the value of the enum as an int in the User_Permissions and User_Group_Permissions tables.
When retrieving the data from the database you can deserialize the value to the enum. This reduces the possibility that the data in your database gets corrupted and does not match with your enum anymore.
I suspect that it will not help long run. what If a single user can have one or more permissions. Then you need to switch flag enum?
Related
In Azure we have four Shards and i want to remove two of them as we do not need them anymore. The Data should be merged into the other two Shards.
I use a Listmap with GUIDs as Key to identifiy the Shard (in our application this is the UserId).
In the tutorials i only found samples to merge Shards with the Range type.
Is there a way to merge these type of shards in a faster way or do i have to write my own tool for this?
If the merge is performed automatically what will for example happen in the following case:
The GUID to identify the Shard is the UserId, now this data is moved from Shard A to Shard B. There is another Table called Comments which has the UserId as ForeignKey. The PrimaryKey in this Table is a classic numeric auto increment value. What will happen to those values if they are moved from Shard A to Shard B? Will they be inserted and a new ID is assigned to them or will this not work at all?
Also there is some local FileStorage invloved which uses IDs in the Path so i will have to write my own tool anyway i think.
For that I took a look at the ShardMapManager but did not fully understand how it works. In the ShardMappingsGlobal Table is a Column called MappingId. But this is not the Guid/UserId which is stored in the Shard Database. How do i get the actual Guid which is used to identify the shard, in my case the UserId?
I also did not find Methods to move data between Shards.
What i would do now is Transfer the Data between the Shards with a tool by myself and then use the ListShardMap.UpdateMapping Method to set a new Shard for the value.
At the end of the operation i would use ListShardMap.DeleteShard or is there a better way to do this?
EDIT:
I wrote my own tool to merge the shards but i get a strange exception now. here some code:
Guid userKey = Guid.Parse(userId);
ListShardMap<Guid> map = GetUserShardMap<Guid>();
try
{
PointMapping<Guid> currentMapping = map.GetMappingForKey(userKey);
PointMapping<Guid> mappingOffline = map.UpdateMapping(currentMapping, new PointMappingUpdate()
{
Status = MappingStatus.Offline
});
}
The UpdateMapping causes the following exception:
Store Error: Error 515, Level 16, State 2, Procedure __ShardManagement.spBulkOperationShardMappingsLocal, Line 98, Message: Cannot insert the value NULL into column 'LockOwnerId', table __ShardManagement.ShardMappingsLocal
I do not understand why there is even an insert? I checked for the mappingId in the local and global Shardmapping tables and the mapping is there so no insert should be required in my opinion. I also took a look at the Code of the mentioned stored procedure spBulkOperationShardMappingsLocal here: https://github.com/Azure/elastic-db-tools/blob/master/Src/ElasticScale.Client/ShardManagement/Scripts/UpgradeShardMapManagerLocalFrom1.1To1.2.sql
In the Insert statement the LockOwnerId is not passed as parameter so it can only fail.
Currently i work with a testsetup because i do not want to play on the productive system of course. Maybe i made a mistake there but to me everything looks good. i would be very grateful about any hint regarding this error.
In the tutorials i only found samples to merge Shards with the Range type. Is there a way to merge these type of shards in a faster way or do i have to write my own tool for this?
Yes, the Split-Merge tool can move data from both range and list shard maps. For a list shard map you can issue shardlet move requests for each key. The Split-Merge tool unfortunately has some complicated set up, last time it took me around an hour to configure. I know this is not great, I'll leave it up to you to determine whether it would take more or less time to write your own custom version.
There is another Table called Comments which has the UserId as ForeignKey. The PrimaryKey in this Table is a classic numeric auto increment value. What will happen to those values if they are moved from Shard A to Shard B? Will they be inserted and a new ID is assigned to them or will this not work at all?
The values of autoincrement columns are not copied over, they will be regenerated at the destination. So new ids will be assigned to these rows.
For that I took a look at the ShardMapManager but did not fully understand how it works. In the ShardMappingsGlobal Table is a Column called MappingId. But this is not the Guid/UserId which is stored in the Shard Database. How do i get the actual Guid which is used to identify the shard, in my case the UserId?
I would strongly suggest not trying to edit the ShardMapManager tables on your own, it's very easy to mess up. Editing ShardMapManager tables is precisely what the Elastic Database Tools library is designed to do.
You can update the metadata for a mapping by using the ListShardMap.UpdatePointMapping method. Just to be clear, this only updates the ShardMapManager tables' knowledge of where the data should be for the key. Actually moving the mapping must be done by a higher layer.
This is a high-level summary of what the Split-Merge service does:
Lock the mapping to prevent concurrent update from another shard map management operation
Mark the mapping offline with ListShardMap.UpdatePointMapping. This prevents data-directed routing with OpenConnectionForKey from being allowed to access data with that key. It also kills all current sessions on the shard to force them to reconnect, this ensure that there are no active connections operating on data with the now-offline key
Move the underlying data, using the Shard Map's SchemaInfo to determine which tables need to be moved
Update the mapping and mark it online with ListShardMap.UpdatePointMapping
Unlock the mapping
In an MVC app, administrator has a CRUD controller for managing users. Now, the functionality of the edit part needs to be extended and it involves adding a number role dependent tabs. They depend on the role of the viewed user, rather than on roles of the administrator who is viewing them. The easiest way for achieving this, would be getting all roles of that user as a array of strings (or similar), but how do I actually go about obtain those.
Is there a preferred method of getting all roles of a single user in SimpleMembership (based on his UserId) or do I just have to patch up a stored function in the database and pull those through it?
Writing the function is not a big deal, but this problem doesn't sound like something I should have to make workarounds for.
Use the Roles.GetRolesForUser() method https://msdn.microsoft.com/en-us/library/8h930x07(v=vs.110).aspx
string[] rolesArray = Roles.GetRolesForUser("username");
With the string being the User Name of the user as contained in the aspnetdb.
If you want to find by using a guid, you could try the following:
Guid userId; // ID of user - you can populate this somehow
MembershipUser memUser = Membership.GetUser(userId);
string[] roles = Roles.GetRolesForUser(memUser.UserName);
Here is the stored procedure I mentioned in the question:
CREATE FUNCTION GetUserRoles
(
#UserId int
)
RETURNS TABLE
AS
RETURN
(
SELECT
r.RoleName
FROM
dbo.webpages_UsersInRoles uir
JOIN dbo.webpages_Roles r ON (r.RoleId = uir.RoleId)
WHERE
uir.UserId = #UserId
)
GO
The only reason to go with this instead than the answer by user1666620, would be if you wanted to skip one unnecessary query to the DB. The preferred method to use this solution would be to add this function to your dbml (or it's EF equivalent). Obviously this first needs to be added in the database.
My question is :
I have a domain table in database, where I add some rules for users.
Something like this:
Table UserRoles:
idRule cRuleDescription bRuleIsActive
1 Edit client true
2 Delete client false
My C# application refers to this table using enumeration, like this :
public enum UserRoles: int
{
CanEditClient = 1,
CanDeleteClient = 2,
CanChangeClientName = 3,
//More code
}
And I use a C# method that query the database, by its Id, to know if the logged user has permission for some action. Something like this:
bool HasPermissionToEdit = Rules.UserHasPermisson(UserName, UserRoles.CanEditClient)
if(HasPermissionToEdit)
{
//Do the work!
}
But using this gives me some trouble. Someone created new rows in a production eviroment on database. And the ids are not correct anymore. And I'll need to change the enumeration on my C# code. I need to use this table because, administration users can manage the rules for other group of users.
How do you work with it? There is a better way to reference a registry on a table?
If you want to implement this properly, make the key contain the name of the role. That way, there cannot be accidents with the ID's where someone thinks the key is meaningless. Naming it idRule does not help.
Second, add a check constraint to enforce correct usage of role names.
Third, add a unique key constraint on the name of the role to enforce that each role only occurs once.
You can use string enums to convert from the role names to integers if you want to. See C# String enums for a nice design pattern.
Example table:
ID - NAME - DESCRIPTION - IS_ACTIVE
1 - CanEditClient - client - true
2 - CanDeleteClient - client - false
3 - CanChangeClientName - client - true
Then make sure to add a unique constraint to NAME and a check constraint as well.
Now, no matter how someone shuffles the ID's, your code will continue to work.
Using the rule ID to map to an enum is not crazy in itself as long as the rule ID is not automatically generated (i.e. not an identiy column).
If i have a field in my db which clarify the type of the application .
takes two values 0 or 1
0 for web app and 1 for win app
and now there is some requirement in my business:
There are some win applications available to all users and some of
them belong to specific users .
What 's the best solution to handle this case .
adding new field to state whether it's public or private
or just adding new value to the same field say 2 to state it's private win app
If you haven't already it would probably be best to slip in a user, role, permission based security model to the database/system, thereby giving you the ability to specify a group of users that have access to a particular application, whether it be web or windows based
I'd say add a new column next to your AppId called PublicIndicatior
Oh and be sure to have a lookup table so people can see what 0 or 1 means, and foreign key it to your data table
Lookup Table:
AppTypeId, AppTypeDescription
0, WebApp
1, WinApp
Data Table:
Id, AppTypeId, PublicIndicator
1,0,1
etc
As Pope suggested above (I +1 him), the best scenario is to add in a new user table (or tables for roles etc if possible) and then link to that through either a new foreign key, or using the appid (assuming it is on your table and unique). Then when the boss comes back 3 weeks later and say, "that's great, but now can we restrict App99 to just the Accounts Dept" you are not going back to the drawing board.
This question is inspired by this SO question regarding Access Control in ASP.NET MVC. Here I'm trying to bring the accepted answer into a tangible solution.
The answer mentions using FileSystemSecurity as an inspiration to managing permissions. Here I'm also using an enum with Flags attribute to define the ACL for all my objects. Additionally each member of my objects will be stored in a column within SQL. Assume a simplified Linq2SQL, EF, or nHibernate ORM mapping.
Edit: Added the following benefit / rationale for this approach
This security model was inspired by the FileSystemRights, the .NET approach to managing file level permissions.
One of the main reasons I like this approach is so I can easily create a summary of all the permissions by OR'ing all the individual ACLs together. I also like that I can add a DENY ACL to remove an inherited permission.
List<myObject> PermissionsList = GetACLForObject(ItemID, UserID);
foreach (var acl in PermissionsList)
{
// The following enum has the [FlagsAttribute] applied so the .ToString() is pretty
PermissionWithFlagsEnum sampleForSO = (PermissionWithFlagsEnum )acl.Permission;
Console.Writeline ("Setting " + sampleForSO.ToString() + " permission for group: " + acl.ACLName);
ResultingPermission = resultPermission | acl.Permission ;
}
public int ResultingPermission {get;set;}
/End Edit
Then it occurred to me that I could compare less privileged users by numeric value of the enum from more privileged users's enum.
I'm thinking that this definition would allow for quick and easy identification of users within the SQL database, without having to parse the enum on the backend. ( Find unprivileged users via select users from permissions where security < DBAceessRights.DetailRead )
Here is how I defined the flags (lowest value has the least permission)
[Flags]
public enum DBAccessRights
{
DenyAll =1 << 1,
WikiMode = 1 << 4,
AppearInListing = 1 << 8,
DetailRead = 1 << 12,
CreateNew = 1 << 18,
DetailEdit = 1 << 22,
DeleteChild = 1 << 26,
DeleteThis = 1 << 30,
EditPermissions = 1 << 31,
}
I have a permission table that joins a user ID to an object specific ACE. This should reduce the need for concurrent updates on a particular row.
Question
Is this a good idea?
Has someone done it before (better than this)? (Edit: It is the accepted answer here )
If this is a standard way of implementing permissions, what is it called?
I'd say no.
You're assuming you'll need no more than 32 (or 64 if you use bigint) privileges. Sounds like an arbitrary limit to me. Moving to a varbinary in the database can overcome this, but then your enum is up the creek (can't create enums on byte[])! And you won't be able to do numeric comparisons.
You're assuming that privilege 0x1 will always be logically less than 0x2 and 0x80 and so on. That's rarely the case in my experience. More usually, privileges are quite independent of each other (ie: having the "add user" privilege has nothing to do with the "upload image" privilege; one group of users (admins) will have the former while others (content publishers) have the latter. This means your numeric comparison isn't as helpful as you first thought.
What you're doing may yield a performance benefit, but you haven't demonstrated there's a performance problem yet! Most of my privilege systems work with one database record per user to grant or deny each privilege. Fetching 100 records doesn't tax my database. You'd do better simply caching each user's permissions between requests than using a bitmask.
Regarding update performance: how often do user permissions change anyway? Once a system is in place they tend to be pretty static in my experience.
I've found bitmasks most useful when trying to pack alot of data into a small space. But often my point 1 comes back to bite me when I end up with more than 64 things.
Note that I have used this technique when recording statistics of user actions (tracking what items users find in searches, what entities they view, etc). My reason was purely to make sure database record lengths were fixed and small so inserts were fast. And I wasn't doing numeric comparisons. (And, to be fair, I never tested to see if there was any difference between an int column and several bit columns).
EDIT
A basic alternative (which I'm using): an M:N relationship between Users and Privileges (I call them Rights).
(Sorry about those Micky Mouse ears on my user!)
The presence of a record in UserRight indicates the right is granted to that user. The absence indicates no right. This query gives you all the rights assigned to a user.
SELECT [dbo].[User].Username, [dbo].[Right].Id, [dbo].[Right].Name
FROM [dbo].[Right]
INNER JOIN [dbo].[UserRight] ON [dbo].[Right].Id = [dbo].[UserRight].RightId
INNER JOIN [dbo].[User] ON [dbo].[User].Id = [dbo].[UserRight].UserId
WHERE [dbo].[User].Id = #pUserId
Then, in code to assert a user has a right:
var grantedRights = RunTheAboveQuery(currentUser.Id);
if (grantedRights.Any(r => r.Id == requiredRight))
// User has the right.
else
// User does not have the right.
Obviously, you can scale this to check a user has several rights in one query.
This doesn't artificially limit how many Privileges your system supports. And it doesn't assume any relationships between Privileges (so you can't do your numeric comparisons; everything is done by IEnumerable<Right> in my system). And, if you're really keen on bitmasks, you could create a Dictionary<User, BitArray> in a cache!
I also have the concept of a Role which provides a logical group of rights for users. It's just another M:N table. That's an exercise for the reader!
In general putting multiple values (flags) into a single field is a bad idea.
If you're planning on auditing this data its a really bad idea since it because its hard to efficiently tease out which flags changed from update to update.
Problems you may encounter even if you don't do auditing
Many simple queries (what users the DeleteThis authroization) aren't
SARGable because you'll need to perform a bitwise operation before
a comparison.
Also select users from permissions where security <
DBAceessRights.DetailRead may not return the right results because
AppearInListing & CreateNew is greater than DetailRead but doesn't
have the DetailRead turned on. So the benefit you hoped for you may not get
Managing concurrency (multiple writers to ACL) is more difficult since mutating one "logical value" is actually mutating all the values.