Saturday, November 7, 2009

Make sure an event is not fired

Recently I've been assigned with a task, this task is about making sure that an event will not fire unless user has the right to fire it.
In other words, you have a button in a multi user application and user X does not have the right to delete from database or to add to database, this can be easily done by writing Button.Enabled := False right? wrong, what if the user has some knowledge about windows, I mean he knows how to enable/disable a control with external applications like WinSpy++ or any other software which has option to modify a windows control behavior.
In this case you need a slick way to be absolutely sure that is not possible(it's not 100% sure only 99.99% because softice can come into play but there are few people capable to work with it and at that time you can't really do anything to protect your application) here's my implementation:
I've started by creating a unit which defines and implements two classes: TCtrlHolder and TCtrlHolderList.
type TCtrlHolder = class
                 public
                       Obj : TButton;
                       Name : string;
                       AllowedClick,
                       DeniedClick : TNotifyEvent;
                 public
                       procedure SetEnable(T: boolean);
                 public
                       constructor Create(onButton: TButton);
     end;// type TCtrlHolder = class

type TCtrlHolderList = class
                     private
                            FCtrlList : TList;
                            procedure DisplayDeniedMessage(Sender: TObject);
                     public
                           UserMessage : string;
                     public
                           function AddCtrl(thisCtrl: TCtrlHolder): integer;
                           procedure ReadRightsFromINI(const FileName: string);
                     public
                           constructor Create(const WithMessage: string = '');
                           destructor Destroy; override;
     end;// type TCtrlHolderList = class
The TCtrlHolder is just an intermediate class between the control(button in this case) and TCtrlHolderList which only holds the controls.
implementation
{ TCtrlHolderList }

function TCtrlHolderList.AddCtrl(thisCtrl: TCtrlHolder): integer;
begin
     thisCtrl.DeniedClick := DisplayDeniedMessage;
     Result := FCtrlList.Add(thisCtrl)
end;

constructor TCtrlHolderList.Create(const WithMessage: string = '');
begin
     FCtrlList := TList.Create;
end;

destructor TCtrlHolderList.Destroy;
var i : integer;
    tmp : TObject;
begin
     for i := FCtrlList.Count -1 downto 0 do begin
         tmp := FCtrlList[i];
         FreeAndNil(tmp);
         FCtrlList.Delete(i);
     end;//for i := FCtrlList.Count -1 downto 0 do begin
     FreeAndNil(FCtrlList);
end;

procedure TCtrlHolderList.DisplayDeniedMessage(Sender: TObject);
begin
     if UserMessage <> EmptyStr then
        MessageDlg(UserMessage, mtWarning, [mbOk], 0)
     else
         MessageDlg('Access denied', mtWarning,[mbOK], 0);
end;

procedure TCtrlHolderList.ReadRightsFromINI(const FileName: string);
var ini : TIniFile;
    i : integer;
    s : string;
begin
     ini := TIniFile.Create(FileName);
     for i := 0 to FCtrlList.Count -1 do begin
         s := ini.ReadString('rights', TCtrlHolder(FCtrlList[i]).Name, 'xxx');
         if s <> 'xxx' then
            TCtrlHolder(FCtrlList[i]).SetEnable(StrToBool(s));
     end;// for i := 0 to FCtrlList.Count -1 do begin
     FreeAndNil(ini);
end;

{ TCtrlHolder }

constructor TCtrlHolder.Create(onButton: TButton);
begin
     Obj := onButton;
     Name := Obj.Name;
     AllowedClick := Obj.OnClick;
     if not Obj.Enabled then
        Obj.OnClick := DeniedClick;
end;

procedure TCtrlHolder.SetEnable(T: boolean);
begin
     Obj.Enabled := T;
     if T then
        Obj.OnClick := AllowedClick
     else
         Obj.OnClick := DeniedClick;
end;
The idea is very simple and elegant, you only assign an event based on the rights, no conditions in the real OnClick event.
A demo application can be downloaded from this link(only source code is provided).

No comments:

Post a Comment

Blogroll(General programming and Delphi feeds)