unit abSecurity;

interface

uses
  Windows, Classes, SysUtils, Forms, Controls, ComCtrls, graphics, Dialogs;

const
  C_SUPERVISOR_NAME = 'Supervisor';
  C_DEFAULT_USER_TEMPLATE_REGISTRY_KEY_NAME = '\Software\AbEclecticVision\AbEclecticSecurity\CurrentVersion\SecurityObject\UserTemplate';
  C_DEFAULT_USER_TEMPLATE_FILE_NAME = 'UserTemplate';

  C_YES = ' (Yes)';
  C_NO = ' (No)';
  //C_MAX_ID = 2048;
  C_LAST_USER_COUNT = 5;

type
  TString40 = string[40];
  TString34 = string[34];
  
resourcestring
  rsAutoLogonFail = 'Auto Logon Failed.';
  rsOnlyOneInstanceFail = 'No more than one instance of Security Component allowed.';
  rsVersionMismatch = 'Version mismatch. Cannot continue';

type

  EabSecurityException = class(Exception);

  TabSecurityOptions = (soForceBuildSecurityFromClients,
                      soForceIDFromClients,
                      soForceClientsVisibleAfterLogon,
                      soAutoSaveUsers,
                      soShowCheckedOKOnly,
                      soAutoLogon,
                      soReleaseOwnerOnLogonFail,
                      soCreateSupervisorIfMissing,
                      soShowIcons,
                      soStorePassword,
                      soSilentLogonTry,
                      //v 1.4
                      soSaveUsersInFile);
  TabSecurityOptionSet = set of TabSecurityOptions;

  TabSecurityError = (seNone, seUserTemplateMissing,
                    seSecurityTemplateMissing,
                    seSecurityTemplateCannotBuildFromClients,
                    seUsrAccessInitEntryMissing,
                    seUsrAccessUserEntryMissing,
                    seUsrAccessRightEntryMissing,
                    eUsrAccessLoginNameEntryMissing,
                    seUsrAccessLoginNameEntryMissing,
                    seUsrAccessInitFailed);

  TNodeType = (ntNone, ntPassword, ntUserLeave, ntIntermediate, ntUserRegular, ntSupervisor, ntSupervisorLeave);

  TVersion = record
    Major: integer;
    Minor: integer;
    MinorMinor: integer;
    Patch: char;
  end;

  TAdditionalInfo = record
    iItemIndex: integer;
    iImageIndex: integer;
    sOwner: string[40];
    Reserved: array[0..79] of char;
  end;

  TNodeData = record
    iId: integer;
    sCaption: string[40];
    bCheckOK: boolean;
    AdditionalInfo: TAdditionalInfo;
  end;
  PNodeData = ^TNodeData;

  TabSecurityTemplate = class(TTreeView)
    function GetPasswordFromUserTreeNode(tnUser: TTreeNode): string;
    function GetUserPassword(sUserName: string): string;
    procedure AddUserToUserTemplate(tvSourceTemplate: TabSecurityTemplate; sUserName, sPassword: string; bGrantAll: boolean);
    procedure BuildASecurityUser(tnDest, tnSource: TTreeNode; bGrantAll: boolean);
    function GetUserNodeByUserName(sUserName: string): TTreeNode;
    function GetUserNodeForSelected: TTreeNode;
    function GetUserNodeForNode(tn: TTreeNode): TTreeNode;
    procedure DeleteNode(tnUserNode: TTreeNode);
    function GetSelectedNodeType(var sUser, sPassword: string): TNodeType;
    function GetNodeType(tn: TTreeNode; var sUser, sPassword: string): TNodeType;
    function GetNodeTypeSecurityTemplate(tn: TTreeNode): TNodeType;
    procedure ClearTemplate;
    procedure LoadFromStreamSecurity(Stream: TMemoryStream);
    procedure SaveToStreamSecurity(Stream: TMemoryStream);
    function GenerateSecurityId: integer;
    function IsValidSecurityId(iID: integer): boolean;
    destructor Destroy; override;
    function DeleteKey(sKey: string): boolean;//v1.4
  end;

  TabSecurity = class;

  TabSecurityDialogs = class
  private
    fSecurity: TabSecurity;
    procedure SetSecurityObject(SecurityObject: TabSecurity);
  public
    ftvUserTemplate: TabSecurityTemplate;
    ftvSecurityTemplate: TabSecurityTemplate;
    //ffrUserTemplate: TForm;
    constructor Create{(AOwner: TComponent); override};
    destructor Destroy; override;
    function Init(var sLogonName: string; var sPassword: string; bTrySilentInit: boolean; bRestrictToCheckOK: boolean): boolean;
  published
    property SecurityObject: TabSecurity read fSecurity write SetSecurityObject;
  end;

  TabSecurity = class(TComponent)
  private
    fhFileMap: THandle;
    fsCurrentUser: string;
    fbIsSupervisor: boolean;

    flClientList: TList;//Currently supports TabActionListClients only
    fbUserBlobChanged: boolean;

    fbLoggedOn: boolean;

    FLastError: TabSecurityError;
    fSecurityOptions: TabSecurityOptionSet;
    fsUserTemplateRegistryKeyName: string;
    fsUserTemplateFileName: string;

    fImglActions: TImageList;

    //D4 Moved to publicftvSecurityTemplate: TabSecurityTemplate;
    ffrSecurityTemplate: TForm;

    procedure NotifyClientsForBeingDestroyed;
    procedure ForceClientsVisible;
    function ReadUserTemplateFromRegistry(var sPasswordCripted: string): boolean;
    function WriteUserTemplateIntoRegistry(sPasswordCripted: string): boolean;
    function GetCurrentUser: string;
    function OnlyInstance: boolean;
    procedure AddInLastUser(sUserName: string);
  protected
    procedure Loaded; override;
  public

    ftvUserTemplate: TabSecurityTemplate;//v1.4
    ftvSecurityTemplate: TabSecurityTemplate;//v1.4

    flstLastUser: TStringList;
    fSecurityDialogs: TabSecurityDialogs;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function Logon(sUserName: string = '';  sPassword: string = ''; bTrySilent: boolean = false): boolean;
    procedure ShowPrivileges;
    function CheckId(ID: integer): boolean;
    procedure AddClientObject(Client: TObject);
    procedure RemoveClientObject(Client: TObject);

    function DoBuildSecurityTemplateFromClients(ftvSecurityTemplate: TabSecurityTemplate; fimglActions: TImageList): boolean;

    procedure DefineProperties(Filer: TFiler); override;

    procedure StoreTreeProperty(Stream: TStream);
    procedure LoadTreeProperty(Stream: TStream);
    procedure StoreImageProperty(Stream: TStream);
    procedure LoadImageProperty(Stream: TStream);

    //v 3.0
    function GenerateUniqueSecurityId: integer;
    function GetActionClientById(iId: integer): TObject;
    property Images: TImageList read FimglActions;
    property UserTemplate: TabSecurityTemplate read ftvUserTemplate write ftvUserTemplate;
    procedure SetOptions(SecurityOptionSet: TabSecurityOptionSet);
{$IFDEF DELPHI4}
    property SecurityTemplate: TabSecurityTemplate read ftvSecurityTemplate write ftvSecurityTemplate;
{$ENDIF}
  published
    property UserTemplateRegistryKeyName: string read fsUserTemplateRegistryKeyName write fsUserTemplateRegistryKeyName;
    property UserTemplateFileName: string read fsUserTemplateFileName write fsUserTemplateFileName;
    property CurrentUser: string read GetCurrentUser;
    property IsSupervisor: boolean read fbIsSupervisor;
    property LastError: TabSecurityError read FLastError;
    property Options: TabSecurityOptionSet read fSecurityOptions write SetOptions;
{$IFNDEF DELPHI4}
    property SecurityTemplate: TabSecurityTemplate read ftvSecurityTemplate write ftvSecurityTemplate;
{$ENDIF}    
  end;

  procedure Register;
  function GetStringItem(Expression: string; Count: integer; Separator: Char): string;
  procedure CopyEntireUserTemplate(tvSource, tvDest: TabSecurityTemplate);

