unit qu_sec;

{******************************************************}
{ Derek Agar                                           }
{ Copyright 1998 Rochester College                     }
{                                                      }
{******************************************************}


{Some parts taken from:
{*******************************************************}
{                                                       }
{         Delphi VCL Extensions (RX)                    }
{                                                       }
{         Copyright (c) 1996 AO ROSNO                   }
{         Copyright (c) 1997 Master-Bank                }
{                                                       }
{*******************************************************}


{$N+,P+,S-}

interface

uses SysUtils, Classes, Controls, Messages,
  {$IFDEF WIN32} Bde, {$ELSE} DbiTypes, DbiProcs, DbiErrs,
  {$ENDIF} DB, DBTables, WinTypes, DBSecur, StrUtils, EZDSLBar;

type
	TSecurityQuery = class(TQuery)
	private
		{FAlternateSQL : TStrings;}
		FDBSecurity : TDBSecurity;
		FCheckOnlyOnFirstOpen,
      {this is used to determine if the security info was already retreived before}
      {this is useful if a query gets opened multiple times like in a detail relationship}
      FOpenedAlready,
		FEnableDelete,
		FEnableSelect,
		FEnableInsert,
		FenableUpdate,
		FUpdColumns,
		{if true will only set the initial fields to non readonly for the insert
		 else will set all fields to non readonly for insert,
		 IF THE USER HAS INSERT PERMISSION!}
		FSetIniFldPropOnInsert,
		{used to figure out wether we need to set the field properties back
		 after a post}
		FJustInserted : boolean;
		FTableName : string;
		FInitFldProp,
		FPermissionFldProp : TBooleanArray;
      procedure GetSecurityInfo;
	protected
	    procedure DoBeforeOpen; override;
		 procedure DoBeforeDelete; override;
		 procedure DoBeforeInsert; override;
		 procedure DoBeforeEdit; override;
		 procedure DoAfterPost; override;
	public
    {constructor Create(AOwner: TComponent); override;}
    destructor Destroy; override;
	published
		{property AlternateSQL : TStrings read FAlternateSQL write FAlternateSQL;}
      property AllEditableOnInsert : boolean read FSetIniFldPropOnInsert
         write FSetIniFldPropOnInsert;
		property CheckOnlyOnFirstOpen : Boolean read FCheckOnlyOnFirstOpen
			write FCheckOnlyOnFirstOpen;
		property DBSecurity : TDBSecurity read FDBSecurity write FDBSecurity;
	end;

procedure register;
implementation

procedure TSecurityQuery.GetSecurityInfo;
var iLines, iWordCount, iCount, iFld  : integer;
sLine, sWord : string;
begin
    { Search for From clause }
	iLines := 0;
	while iLines <= sql.count do begin
      sline:= sql[iLines];
		iWordCount := WordCount(sline,[' ']);
		For iCount := 0 To  iWordCount Do Begin
			sword := extractword(iCount,sline,[' ']);
		 	if sword = 'from' then break;
		End;

    	if sWord='from' then begin
			{if it is the last word in the string then get the next line}
			if iCount = iWordCount then begin
				{check to see if there is another line}
				if iLines < sql.count-1 then begin
					inc(iLines);
      			sline:= sql[iLines];
					FTableName := extractword(1,sline,[' ']);
					break;
				end
				else begin	{if iLines < sql.count-1 then begin}
					FTableName := ''; {there are no more lines}
					break;
				end; {else} {if iLines < sql.count-1 then begin}
         end   {if iCount = iWordCount then begin}
         else begin
            FTableName := extractword(iCount+1,sline,[' ']);
            break;
         end;  {else}   {if iCount = iWordCount then begin}
      end;  {if sWord='FROM' then begin}

		inc(iLines);
	end; {while iLines <= sql.count-1 do begin}

	{now if the table name is set, we will check the permissions}
	With FDBSecurity Do Begin
		if (FTableName > '') and (TableExists(FTableName)) then begin
			FEnableSelect := TableSelect;
			FEnableDelete := TableDelete;
			FEnableInsert := TableInsert;
			FEnableUpdate := TableUpdate;
			FUpdColumns := TableUpdateColumns;
		end
		else begin
			FEnableSelect := false;
			FEnableDelete := false;
			FEnableInsert := false;
			FEnableUpdate := false;
			FUpdColumns := false;
		end;
	end;

   if FUpdColumns then begin
      {if not created, then create the boolean array, else resize}
      if FInitFldProp = nil then begin
		   FInitFldProp := TBooleanArray.Create(FieldCount);
		   FPermissionFldProp := TBooleanArray.Create(FieldCount);
      end
      else begin
         FInitFldProp.Capacity := FieldCount;
         FPermissionFldProp.Capacity := FieldCount;
      end;

      for iFld := 0 to FieldCount-1 do begin
         FInitFldProp.Flag[iFld] := Fields[iFld].readonly;
         {the columns security must be in the form of owner.tablename.columnname or
         tablename.columnname, consistent with how it is loaded}
         FPermissionFldProp.Flag[iFld] :=
            not (FDBSecurity.ColumnExists(FTableName+'.'+Fields[iFld].FieldName));
			{if the user has update permission on the column, then mark readonly as false}
			Fields[iFld].readonly := FPermissionFldProp.Flag[iFld];
      end;
   end;
end;


{constructor TSecurityQuery.Create(AOwner: TComponent);
begin
  	inherited Create(AOwner);
	{check to see if we can check security first}
	{if not (csDesigning in ComponentState) or
      (FCheckSecurityOnCreate) or (FDBSecurity <> nil) then GetSecurityInfo;}
{end;}

{*******************************************************************************
   TSecurityQuery.DoBeforeDelete - 6/18/1998 11:25am
 By:  Derek Agar
 Rochester College
 Description: checks to see if the user can delete before allowing the delete to happen

********************************************************************************}
Procedure TSecurityQuery.DoBeforeDelete;
Begin
  	if FEnableDelete then
  		inherited DoBeforeDelete
	else
    SysUtils.Abort;
End; {TSecurityQuery.DoBeforeDelete}

{*******************************************************************************
   TSecurityQuery.DoBeforeOpen - 6/18/1998 11:31am
 By:  Derek Agar
 Rochester College
 Description:	checks to see if the user can select before allowing the query to open

********************************************************************************}
Procedure TSecurityQuery.DoBeforeOpen;
Begin
   if ((not FOpenedAlready) or (not FCheckOnlyOnFirstOpen))
      and (FDBSecurity <> nil) then GetSecurityInfo;
  	if (FEnableSelect) or (csDesigning in ComponentState) then
  		inherited DoBeforeOpen
	else
    SysUtils.Abort;
End; {TSecurityQuery.DoBeforeOpen}


{*******************************************************************************
   TSecurityQuery.DoBeforeInsert - 6/18/1998 11:42am
 By:  Derek Agar
 Rochester College
 Description:	Checks to see if the user can insert before allowing the insert.
 If the user can insert, then will check wether to mark all fields as editable or
 only those fields that where not readonly at startup.

********************************************************************************}
Procedure TSecurityQuery.DoBeforeInsert;
var iFld : integer;
Begin
  	if FEnableInsert then begin
		{check to see if we need to change the readonly property on the fields}
		If ( FUpdColumns  ) Then Begin
      	if FSetIniFldPropOnInsert then begin
				For iFld := 0 To FieldCount - 1 Do Begin
					Fields[iFld].readonly := FInitFldProp.Flag[iFld];
				End;
			end
			else begin
				For iFld := 0 To  FieldCount - 1 Do Begin
					Fields[iFld].readonly := false;
				End;
			end;
			FJustInserted := true;
		end;	{If ( FUpdColumns  ) Then Begin			  }
		inherited DoBeforeInsert;
   end	{if FEnableInsert then begin}
	else
    SysUtils.Abort;
End; {TSecurityQuery.DoBeforeInsert}

{*******************************************************************************
   TSecurity.DoBeforeEdit - 6/18/1998 11:46am
 By:  Derek Agar
 Rochester College
 Description:	Checks to see if the user can update before allowing the query to edit.
 This is not reliant on the ReadOnly field as there is a possibility to have the
 permission to insert, but not edit.

********************************************************************************}
Procedure TSecurityQuery.DoBeforeEdit;
Begin
  	if FEnableUpdate or FUpdColumns then
  		inherited DoBeforeEdit
	else
    SysUtils.Abort;
End; {TSecurity.DoBeforeEdit}

{*******************************************************************************
   TSecurityQuery.DoAfterPost - 6/18/1998 3:52pm
 By:  Derek Agar
 Rochester College
 Description: resets the readonly attributes on the fields if just inserted

********************************************************************************}
Procedure TSecurityQuery.DoAfterPost;
var iFld :integer;
Begin
	{if we just inserted, then reset the fields}
	if FUpdColumns and FJustInserted then begin
      for iFld := 0 to FieldCount-1 do begin
			{if the user has update permission on the column, then mark readonly as false}
			Fields[iFld].readonly := FPermissionFldProp.Flag[iFld];
		end;
		{I set it here because FJustInserted does not matter if not FUpdColumns}
		FJustInserted := false;
	end;
	inherited DoAfterPost;
End; {TSecurityQuery.DoAfterPost}

destructor TSecurityQuery.destroy;
begin
   if FInitFldProp <> nil then FInitFldProp.free;
   if FPermissionFldProp <> nil then FPermissionFldProp.free;
	inherited destroy;
end;


procedure Register;
begin
	RegisterComponents('RX DBAware',[TSecurityQuery]);
end;

end.
