6

The first answer to this question: What to do when bit mask (flags) enum gets too large is precisely what I'm trying to do, only I don't know how to store it in the database using LINQ to SQL and tie it to users without creating 2 tables for each thing/logical group.

This is best explained via code (these are LinqPad friendly although incomplete):

// Requirements:
// 1) Using LINQ to SQL
// 2) Track User's CRUD rights in different parts of an application (parts referred to below as 'Things')
// For example: ThingA.Create, ThingB.Read, ThingB.Update, ThingC.Delete
// Desired usage: if (User.IsAllowed(ThingA.Create)) { // allowed } else { // not allowed }
// 3) Allow for more than 64 'Things'
// 4) Do not want to refer to permissions using strings like User.IsAllowed("Create ThingA");

//
// Scenario A: This works, but you would be limited to adding only 60 'Things'
// Example usage:
// User Bob = new User();
// var desiredPermissions = Permissions.Create | Permissions.ThingA; // Permission = Unique binary value combination of flags
// if ((Bob.Permissions & desiredPermissions) == desiredPermissions) { // Bob has permission to Create ThingA
[Flags]
public enum Permissions : ulong
{
    Create = 1 << 0,
    Read = 1 << 1,
    Update = 1 << 2,
    Delete = 1 << 3,

    // Limited to 60 'Things'
    ThingA = 1 << 4,
    ThingB = 1 << 5 
}

// User Model
[Table(Name="Users")]
public class User
{
    [Column(IsPrimaryKey = true)]
    public string FName { get; set; }

    [Column]
    public Permissions Permissions { get; set; }

    public User()
    {
    }
}

ScenarioB:

//
// Scenario B: This would work too, but each new 'Thing' would need its own flag enum list stored in its own table (ThingXPermissions),
// with another table linking ThingXPermissions.X to Users.ID (UserThingXPermissions) (yuck!)
// Would like to avoid having to change database structure when adding more 'Things' in future.

// User Model
[Table(Name="Users")]
public class User
{
    [Column(IsPrimaryKey = true, IsDbGenerated = true)]
    public int ID { get; set; }

    private EntitySet<ThingAPermissions> userThingAPermissions = new EntitySet<ThingAPermissions>();
    [Association(Name = "FK_User_UserThingAPermissions", Storage = "userThingAPermissions", OtherKey = "FK_User_Id", ThisKey = "ID")]
    public IEnumerable<ThingAPermissions> UserThingAPermissions
    { get { return userThingAPermissions; } }

    public IEnumerable<ThingAPermissions> ThingAPermissions
    { get { return (from up in UserThingAPermissions select up.UserThingAPermissions).AsEnumerable(); } }

    public User()
    {
    }
}

[Table(Name="UserThingAPermissions")]
public class UserThingAPermissions
{
    [Column(IsPrimaryKey = true)]
    public int FK_User_Id;

    private EntityRef<User> user;
    [Association(IsForeignKey = true, ThisKey = "FK_User_Id")]
    public User User
    {
        get { return user.Entity; }
        set { user.Entity = value; }
    }

    [Column]
    public ThingAPermissions ThingAPermissions { get; set; }
}

// ThingAPermissions
[Flags]
public enum ThingAPermissions : ulong
{
    Create = 1 << 0,
    Read = 1 << 1,
    Update = 1 << 2,
    Delete = 1 << 3
}

Desired Scenario:

//
// Desired Scenario: Psuedo code of what I'd like to be able to do:
// Single Permissions (CRUD) list
// Single||simple Things list
// Single||simple table associating UserX, ThingX, PermissionX
// Example usage:
// User Bob = new User();
// var desiredPermissions = Permissions.Create | Things.ThingZ; // Permission = Unique binary value combination of flags
// if ((Bob.Permissions & desiredPermissions) == desiredPermissions) { // Bob has permission to Create ThingZ
// Missing link: Combining enums and storing into database linked to user
// e.g.
// [Table = "UserPermissions"]
// (User, Thing, Permission)
// 1, ThingZ, Create
// 1, ThingZ, Delete
// 1, ThingX, Read
// 2, ThingZ, Read
// 2, ThingX, Delete
[Flags]
public enum Permissions : ulong
{
    Create = 1 << 0,
    Read = 1 << 1,
    Update = 1 << 2,
    Delete = 1 << 3
}

[Flags]
public enum Things : ulong
{
    ThingZ = 1 << 0,
    ThingY = 1 << 1,
    ThingX = 1 << 2,
    ThingW = 1 << 3
}

[Table(Name="UserPermissions")]
public class UserPermission
{
    [Column(IsPrimaryKey = true)]
    public int FK_User_Id;

    private EntityRef<User> user;
    [Association(IsForeignKey = true, ThisKey = "FK_User_Id")]
    public User User
    {
        get { return user.Entity; }
        set { user.Entity = value; }
    }

    [Column]
    public int FK_Thing_Thing { get; set; }

    private EntityRef<Things> thing;
    [Association(IsForeignKey = true, ThisKey = "FK_User_Id")]
    public User User
    {
        get { return user.Entity; }
        set { user.Entity = value; }
    }

    [Column]
    public Permissions Permission { get; set; }
}

Additional code attempts:

WPF permission-based authorization using Enum Flag Bit

Community
  • 1
  • 1
Brock Hensley
  • 3,617
  • 2
  • 29
  • 47
  • I don't think you would need two tables for each logical group. If you used bitmasking for your enums and had create/delete/update in one and thing1/thing2/thing3 in another then you could just iterate over the enum and store a row in the DB for each logical pair for the user, you would also only store the logical pairs which apply to the user, so like four rows max. One column for Create/Update/Delete/Read (each in their own row) and a second column which was a bitmask of thing1/thing2/thing3. I don't think four columns is too much extra... - I could be missing something though... :) – Faraday Sep 06 '13 at 22:37

1 Answers1

3

Not sure you fully understand how flags work. The reason you're using the left shift operator is so your enums will be exponents of the following value.

The flag attribute is to let .NET know that the value of your enum could be multiples.

For instance if you take your ThingA and ThingB and add them (48)

Console.Write((Permissions)48));

The value returned is BOTH ThingA and ThingB. You would just be able to save it as a single ulong in the database, your code will be able to figure out the rest.

Eric Bishard
  • 5,201
  • 7
  • 51
  • 75