const
  C_VERSION: TVersion = (Major: 1; Minor: 4; MinorMinor: 0; Patch: #0);
  
implementation

uses Registry, abActnLst, abEntryPasswordDlg, abCript, abDlgSecurity,
     stdctrls{//v 3.0}, Math;

{$A-}

procedure Register;
begin
  RegisterComponents ('Security', [TabSecurity]);
end;


function GetStringItem(Expression: string; Count: integer; Separator: Char): string;
var
  i: integer;
  position: byte;
begin
 Result:= '';
 try
   for i:= 1 to Count  do
   begin
     position:= Pos(Separator, Expression);
     if position <> 0 then
     begin
   	   Result:= Copy(Expression, 1, Position - 1);
	   Expression:= Copy(Expression, Position + 1, length(Expression) - Position);
      end
      else
      begin
        if i = Count then
          Result:= Expression
        else
          Result:= '';
        exit;
      end;
   end;
 finally
   Result:= Trim(Result);
 end;
end;

//v 3.0
function GetCaption(sCaption: string): string;
var
  i: integer;
begin
  for i := length(sCaption) downto 1 do
    if sCaption[i] = '&' then
    begin
      sCaption := Copy(sCaption, 1, Pred(i)) + Copy(sCaption, Succ(i), Length(sCaption) - i);
    end;

  for i := length(sCaption) downto 1 do
    if sCaption[i] <> ' ' then
      if sCaption[i] < '!' then
        sCaption[i] := '_';
  Result := sCaption;
end;


procedure CopyEntireUserTemplate(tvSource, tvDest: TabSecurityTemplate);
var
  i: integer;
  lpNodeData: PNodeData;
begin
  tvDest.Items.assign(tvSource.Items);
  for i:= Pred(tvSource.Items.Count) downto 0 do
    if nil <> tvSource.Items[i].Data then
    begin
      new(lpNodeData);
      tvDest.Items[i].Data := lpNodeData;
      PNodeData(tvDest.Items[i].Data)^ := PNodeData(tvSource.Items[i].Data)^;
    end;
end;


function TabSecurity.OnlyInstance: boolean;
begin
{$B-}
  if not (Assigned(Owner) and (Owner is TWinControl)) then
    Result := true
  else
  begin
    fhFileMap := OpenFileMapping(FILE_MAP_READ, false, PChar(intToStr(TWinControl(Owner).Handle)));
    if fhFileMap = 0 then
    begin
      fhFileMap := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, SizeOf(WORD), PChar(intToStr(TWinControl(Owner).Handle)));
      Result := true;
    end
    else
      Result := false;
  end;
end;

function TabSecurity.GetActionClientById(iId: integer): TObject;
var
  i, j: integer;
begin
  for i := Pred(flClientList.Count) downto 0 do
    if TObject(flClientList[i]) is TabActionList then
      for j:= 0 to Pred(TabActionList(TObject(flClientList[i])).ActionCount) do
        if TabActionList(TObject(flClientList[i])).Actions[j] is TabAction then
          if iId = TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId then
          begin
            Result := TabAction(TabActionList(TObject(flClientList[i])).Actions[j]);
            exit;
          end;
  Result := nil;
end;

//v 3.0
function TabSecurity.GenerateUniqueSecurityId: integer;
var
  i, j: integer;
begin
  Result := 0;
  for i := Pred(flClientList.Count) downto 0 do
    if TObject(flClientList[i]) is TabActionList then
      for j:= 0 to Pred(TabActionList(TObject(flClientList[i])).ActionCount) do
        if TabActionList(TObject(flClientList[i])).Actions[j] is TabAction then
          if Result <= TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId then
            Result := Succ(TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId);
  Result := max(ftvSecurityTemplate.GenerateSecurityId, Result);
end;

constructor TabSecurity.Create;
begin
  inherited;
  fLastError:= seNone;
  fbUserBlobChanged := false;
  fbLoggedOn := false;
  flClientList := TList.Create;
  fSecurityOptions := [soAutoSaveUsers,
                       soShowCheckedOKOnly,
                       soAutoLogon,
                       soReleaseOwnerOnLogonFail,
                       soCreateSupervisorIfMissing,
                       soShowIcons,
                       soSaveUsersInFile];
  fsUserTemplateRegistryKeyName := C_DEFAULT_USER_TEMPLATE_REGISTRY_KEY_NAME;
  fsUserTemplateFileName := C_DEFAULT_USER_TEMPLATE_FILE_NAME;

  fimglActions := TImageList.Create(nil);
  fimglActions.Masked := false;
  ffrSecurityTemplate := TForm.Create(nil);
  ffrSecurityTemplate.Align := alClient;
  ftvSecurityTemplate := TabSecurityTemplate.Create(nil);
  ftvSecurityTemplate.Parent := ffrSecurityTemplate;
  ftvSecurityTemplate.Align := alClient;
  ftvUserTemplate := TabSecurityTemplate.Create(nil);
  ftvUserTemplate.Parent := ffrSecurityTemplate;
  fSecurityDialogs := TabSecurityDialogs.Create{(nil)};

  flstLastUser := TStringList.Create;
  fhFileMap := 0;
  if not OnlyInstance then
  begin
    closehandle(fhFileMap);
    raise Exception.Create(rsOnlyOneInstanceFail);
  end;

  //v 1.4
  (*
  if (csDesigning in ComponentState) and Assigned(Owner) then
  begin
    bActionListRefered := false;
    for i:= Pred(Owner.ComponentCount) downto 0 do
      if Owner.Components[i] is TabActionList then
        if not Assigned(TabActionList(Owner.Components[i]).SecurityObject) then
        begin
          TabActionList(Owner.Components[i]).SecurityObject :=  Self;
          bActionListRefered := true;
          if TabActionList(Owner.Components[i]).ActionCount > 0 then
          begin
            //fimglActions.Assign(TImageList(TabActionList(Owner.Components[i]).images));
            fimglActions.Masked := false;
          end;
        end;

    if bActionListRefered then
      if mrYes = MessageDlg('New abActionList found. Do you want to build security template from clients?', mtConfirmation, [mbYes, mbNo], 0) then
      begin
        DoBuildSecurityTemplateFromClients(ftvSecurityTemplate, fimglActions);
      end;
  end;
  *)
end;


destructor TabSecurity.Destroy;
begin
  NotifyClientsForBeingDestroyed;
  flClientList.Destroy;

  ftvUserTemplate.ClearTemplate;
  ftvUserTemplate.Destroy;
  ftvSecurityTemplate.ClearTemplate;

  ftvSecurityTemplate.Destroy;
  ffrSecurityTemplate.Destroy;
  fSecurityDialogs.Destroy;
  fimglActions.Destroy;
  flstLastUser.Destroy;
  CloseHandle(fhFileMap);
  inherited;
end;

procedure TabSecurity.SetOptions(SecurityOptionSet: TabSecurityOptionSet);
begin
  fSecurityOptions := SecurityOptionSet;
  if soShowIcons in fSecurityOptions then
    ftvSecurityTemplate.Images := fimglActions
  else
    ftvSecurityTemplate.Images := nil;
end;

procedure TabSecurity.ForceClientsVisible;
var
  i, j: integer;
begin
  for i := Pred(flClientList.Count) downto 0 do
    if TObject(flClientList[i]) is TabActionList then
      if {//v 3.0 was soSetInvisibleOnFail} sorSetInvisibleOnFail = TabActionList(flClientList[i]).Options then//v 3.0 ???? was "in" and cannot compile ????
        for j:= Pred(TabActionList(TObject(flClientList[i])).ActionCount) downto 0 do
          if TabActionList(TObject(flClientList[i])).Actions[j] is TabAction then
            if soForceClientsVisibleAfterLogon in fSecurityOptions then
              TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).Visible := true;

end;


function TabSecurity.Logon(sUserName: string = '';  sPassword: string = ''; bTrySilent: boolean = false): boolean;
begin
  Result := false;
  fbLoggedOn := fSecurityDialogs.Init(sUserName, sPassword, bTrySilent, soShowCheckedOKOnly in fSecurityOptions);
  if fbLoggedOn then
  begin
    fsCurrentUser := sUserName;
    fbIsSupervisor := fsCurrentUser = C_SUPERVISOR_NAME;

    WriteUserTemplateIntoRegistry(TCripter.Cript(sPassword));

    Result := true;
  end
  else
  begin
    fsCurrentUser := '';
    fbIsSupervisor := false;
  end;
  ForceClientsVisible;
end;


procedure TabSecurity.ShowPrivileges;
var
  dlgPassword: TDlgPassword;
  frmSecurity: TfrmSecurity;

begin
  if not fbLoggedOn then
    Logon(fsCurrentUser)
  else
  begin
    dlgPassword := TDlgPassword.Create(nil);
    try
      frmSecurity := TfrmSecurity.Create(nil);
      frmSecurity.ftvSecurityTemplate := ftvSecurityTemplate;



      try
        TabSecurityTemplate(frmSecurity.ftvUserTemplate).ClearTemplate;
        TabSecurityTemplate(frmSecurity.ftvUserTemplate).Images := ftvSecurityTemplate.Images;
        dlgPassword.CopyUserTemplate(ftvUserTemplate, TabSecurityTemplate(frmSecurity.ftvUserTemplate), fsCurrentUser, soShowCheckedOKOnly in fSecurityOptions);
        frmSecurity.IsSupervisor := fsCurrentUser = C_SUPERVISOR_NAME;
        if frmSecurity.ftvUserTemplate.Items.Count > 0 then
          frmSecurity.ftvUserTemplate.Items[0].Expand(true);
        if mrOK = frmSecurity.ShowModal then
        begin
          if fsCurrentUser = C_SUPERVISOR_NAME then
            dlgPassword.CopyUserTemplate(TabSecurityTemplate(frmSecurity.ftvUserTemplate), ftvUserTemplate, fsCurrentUser, soShowCheckedOKOnly in fSecurityOptions)//Might be used as is for a user copy only as well
          else//Copy Data (Password only)
            PNodeData(ftvUserTemplate.GetUserNodeByUserName(fsCurrentUser).Item[0].Data)^ :=
               PNodeData(TabSecurityTemplate(frmSecurity.ftvUserTemplate).GetUserNodeByUserName(fsCurrentUser).Item[0].Data)^;

          WriteUserTemplateIntoRegistry(TCripter.Cript(PNodeData(ftvUserTemplate.GetUserNodeByUserName(fsCurrentUser).Item[0].Data)^.sCaption));
        end;
      finally
        frmSecurity.Destroy;
      end;
    finally
      dlgPassword.Destroy;
    end;
  end;
end;


function TabSecurity.WriteUserTemplateIntoRegistry(sPasswordCripted: string): boolean;
var
  Reg: TRegistry;
  strUserTemplate: TMemoryStream;
begin
  Reg:= TRegistry.Create;
  try
    Reg.RootKey := HKEY_CURRENT_USER;
    if not Reg.OpenKey(fsUserTemplateRegistryKeyName, true) then
    begin
      Result := false;
      exit;
    end
    else
    begin
      strUserTemplate := TMemoryStream.Create;
      try
        ftvUserTemplate.SaveToStreamSecurity(strUserTemplate);
        strUserTemplate.Position := 0;
        //v 1.4
        if soSaveUsersInFile in fSecurityOptions then
        begin
          strUserTemplate.SaveToFile(fsUserTemplateFileName);
          Reg.WriteString('UserTemplateFileName', fsUserTemplateFileName);
        end
        else
        begin
          try
            Reg.WriteBinaryData('UserTemplate', strUserTemplate.Memory^, strUserTemplate.Size);
          except
              ShowMessage('Error saving Users into Windows Registry. ' + #9#13 +
                          'Probably you work with an older version of Windows that limits the size of binary block to be written to the registry. ' + #9#13 +  #9#13 +
                          'To resolve this problem you may chose to save Users into a file, pointed by a registry entry. ' +
                          '(set "soSaveUsersInFile" option to your TabSecurity component and rebuild)');
            Result := false;
            exit;
          end;
        end;

        try
          Reg.WriteInteger('UserTemplateSize', strUserTemplate.Size);
        except
          Result := false;
          exit;
        end;

        strUserTemplate.Clear;
        flstLastUser.SaveToStream(strUserTemplate);
        try
          Reg.WriteBinaryData('LastUsers', strUserTemplate.Memory^, strUserTemplate.Size);
        except
          //Result := false;
          //exit;
        end;

        try
          Reg.WriteInteger('LastUsersSize', strUserTemplate.Size);
        except
          //Result := false;
          //exit;
        end;

        if soStorePassword in fSecurityOptions then
        try
          Reg.WriteString('Password', sPasswordCripted);
        except//Not important
        end;
      finally
        strUserTemplate.Destroy;
      end;
    end;
    Result := true;
  finally
    Reg.Free;
  end;
end;


function TabSecurity.ReadUserTemplateFromRegistry(var sPasswordCripted: string): boolean;
var
  Reg: TRegistry;
  iUserTemplateSize: integer;
  strUserTemplate: TMemoryStream;
begin
  sPasswordCripted := '';
  Reg:= TRegistry.Create;
  try
    Reg.RootKey := HKEY_CURRENT_USER;
    if not Reg.OpenKey(fsUserTemplateRegistryKeyName, true) then
    begin
      Result := false;
      exit;
    end
    else
    begin
      try
        iUserTemplateSize := Reg.ReadInteger('UserTemplateSize');
      except
        Result := false;
        exit;
      end;
    end;

    if soStorePassword in fSecurityOptions then
      try
        sPasswordCripted := Reg.ReadString('Password');
      except//Not so important
      end;

    strUserTemplate := TMemoryStream.Create;
    try
      strUserTemplate.SetSize(iUserTemplateSize);
      //v 1.4
      if soSaveUsersInFile in fSecurityOptions then
        try
          strUserTemplate.LoadFromFile(fsUserTemplateFileName);
        except
          strUserTemplate.SetSize(0);
        end
      else
        {if iUserTemplateSize} Reg.ReadBinaryData('UserTemplate', strUserTemplate.Memory^, iUserTemplateSize);{ then}

      if iUserTemplateSize <> strUserTemplate.Size then
      begin
        Result := false;
        exit;
      end;
      strUserTemplate.Position := 0;

      ftvUserTemplate.LoadFromStreamSecurity(strUserTemplate);

      strUserTemplate.Clear;
      try
        iUserTemplateSize := Reg.ReadInteger('LastUsersSize');
      except
        Result := false;
        exit;
      end;
      strUserTemplate.SetSize(iUserTemplateSize);
      if iUserTemplateSize  = Reg.ReadBinaryData('LastUsers', strUserTemplate.Memory^, iUserTemplateSize) then
        flstLastUser.LoadFromStream(strUserTemplate);
    finally
      strUserTemplate.Destroy;
    end;
    Result := true;
  finally
    Reg.Free;
  end;
end;


function TabSecurity.CheckId(ID: integer): boolean;
  function GetNodeForId(tn: TTreeNode): TTreeNode;
  var
    tnChild: TTreeNode;
  begin
    Result := nil;
    if tn.HasChildren then
    begin
      tnChild := tn.GetFirstChild;
      while Assigned(tnChild) do
      begin
        Result := GetNodeForId(tnChild);
        if Assigned(Result) then
          exit;
        tnChild := tn.GetNextChild(tnChild);
      end;
    end
    else
      if Assigned(tn.Data) then
        if PNodeData(tn.Data)^.iId = Id then
          Result := tn;
end;
var
  tnID: TTreeNode;
begin
  if fbLoggedOn then
  begin
    tnID := GetNodeForId(ftvUserTemplate.GetUserNodeByUserName(fsCurrentUser));
    if Assigned(tnID) then
      if Pos(C_YES, tnID.Text) = (Succ(length(tnID.Text)) - length(C_YES)) then
      begin
        Result := true;
        exit;
      end;
  end;
  Result := false;
end;


procedure TabSecurity.AddClientObject(Client: TObject);
begin
  if Client is TabActionList then
    flClientList.Add(Client);
  //Currently supports only TabActionList type of clients
  //Add else if clause and corresponding code for other type of clients
end;


procedure TabSecurity.RemoveClientObject(Client: TObject);
var
  i: integer;
begin
  for i := Pred(flClientList.Count) downto 0 do
    if flClientList.Items[i] = Client then
    begin
      flClientList.Delete(i);
      exit;
    end;
end;


procedure TabSecurity.NotifyClientsForBeingDestroyed;
var
  i: integer;
begin
  for i := Pred(flClientList.Count) downto 0 do
    if TObject(flClientList[i]) is TabActionList then
    begin
      TabActionList(flClientList[i]).SecurityObject := nil;
    end;
end;


function TabSecurity.DoBuildSecurityTemplateFromClients(ftvSecurityTemplate: TabSecurityTemplate; fimglActions: TImageList): boolean;
var
  i, j, k: integer;
  slIDs: TStringList;
  tnParent, tnChild: TTreeNode;
  sCategoryItem: string;
  iCategoryId: integer;
  msBlobSecurityTemplate: TMemoryStream;

  bmpImage: TBitmap;
  iImageOffset: integer;

  iByte: BYTE;
  iWord: WORD;
  iDWord: DWORD;
  s: Array[0..65535] of Char;
  lpNodeData: PNodeData;

  function FindTreeNodeByCategoryAndLevel(sCategoryItem: string; iLevel: integer): TTreeNode;
  var
    i: integer;
  begin
    for i := Pred(ftvSecurityTemplate.Items.Count) downto 0 do
      if ftvSecurityTemplate.Items[i].Level = iLevel then
        if ftvSecurityTemplate.Items[i].Text = sCategoryItem then
        begin
          Result := ftvSecurityTemplate.Items[i];
          exit;
        end;
    Result := nil;
  end;

  procedure ParseTree(tn: TTreeNode);
  var
    tnChild: TTreeNode;
  begin
    if tn.Level = 0 then
    begin//Write header
      iByte := 1;
      msBlobSecurityTemplate.WriteBuffer(iByte, 1);//number of languages
      msBlobSecurityTemplate.WriteBuffer('GB'+ #0, 3);//language identifier
      iDWord := 0;
      msBlobSecurityTemplate.WriteBuffer(iDWord, 4);//size of the language related part of the blob (w/o header) - set up later
      iWord := tn.Count;
      msBlobSecurityTemplate.WriteBuffer(iWord, 2);//Number of level 1 nodes
    end
    else
    begin
      iWord := tn.Count;
      msBlobSecurityTemplate.WriteBuffer(iWord, sizeOf(iWord));//Number of children
      iWord := WORD(tn.Data);
      msBlobSecurityTemplate.WriteBuffer(iWord, sizeOf(iWord));//Self Security ID
      iWord := Length(tn.Text);
      msBlobSecurityTemplate.WriteBuffer(iWord, sizeOf(iWord));//Length of the name
      move(PChar(Pointer(tn.Text))^, s, length(tn.Text));
      s[length(tn.Text)] := #0;
      msBlobSecurityTemplate.WriteBuffer(Addr(s[0])^, Succ(iWord));//Self Caption
    end;
    tnChild := tn.GetFirstChild;
    while Assigned(tnChild) do
    begin
      ParseTree(tnChild);
      tnChild := tn.GetNextChild(tnChild);
    end;
  end;

  function GetCategoryIdNext: integer;
  begin
    repeat
      inc(iCategoryId);
    until slIDs.IndexOf(intToStr(iCategoryId)) <> 0;
    Result := iCategoryId;
  end;

  function CheckSecurityIDsDuplicate: boolean;//0 - Check Passed, <> 0 - duplicates found
  var
    i, j, iDuplicateIndx: integer;
  begin
    for i := Pred(flClientList.Count) downto 0 do
      if TObject(flClientList[i]) is TabActionList then
        for j:= Pred(TabActionList(TObject(flClientList[i])).ActionCount) downto 0 do
          if TabActionList(TObject(flClientList[i])).Actions[j] is TabAction then
          begin
            iDuplicateIndx := slIDs.IndexOf(intToStr(TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId));
            if -1 <> iDuplicateIndx then
            begin
              Result := false;
              exit;
            end;
            if 0 <> TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId then
              slIds.Add(intToStr(TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId));
        end;
    Result := true;
  end;

begin
  //Result := false;
  slIDs := TStringList.Create;
  try
    if soForceIDFromClients in fSecurityOptions then
      if not CheckSecurityIDsDuplicate then
        raise Exception.Create('Duplicate Security ID Found. Cannot Continue. Please correct.');
    iCategoryId := 0;

    //Leaks 01/30/02 //ftvSecurityTemplate.Items.Clear;
    ftvSecurityTemplate.ClearTemplate;
    fimglActions.Clear;

    tnParent := ftvSecurityTemplate.Items.Add(nil, 'User');//Root node Id (tag) is 0 by default
    new(lpNodeData);
    lpNodeData^.iId := 0;
    lpNodeData^.sCaption := '';//tnParent.Text;
    lpNodeData^.bCheckOK := false;
    lpNodeData^.AdditionalInfo.iItemIndex := tnParent.AbsoluteIndex;//No need because changes???
    lpNodeData^.AdditionalInfo.iImageIndex := 0;
    tnParent.Data := lpNodeData;

    iImageOffset := 0;
    for i := Pred(flClientList.Count) downto 0 do
      if TObject(flClientList[i]) is TabActionList then
      begin

        if iCategoryId = 0 then//FirstFound determins Images characteristics
        begin
          if Assigned(TabActionList(flClientList[i]).Images) then
          begin
            fimglActions.Assign(TabActionList(flClientList[i]).Images);
            fimglActions.Masked := false;
          end;

          bmpImage := TBitmap.Create;
          bmpImage.LoadFromResourceName(HInstance, 'TCHECKABLE');//Index 4
          if Assigned(bmpImage) then
            fimglActions.Insert(0, bmpImage, bmpImage);//Add mask

          bmpImage := TBitmap.Create;
          bmpImage.LoadFromResourceName(HInstance, 'TPASSWORD');//Index 3
          if Assigned(bmpImage) then
            fimglActions.Insert(0, bmpImage, bmpImage);//Add mask

          bmpImage := TBitmap.Create;;
          //bmpImage.Assign(img2.Picture);
          bmpImage.LoadFromResourceName(HInstance, 'TCATEGORY');//Index 2
          if Assigned(bmpImage) then
            fimglActions.Insert(0, bmpImage, bmpImage);//Add mask

          bmpImage := TBitmap.Create;;
          //bmpImage.Assign(img2.Picture);
          bmpImage.LoadFromResourceName(HInstance, 'THAND');//Index 1
          if Assigned(bmpImage) then
            fimglActions.Insert(0, bmpImage, bmpImage);//Add mask

          bmpImage := TBitmap.Create;
          bmpImage.LoadFromResourceName(HInstance, 'TUSER');//Index 0
          if Assigned(bmpImage) then
            fimglActions.Insert(0, bmpImage, bmpImage);//Add mask

          tnParent.ImageIndex := 0;
          tnParent.SelectedIndex := 0;
          iImageOffset := 5;
        end
        else if Assigned(TabActionList(flClientList[i]).Images) then
        begin
          iImageOffset := fimglActions.Count;
          for j := 0 to Pred(TabActionList(flClientList[i]).Images.Count) do
          begin
            bmpImage := TBitmap.Create;
            TabActionList(flClientList[i]).Images.GetBitmap(j, bmpImage);
            fimglActions.AddMasked(bmpImage, clWindow{nil});//Add mask
          end;
        end;


        for j:= 0 to Pred(TabActionList(TObject(flClientList[i])).ActionCount) do
          if TabActionList(TObject(flClientList[i])).Actions[j] is TabAction then
          begin
            tnParent := ftvSecurityTemplate.Items[0];
            k := 1;
            sCategoryItem := GetStringItem(TabActionList(TObject(flClientList[i])).Actions[j].Category, k, '/');
            while length(sCategoryItem) > 0 do
            begin
              tnChild := FindTreeNodeByCategoryAndLevel(sCategoryItem, k);//second parameter is level
              if not Assigned(tnChild) then
              begin
                tnChild := ftvSecurityTemplate.Items.AddChild(tnParent, sCategoryItem);
                new(lpNodeData);
                lpNodeData^.iId := 0;
                lpNodeData^.sCaption := sCategoryItem;
                lpNodeData^.bCheckOK := false;
                lpNodeData^.AdditionalInfo.iItemIndex := tnChild.AbsoluteIndex;//No need because changes???
                lpNodeData^.AdditionalInfo.iImageIndex := 2;
                tnChild.Data := lpNodeData;//0 //No ID for intermediate (hierarchical) nodes Pointer(GetCategoryIdNext);

                tnChild.ImageIndex := 2;
                tnChild.SelectedIndex := 2;
              end;
              tnParent := tnChild;
              inc(k);
              sCategoryItem := GetStringItem(TabActionList(TObject(flClientList[i])).Actions[j].Category, k, '/');
            end;
            tnChild := ftvSecurityTemplate.Items.AddChild(tnParent, TString34(GetCaption(TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).Caption)));

            new(lpNodeData);
            if (not (soForceIDFromClients in fSecurityOptions)) or
               (0 = TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId) then
            begin
              lpNodeData^.iId := GetCategoryIdNext;
              //tnChild.Data := Pointer(GetCategoryIdNext);
              TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId := lpNodeData^.iId;//Integer(tnChild.Data);
            end
            else
              //tnChild.Data := Pointer(TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId);
              lpNodeData^.iId := TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).SecurityId;

            tnChild.ImageIndex := -1;
            tnChild.SelectedIndex := -1;
            if Assigned(TabActionList(flClientList[i]).Images) then
              if TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).ImageIndex >= 0 then
              begin
                tnChild.ImageIndex := iImageOffset + TabAction(TabActionList(TObject(flClientList[i])).Actions[j]).ImageIndex;//Otwerwise -1 by default
                tnChild.SelectedIndex := tnChild.ImageIndex;
              end;

            lpNodeData^.sCaption := tnChild.Text;
            lpNodeData^.bCheckOK := false;//No need
            lpNodeData^.AdditionalInfo.iItemIndex := tnChild.AbsoluteIndex;//No need because changes???
            lpNodeData^.AdditionalInfo.iImageIndex := tnChild.ImageIndex;
            tnChild.Data := lpNodeData;
          end;
      end;
  finally
    slIDs.Destroy;
  end;
  Result := true;
