Reputation: 917
I'm writing a GUI application that will have a user log in feature. Each user will belong to (at least one, possibly more than one) group and each group will have attributes indicating if certain rights are allowed or not allowed. The list of rights will cover things like editing things from the past, printing, deleting data, etc. Lots of different actions can be handled by the same right (printing can be initiated both from the menu and from the toolbar, for example).
My question is: what is the best way to implement this security system? Should every action have a Boolean isSecurable attribute and list of rights required? How should the checking be done, by a central structure, or should each action check for the required rights itself?
I'm aiming for correctness here. I know I can hack together a working system quickly but I'd like to have something that won't cause problems down the road. I'm sorry for the verbose explanation but I'm not even sure what to call what I'm looking for.
Edit: This isn't really GUI-specific, I think, but I've researched quite a bit for info on this and most of the stuff I find is for web applications, or general "secure programming" tips.
Upvotes: 2
Views: 712
Reputation: 122
I know this is an old post but wanted to offer my solution. It may not be the best way to do it but it works for me and might be useful for someone.
I have a database application (Windows GUI and MySQL) written in Delphi 7.
Administrators of the application can create users with different levels of access. The app manages Factory Production (raw materials), Products, Sales & Service, as well as Accounting for a company with factories and offices in 3 countries.
Users have access to tabs, tables, and functionality based on their security rights.
Basically, some functions are either disabled or hidden based on privileges. In some cases I display a message to the user, other times their actions are simply ignored if their rights aren't sufficient. Each user can have either single or a combination of rights.
For example, someone might have;
It works like this;
unit bitwise; // Found this unit on stackoverflow - All credit to original author
interface
Const // Added constants that suit me
Adm = 01; // Administrator
Rws = 02; // Read Write Sales
Ros = 04; // Read Only Sale
Rwp = 08; // Read Write Production
Rop = 16; // Read Only Production
roa = 32; // Read Only All
acc = 64; // Accounting
function IsBitSet(const val: byte; const TheBit: Byte): Boolean;
function BitOn(const val: byte; const TheBit: Byte): byte;
function BitOff(const val: byte; const TheBit: Byte): byte;
function BitToggle(const val: byte; const TheBit: Byte): byte;
implementation
function IsBitSet(const val: byte; const TheBit: Byte): Boolean;
begin
Result := (val and (TheBit)) <> 0;
end;
function BitOn(const val: byte; const TheBit: Byte): byte;
begin
Result := val or (TheBit);
end;
function BitOff(const val: byte; const TheBit: Byte): byte;
begin
Result := val and not (TheBit);
end;
function BitToggle(const val: byte; const TheBit: Byte): byte;
begin
Result := val xor (TheBit);
end;
end. // End of Unit
If I want a message displayed when a user attempts something they don't have access to, I use the following function.
Function TForm1.HasRights(Need: Byte; Msg: String;): Boolean;
Begin
If Not IsBitSet(rights, Need) Then
Begin
showdialog('Security', 'You have insufficient Security Rights!', 'You must have ' +
Msg + ' access to perform the action you have attempted.', '', '', false, False, True);
Result := False;
End
Else
Result := True;
End;
I call the above function like this;
If HasRights(Rop Or Rwp Or Adm, '"Read Only Production" or "Read / Write Production"') Then
Begin
// Do something they are allowed to do
End // else ignore them
If I don't need a message box displayed I call IsBitSet like this;
If IsBitSet(rights, Adm) Then
Begin
// Do stuff
end;
Just for clarity, here is the ShowDialog function. It displays a custom form I've created that fits well with my application.
Function TForm1.showdialog(Const DialogTitle: WideString; Const FirstCaption: WideString;
Const SecondCaption: widestring; Const ConfirmBCaption: widestring; Const CancelBCaption:
widestring; LeftButton, RightButton, MiddleButton: Boolean): boolean;
Var
whattheysaid: boolean;
craigsdialog: Tcraigsdialog;
Begin
// Modal1Button and Modal2Button can have modified captions whereas Modal3Button
// is always "Ok". If the only button a user needs is "Ok" then make it visible
// and receive a modalresult of 3 when clicked. This 3rd button is for appearance
// only and just makes it a bit neater.
Whattheysaid := False;
Craigsdialog := Tcraigsdialog.Create(nil);
With Craigsdialog Do
Begin
// Set the Dialog details as required
Caption := DialogTitle;
Label1.Caption := FirstCaption;
Label2.Caption := SecondCaption;
Modal1Button.Visible := leftbutton;
Modal2Button.Visible := rightbutton;
Modal3Button.Visible := Middlebutton;
modal1button.Caption := ConfirmBCaption;
modal2button.Caption := CancelBCaption;
Case ShowModal Of
1: whattheysaid := True
2: whattheysaid := False
3: whattheysaid := True
End;
End;
FreeAndNil(craigsdialog);
Result := whattheysaid;
End;
As I said at the beging, this may or may not be useful, but it works perfectly for me.
Upvotes: 0
Reputation: 41823
Your problem sounds like the State Pattern. Each group that the user belongs to is a different State Object. Attach each applicable State Object to the user. Then, when the user tries to do something that requires a right, ask the state object.
Since a user can be in multiple groups, you might also benefit from the Decorator Pattern.
Upvotes: 0
Reputation: 5870
"BCS" is correct that the security checks should not be tied to the GUI, that should be tied to the underlying actions/operations/methods you're invoking. In an MVC framework, that would be in the Model, or elsewhere, in the actions invoked by the Model.
If the dispatching of your actions is certain to pass through some common mechanism (e.g. all share a certain base class), then putting the security checks there is a good way to cover all the bases.
One additional thought: What you describe as groups may or may not be "roles" in security terminology.
Upvotes: 3
Reputation: 5166
If you will be writing a .NET application you could consider using the membership provider infrastructure. You can even use this approach to implement authentication for both web and desktop clients as described in this MSDN magazine article.
Upvotes: 1
Reputation: 78605
I'd go with something like an MVC system and put none of the security stuff in the GUI code. Make it a part of the model so that it doesn't mater how the user triggers an action, the same security code path gets run.
I've never done much GUI and even less security stuff but one approach would be to have a secure proxy object that keeps track of who is logged in and only forwards requests that are permitted in the current context.
If you want a fun project, that proxy object would be a good candidate for generated code:
Given an interface with security annotations, generate a class that implements that interface and blocks/forwards calls based on the security context passed to it.
Upvotes: 1