rmwaite
rmwaite

Reputation: 917

How to implement security in a GUI application?

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

Answers (5)

Admiral Noisey Bottom
Admiral Noisey Bottom

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;

  • "Read Only Sales & Service" and "Read / Write Production",
  • "Read / Write Sales & Service",
  • Or, any combination of any or all available rights.

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

geowa4
geowa4

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

Liudvikas Bukys
Liudvikas Bukys

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

Yordan Pavlov
Yordan Pavlov

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

BCS
BCS

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

Related Questions