end;


function TabSecurity.GetCurrentUser: string;
begin
  if fbLoggedOn then
    Result := fsCurrentUser
  else
    Result := '';
end;

procedure TabSecurity.AddInLastUser(sUserName: string);
var
  iDxUserName: integer;
begin
  iDxUserName := flstLastUser.IndexOf(sUserName);
  if iDxUserName >= 0 then
    flstLastUser.Delete(iDxUserName);
  if flstLastUser.Count > C_LAST_USER_COUNT then
    flstLastUser.Delete(0);
  flstLastUser.Add(sUserName);
end;

procedure TabSecurity.LoadTreeProperty(Stream: TStream);
begin
  ftvSecurityTemplate.LoadFromStreamSecurity(TMemoryStream(Stream));
end;


procedure TabSecurity.StoreTreeProperty(Stream: TStream);
begin
  ftvSecurityTemplate.SaveToStreamSecurity(TMemoryStream(Stream));
end;


procedure TabSecurity.LoadImageProperty(Stream: TStream);
var
  i: integer;
  Image: TBitmap;
  iDWord: DWORD;
begin
  Stream.ReadBuffer(iDWord, sizeof(iDWord));
  Image := TBitmap.Create;
  try
    for i := 0 to Pred(iDWord) do
    begin
      Image.LoadFromStream(Stream);
      FimglActions.Add(Image, Image);
    end;
  finally
    Image.Destroy;
  end;
