views:

137

answers:

5

I'm trying to create an authorization scheme for my ASP.NET MVC application where an Enum is used to set permissions. For example:

[Flags]
enum Permissions
{
    ReadAppointments = 1,
    WriteAppointments = 2 | ReadAppointments,
    ReadPatients = 4,
    WritePatients = 8 | ReadPatients,
    ReadInvoices = 16,
    WriteInvoices = 32 | ReadInvoices
    ...
}

But I don't really like that because it really doesn't make it clear that Write always includes Read.

I then realized that a requirement would be that a user might have NO access to, for example, Appointments.

Essentially, I'd want a "bitfield" with 3 states: none, readonly, full (read/write). I'd like to still use an enum bitfield since it's easy to store in a DB (as an int). Also it's very easy to see if a permission is set.

Does anyone have any idea how this could be easily accomplished using an Enum... or am I going in the completely wrong direction?

EDIT: I'm really trying to avoid storing permission definitions in the DB since I really want things to be changeable without having to modify much on the DB end. It'd be really nice to know how a large scale application would do this.

A: 

Wrong direction. Your application will grow, then the bitfield will not be sufficient anymore and you are in for a lot of rework. Better get "proper" from the beginning.

TomTom
Ok, if it is the wrong direction, what do you reccommend as the "proper"?
TheCloudlessSky
A: 

wouldn't a value of 0 mean no permissions? ie:

0 is cannot modify appointments, patients, or invoices

1 is read appointments, but cannot modify others

2 is write appointments, but cannot modify others

3 is read/write appointments, but cannot modify others

4 is read patients, but cannot modify others.

so if you have...

51 that's:

read/write invoices and read/write appointments, but no access to patients...

jonathanasdf
A: 

Strictly speaking, you can't have a bitfield with anything other than two possible values any more than you can have a single (decimal) numeric digit with more than ten possible values. Base 2 means two values.

With that out of the way, don't store business-specific permissions as binary values; you will regret it later. Store them individually. Feel free to use a bitfield for defining the specifics of the permission (none/read/write/etc.), but not the nature of the permission itself.

Why is this Community Wiki?

Adam Robinson
Of course I under stand a bit can only take on the two values. I'm more interested in the approach to this.EDIT: Must have accidentally clicked Community Wiki... How can I turn it off?
TheCloudlessSky
@TheC once you make it CW you can't undo it
Earlz
+3  A: 

I would probably do this as separate fields for each area (Invoices. Patients, Appointments) using a single enum to cover each of them.

enum Permission { None, ReadOnly, ReadWrite };

To me, this is easier to understand and manage, and it doesn't combine a bunch of unrelated things (I should say "seemingly unrelated", since I don't know anything about your app).

Ray
Sure that's what I was thinking too, but how could that be easily stored in the database for each field.
TheCloudlessSky
I would use three separate tinyint fields (sql server) for these things - int works also but is bigger than you need.
Ray
Ok but then as the application grows, I have to keep adding more fields to the table... not nice IMO.
TheCloudlessSky
If the app grows, you will probably be adding lots of new tables and fields - it's part of the growth process. I think these all-in-one bitmask fields are handy in some cases, but can get confusing when you start packing too many different things into them. But I see it as more of a personal preference thing rather than a right-or-wrong thing.
Ray
But I think it's overhead if I had to create a field for a permission like ResetUserPassword and only have a small number actually have this value set to true. This compares to *one* field for every user.
TheCloudlessSky
This shows that the answer to every question is 'it depends'. If you have 3 permission fields as in your example, and you expect to add new ones rarely, then I would still suggest separate fields. If you have 20, then your original approach is probably ok. If you have over 32 different permissions, then you will need multiple fields anyway (or you could use a bigint field to hold them all).
Ray
Yeah I completely see where you're coming from on this. I think I'm really going to have to think things through on this but most likely using your approach. Thanks for your thoughts!EDIT: It'd be really nice to know how large applications do this.
TheCloudlessSky
If WriteOnly ever creeps in you may end up with a lot of code re-factoring to do.
Bernard
A: 

I borrowed and modified this example from: http://stackoverflow.com/questions/469287/c-vs-java-enum-for-those-new-to-c/469315#469315

Regardless I would not use an enum, I would use a class which would allow for much more flexibility. Something like this may help, just don't add patients and invoices as that varies orthogonally from the issue of read and write permissions.

There are many ways to do the bit manipulations and that should probably be done on a separate layer of code. If you need bit manipulations for serialization (to a file or database) then you should put that code there.

I don't use C# much so the syntax may be off, I do Java mostly. Anyway, the basic concept should be clear here:

public class Permissions
{
    public static readonly Permissions NONE = new PERMISSIONS("NONE",false,false);
    public static readonly Permissions READ = new PERMISSIONS("READ",true,false);
    public static readonly Permissions FULL= new PERMISSIONS("FULL",true,true);

    public static IEnumerable<Permissions> Values
    {
            get
            {
                    yield return NONE;
                    yield return READ;
                    yield return FULL;
            }
    }

    private readonly string name;
    private readonly boolean read;
    private readonly boolean write;
    private readonly int bits;

    Permissions(string name, boolean read,boolean write)
    {
            this.name = name;
            this.read = read;
            this.write= write;
            this.bits = bits;
    }

    public string Name { get { return name; } }

    // returns true if read permission is granted
    public double isReadable { get { return read; } }

    // returns true if write permission is granted
    public double isWriteable { get { return write; } }

    public override string ToString()
    {
            return name;
    }

    // returns bit field
    public int bits { get { return write ? 1 : 0 | read ? 2 : 0; } }
}
Bernard