end;


procedure TabSecurity.StoreImageProperty(Stream: TStream);
var
  i: integer;
  Image: TBitmap;
  iDWord: DWORD;
begin
  Stream.Position := 0;
  iDWord := FimglActions.Count;
  Stream.WriteBuffer(iDWord, sizeof(iDWord));
  Image := TBitmap.Create;
  try
    for i := 0 to Pred(FimglActions.Count) do
    begin
      FimglActions.GetBitmap(i, Image);
      Image.SaveToStream(Stream);
    end;
  finally
    Image.Destroy;
  end;
end;


procedure TabSecurity.DefineProperties(Filer: TFiler);
begin
  inherited;
  //Filer.DefineBinaryProperty('ImageData', LoadImageCompProperty, StoreImageCompProperty, true);
  Filer.DefineBinaryProperty('evTree', LoadTreeProperty, StoreTreeProperty, true);
  Filer.DefineBinaryProperty('evImage', LoadImageProperty, StoreImageProperty, true);
end;


procedure TabSecurity.Loaded;
var
  sUserName, sPasswordCripted: string;
begin
  inherited Loaded;                                     { call the inherited method first}
  try
    if not (csDesigning in ComponentState) then
    begin
      ReadUserTemplateFromRegistry(sPasswordCripted);

      if soShowIcons in fSecurityOptions then
        ftvSecurityTemplate.Images := fimglActions
      else
        ftvSecurityTemplate.Images := nil;
      if soForceBuildSecurityFromClients in fSecurityOptions then
        if not DoBuildSecurityTemplateFromClients(ftvSecurityTemplate, fimglActions) then
          raise Exception.Create('Cannot build Security template from clients');
      fSecurityDialogs.SecurityObject := Self;

      if soAutoLogon in fSecurityOptions then
      begin
        if flstLastUser.Count > 0 then
          sUserName := flstLastUser[Pred(flstLastUser.Count)]
        else
          sUserName := '';

        sPasswordCripted := TCripter.Decript(sPasswordCripted);
        if not Logon(sUserName, sPasswordCripted, soSilentLogonTry in fSecurityOptions) then
          if soReleaseOwnerOnLogonFail in fSecurityOptions then
            if Owner is TForm then
              Application.Terminate;//TForm(Owner).Release;//raise Exception.CreateRes(@rsAutoLogonFail);
      end;
    end;
  except
    if csDesigning in ComponentState then                            { at design time... }
      Application.HandleException(Self)                { let Delphi handle the exception }
    else raise;                                                     { otherwise, reraise }
  end;
end;

//TabSecurityTemplate******************************************************

destructor TabSecurityTemplate.Destroy;//Leaks
begin
  ClearTemplate;
  inherited;
end;

//v1.4
function TabSecurityTemplate.DeleteKey(sKey: string): boolean;
var
  Reg: TRegistry;
begin
  Reg:= TRegistry.Create;
  try
    Result := Reg.DeleteKey(sKey);
  finally
    Reg.Destroy;
  end;
end;

procedure TabSecurityTemplate.LoadFromStreamSecurity(Stream: TMemoryStream);
var
  i: integer;
  lpNodeData: PNodeData;
  iDWord: DWORD;
  Version: TVersion;
begin
  Stream.Position := 0;
  Stream.ReadBuffer(Version, sizeof(Version));
  if (Version.Major <> C_VERSION.Major) or
     (Version.Minor <> C_VERSION.Minor) or
     (Version.MinorMinor <> C_VERSION.MinorMinor) or
     (Version.Patch <> C_VERSION.Patch) then
    raise Exception.Create(rsVersionMismatch);
  Stream.ReadBuffer(iDWord, sizeof(iDWord));
  Stream.Position := DWORD(Stream.Position) + iDWord * sizeof(TNodeData);
  LoadFromStream(Stream);
  Stream.Position := sizeof (C_VERSION) + sizeof(iDWord);
  for i:= 0 to Pred(iDWord) do
  begin
    new(lpNodeData);
    Stream.ReadBuffer(lpNodeData^, sizeof(TNodeData));
    Items[lpNodeData^.AdditionalInfo.iItemIndex].Data := lpNodeData;
    Items[lpNodeData^.AdditionalInfo.iItemIndex].ImageIndex := lpNodeData^.AdditionalInfo.iImageIndex;
    Items[lpNodeData^.AdditionalInfo.iItemIndex].SelectedIndex := lpNodeData^.AdditionalInfo.iImageIndex;
  end;
end;

procedure TabSecurityTemplate.SaveToStreamSecurity(Stream: TMemoryStream);
var
  i: integer;
  //lpNodeData: PNodeData;
  iDWord: DWORD;
begin
  Stream.position := 0;
  Stream.WriteBuffer(C_VERSION, sizeof(C_VERSION));
  iDWord := Items.Count;
  Stream.WriteBuffer(iDWord, sizeof(iDWord));
  for i:= 0 to Pred(Items.Count) do
  begin
    Items[i].Text := Trim(Items[i].Text);
    {lpNodeData^}PNodeData(Items[i].Data)^.AdditionalInfo.iItemIndex := i;
    {lpNodeData^}PNodeData(Items[i].Data)^.AdditionalInfo.iImageIndex := Items[i].ImageIndex;
    Stream.WriteBuffer(PNodeData(Items[i].Data)^{lpNodeData^}, sizeof(TNodeData));
  end;
  SaveToStream(Stream);
end;

function TabSecurityTemplate.GetPasswordFromUserTreeNode(tnUser: TTreeNode): string;
begin
  if Assigned(tnUser) then
    Result := PNodeData(tnUser.Item[0].Data)^.sCaption
  else
    Result := '';
end;


function TabSecurityTemplate.GetUserPassword(sUserName: string): string;
var
  i: integer;
begin
  if Assigned(Items[0]) then
  for i := 0 to Pred(Items.Count) do
    if Items[i].Level = 0 then
    if sUserName = Items[i].Text then
    begin
      Result := GetPasswordFromUserTreeNode(Items[i]);
      exit;
    end;
  Result := '';
end;


procedure TabSecurityTemplate.AddUserToUserTemplate(tvSourceTemplate: TabSecurityTemplate; sUserName, sPassword: string; bGrantAll: boolean);
var
  tnParent, tnPassword: TTreeNode;
  lpNodeData: PNodeData;
begin
  tnParent := Items.Add(nil, sUserName);
  new(lpNodeData);
  lpNodeData^.iId := 0;
  lpNodeData^.sCaption := '';//sUserName;//'pass';//For test only
  tnParent.Data := lpNodeData;
  tnParent.ImageIndex := 0;
  tnParent.SelectedIndex := 0;


  tnPassword := Items.AddChild(tnParent, 'Password(********)');
  new(lpNodeData);
  lpNodeData^.iId := 0;
  lpNodeData^.sCaption := sPassword;
  tnPassword.Data := lpNodeData;
  tnPassword.ImageIndex := 3;
  tnPassword.SelectedIndex := 3;

  tnParent := Items.AddChild(tnParent, 'Rights');
  new(lpNodeData);
  lpNodeData^.iId := 0;
  lpNodeData^.sCaption := '';
  tnParent.Data := lpNodeData;
  tnParent.ImageIndex := 1;
  tnParent.SelectedIndex := 1;

  if tvSourceTemplate.Items.Count <> 0 then
    BuildASecurityUser(tnParent, tvSourceTemplate.Items[0], bGrantAll);
end;


procedure TabSecurityTemplate.BuildASecurityUser(tnDest, tnSource: TTreeNode; bGrantAll: boolean);
var
  tnDestChild, tnSourceChild: TTreeNode;
  sText: string;
  lpNodeData: PNodeData;
begin
  tnSourceChild := tnSource.GetFirstChild;
  while Assigned(tnSourceChild) do
  begin
    if PNodeData(tnSourceChild.Data)^.iID = 0 then
      sText := {tnSourceChild.Text//}PNodeData(tnSourceChild.Data)^.sCaption
    else
    begin
      if bGrantAll then
        sText := {tnSourceChild.Text}PNodeData(tnSourceChild.Data)^.sCaption + C_YES
      else
        sText := {tnSourceChild.Text}PNodeData(tnSourceChild.Data)^.sCaption + C_NO;
    end;
    tnDestChild := Items.AddChild(tnDest, sText);

    new(lpNodeData);
    tnDestChild.Data := lpNodeData;
    PNodeData(tnDestChild.Data)^ := PNodeData(tnSourceChild.Data)^;
    PNodeData(tnDestChild.Data)^.bCheckOK := bGrantAll;

    tnDestChild.ImageIndex := tnSourceChild.ImageIndex;
    tnDestChild.SelectedIndex := tnSourceChild.SelectedIndex;

    BuildASecurityUser(tnDestChild, tnSourceChild, bGrantAll);
    tnSourceChild := tnSource.GetNextChild(tnSourceChild);
  end;
end;


function TabSecurityTemplate.GetUserNodeByUserName(sUserName: string): TTreeNode;
var
  i: integer;
begin
  if Items.count > 0 then
    for i := 0 to Pred(Items.Count) do
      if Items[i].Level = 0 then
        if sUserName = Items[i].Text then
        begin
          Result := Items[i];
          exit;
        end;
  Result := nil;
end;

function TabSecurityTemplate.GetUserNodeForNode(tn: TTreeNode): TTreeNode;
begin
  Result := tn;
  while Assigned(Result) and (Result.Level > 0) do
    Result := Result.Parent;
end;

function TabSecurityTemplate.GetUserNodeForSelected: TTreeNode;
begin
  Result := GetUserNodeForNode(Selected);
end;


procedure TabSecurityTemplate.DeleteNode(tnUserNode: TTreeNode);
var
  tnChild: TTreeNode;
begin
  tnChild := tnUserNode.GetFirstChild;
  while Assigned(tnChild) do
  begin
    if Integer(tnChild.Data) > 2048 then
    begin
      dispose(PNodeData(tnChild.Data));
      tnChild.Data := nil;
    end;
    DeleteNode(tnChild);

    tnChild := tnUserNode.GetFirstChild;//tnUserNode.GetNextChild(tnChild);
  end;
  if Integer(tnUserNode.Data) > 2048 then
    dispose(PNodeData(tnUserNode.Data));
  tnUserNode.Delete;
end;


function TabSecurityTemplate.GetNodeType(tn: TTreeNode; var sUser, sPassword: string): TNodeType;
begin
  if tn.Level = 0 then
  begin
    if tn.Text = C_SUPERVISOR_NAME then
    begin
      Result := ntSupervisor;
      sUser := C_SUPERVISOR_NAME;
      sPassword := PNodeData(tn.Data)^.sCaption;
    end
    else
    begin
      Result := ntUserRegular;
      sUser := tn.Text;
      sPassword := PNodeData(tn.Data)^.sCaption;
    end;
  end
  else if (tn.Level = 1) and (not tn.HasChildren) then
  //else if (tn.Level = 1) and (not (Assigned(tn.Data) and (Integer(tn.Data) <= C_MAX_ID)) or ((Integer(tn.Data) >= C_MAX_ID) and (PNodeData(tn.Data)^.iID > 0))) then
  begin
    Result := ntPassword;
    sPassword := PNodeData(tn.Data)^.sCaption;
    sUser := tn.Parent.Text;
  end
  //else if not (tn.HasChildren) then
  else if PNodeData(tn.Data)^.iID > 0 then
  begin
    if GetUserNodeForNode(tn).Text = C_SUPERVISOR_NAME then
      Result := ntSupervisorLeave
    else
      Result := ntUserLeave;
  end
  else
    Result := ntIntermediate;
end;

function TabSecurityTemplate.GetNodeTypeSecurityTemplate(tn: TTreeNode): TNodeType;
begin
  if not Assigned(tn) then
    Result := ntNone
  else if tn.Level = 0 then
    Result := ntUserRegular
  else if PNodeData(tn.Data)^.iID > 0 then
    Result := ntUserLeave
  else
    Result := ntIntermediate;
end;


function TabSecurityTemplate.GetSelectedNodeType(var sUser, sPassword: string): TNodeType;
begin
  if Assigned(Selected) then
    Result := GetNodeType(Selected, sUser, sPassword)
  else
    Result := ntNone;
end;


procedure TabSecurityTemplate.ClearTemplate;
var
  i: integer;
begin
  for i:= Pred(Items.Count) downto 0 do
    //if Integer(Items[i].Data) > 2048 then
      dispose(PNodeData(Items[i].Data));
  Items.Clear;
end;

function TabSecurityTemplate.GenerateSecurityId: integer;
var
  i: integer;
begin
  Result := 0;
  for i:= Pred(Items.Count) downto 0 do
    if Result < PNodeData(Items[i].Data)^.iId then
      Result := PNodeData(Items[i].Data)^.iId;
  inc(Result);
end;

function TabSecurityTemplate.IsValidSecurityId(iID: integer): boolean;
var
  i: integer;
begin
  for i:= Pred(Items.Count) downto 0 do
    if iID = PNodeData(Items[i].Data)^.iId then
    begin
      Result := false;
      exit;
    end;
  Result := true
end;

//Class TabSecurityDialogs
constructor TabSecurityDialogs.Create{(AOwner: TComponent)};
begin
  inherited;
  (*
  ffrUserTemplate := TForm.Create(nil);
  ffrUserTemplate.Align := alClient;
  ftvUserTemplate := TabSecurityTemplate.Create(nil);
  ftvUserTemplate.Parent := ffrUserTemplate;
  ftvUserTemplate.Align := alClient;
  *)
end;

destructor TabSecurityDialogs.Destroy;
begin
  inherited;
  (*
  ftvUserTemplate.Destroy;
  ffrUserTemplate.Destroy;
  *)
end;


function TabSecurityDialogs.Init(var sLogonName: string; var sPassword: string; bTrySilentInit: boolean; bRestrictToCheckOK: boolean): boolean;
var
  dlgPassword: TDlgPassword;

  procedure LoggedOn;
  begin
    Result := true;
    sLogonName := dlgPassword.edtUserName.Text;
    fSecurity.AddInLastUser(dlgPassword.edtUserName.Text);
    sPassword := dlgPassword.edtPassword.Text;
  end;

begin
  dlgPassword := TDlgPassword.Create(nil);
  try
    dlgPassword.fbRestrictToCheckOK := bRestrictToCheckOK;
    if length(sLogonName) > 0 then
      dlgPassword.edtUserName.Text := sLogonName
    else if fSecurity.flstLastUser.Count > 0 then
      dlgPassword.edtUserName.Text := fSecurity.flstLastUser[Pred(fSecurity.flstLastUser.Count)]
    else
      dlgPassword.edtUserName.Text := C_SUPERVISOR_NAME;
    dlgPassword.edtPassword.Text := sPassword;

    dlgPassword.ftvUserTemplate := ftvUserTemplate;
    dlgPassword.ftvSecurityTemplate := ftvSecurityTemplate;
    if bTrySilentInit then
      if dlgPassword.CheckUserPassword then
      begin
        LoggedOn;
        exit;
      end;

    dlgPassword.edtUserName.Items.Assign(fSecurity.flstLastUser);
    //v.1.5
    case dlgPassword.ShowModal of
    mrOK:  LoggedOn;
    mrCancel:
    begin
      if fSecurity.fbLoggedOn then
      begin
       if mrNo = MessageDlg('Do you want to keep the current logon? If no, all protected functionality will be disabled.', mtConfirmation, [mbYes, mbNo], 0) then
         Result := false
       else
       begin
         sLogonName := fSecurity.fsCurrentUser;
         //sPassword := fSecurity.fsPassword;
         Result := true;
       end;
      end
      else
        Result := false;
    end;
    else
      Result := false;
    end;
  finally
    dlgPassword.Destroy;
  end;
end;


procedure TabSecurityDialogs.SetSecurityObject(SecurityObject: TabSecurity);
begin
  fSecurity := SecurityObject;
  ftvUserTemplate := SecurityObject.UserTemplate;
  ftvSecurityTemplate := SecurityObject.ftvSecurityTemplate;

  if ftvUserTemplate.Items.Count = 0 then
    if soCreateSupervisorIfMissing in SecurityObject.Options then
      ftvUserTemplate.AddUserToUserTemplate(ftvSecurityTemplate, 'Supervisor', '', true);//Test only
end;

end.

