horse icon indicating copy to clipboard operation
horse copied to clipboard

Any Changes

Open AndreaLai74 opened this issue 2 years ago • 5 comments

Hi Vinicius, I have made some changes to unit Horse.BasicAuthentication.pas . This change is necessary because if you deploy an apache module the "Req.Heades[Config.Header]" does not read any value. Another change that I have made is to extend the possibility of passing the THorseReq value to the Authentication procedure.

  I attach the code in this message if you can add it to the source code.

AndreaLai74 avatar Aug 03 '22 09:08 AndreaLai74

unit Horse.BasicAuthentication;

{$IF DEFINED(FPC)} {$MODE DELPHI}{$H+} {$ENDIF}

interface

uses {$IF DEFINED(FPC)} SysUtils, base64, Classes, {$ELSE} System.SysUtils, System.NetEncoding, System.Classes, {$ENDIF} Horse, Horse.Commons;

const AUTHORIZATION = 'Authorization'; REALM_MESSAGE = 'Enter credentials';

type IHorseBasicAuthenticationConfig = interface ['{DB16765F-156C-4BC1-8EDE-183CA9FE1985}'] function Header(const AValue: string): IHorseBasicAuthenticationConfig; overload; function Header: string; overload; function RealmMessage(const AValue: string): IHorseBasicAuthenticationConfig; overload; function RealmMessage: string; overload; function SkipRoutes(const AValues: TArray): IHorseBasicAuthenticationConfig; overload; function SkipRoutes(const AValue: string): IHorseBasicAuthenticationConfig; overload; function SkipRoutes: TArray; overload; end;

THorseBasicAuthenticationConfig = class(TInterfacedObject, IHorseBasicAuthenticationConfig) private FHeader: string; FRealmMessage: string; FSkipRoutes: TArray; function Header(const AValue: string): IHorseBasicAuthenticationConfig; overload; function Header: string; overload; function RealmMessage(const AValue: string): IHorseBasicAuthenticationConfig; overload; function RealmMessage: string; overload; function SkipRoutes(const AValues: TArray): IHorseBasicAuthenticationConfig; overload; function SkipRoutes(const AValue: string): IHorseBasicAuthenticationConfig; overload; function SkipRoutes: TArray; overload; public constructor Create; class function New: IHorseBasicAuthenticationConfig; end;

type THorseBasicAuthentication = {$IF NOT DEFINED(FPC)} reference to {$ENDIF} function(const AUsername, APassword: string; Req: THorseRequest): Boolean;

procedure Middleware(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINED(FPC)} TNextProc {$ELSE} TProc {$ENDIF}); function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication): THorseCallback; overload; function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication; Req: THorseRequest): THorseCallback; overload; function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication; const AConfig: IHorseBasicAuthenticationConfig): THorseCallback; overload;

implementation

var Config: IHorseBasicAuthenticationConfig; Authenticate: THorseBasicAuthentication;

function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication): THorseCallback; begin Result := HorseBasicAuthentication(AAuthenticate, THorseBasicAuthenticationConfig.New); end;

function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication; Req: THorseRequest): THorseCallback; begin Result := HorseBasicAuthentication(AAuthenticate, THorseBasicAuthenticationConfig.New); end;

function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication; const AConfig: IHorseBasicAuthenticationConfig): THorseCallback; begin Config := AConfig; Authenticate := AAuthenticate; Result := Middleware; end;

procedure Middleware(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINED(FPC)} TNextProc {$ELSE} TProc {$ENDIF}); const BASIC_AUTH = 'basic '; var LBasicAuthenticationEncode: string; LBase64String: string; LBasicAuthenticationDecode: TStringList; LIsAuthenticated: Boolean; LPathInfo: string; begin LPathInfo := Req.RawWebRequest.PathInfo; if LPathInfo = EmptyStr then LPathInfo := '/'; if MatchRoute(LPathInfo, Config.SkipRoutes) then begin Next(); Exit; end;

LBasicAuthenticationEncode := Req.Headers[Config.Header];

//* Modifica per provare a leggere il dato dalla RawWebRequest ..... if LBasicAuthenticationEncode.Trim.IsEmpty then LBasicAuthenticationEncode := Req.RawWebRequest.GetFieldByName(Config.Header);

if LBasicAuthenticationEncode.Trim.IsEmpty and not Req.Query.TryGetValue(Config.Header, LBasicAuthenticationEncode) then begin Res.Send('Authorization not found').Status(THTTPStatus.Unauthorized).RawWebResponse {$IF DEFINED(FPC)} .WWWAuthenticate := Format('Basic realm=%s', [Config.RealmMessage]); {$ELSE} .Realm := Config.RealmMessage; {$ENDIF} raise EHorseCallbackInterrupted.Create; end;

if not LBasicAuthenticationEncode.Trim.ToLower.StartsWith(BASIC_AUTH) then begin Res.Send('Invalid authorization type').Status(THTTPStatus.Unauthorized); raise EHorseCallbackInterrupted.Create; end;

LBasicAuthenticationDecode := TStringList.Create; try LBasicAuthenticationDecode.Delimiter := ':'; LBasicAuthenticationDecode.StrictDelimiter := True; LBase64String := LBasicAuthenticationEncode.Trim.Replace(BASIC_AUTH, '', [rfIgnoreCase]); LBasicAuthenticationDecode.DelimitedText := {$IF DEFINED(FPC)}DecodeStringBase64(LBase64String){$ELSE}TBase64Encoding.base64.Decode(LBase64String){$ENDIF};

try
  LIsAuthenticated := Authenticate(LBasicAuthenticationDecode.Strings[0], LBasicAuthenticationDecode.Strings[1], Req);
except
  on E: exception do
  begin
    Res.Send(E.Message).Status(THTTPStatus.InternalServerError);
    raise EHorseCallbackInterrupted.Create;
  end;
end;

finally LBasicAuthenticationDecode.Free; end;

if not LIsAuthenticated then begin Res.Send('Unauthorized').Status(THTTPStatus.Unauthorized); raise EHorseCallbackInterrupted.Create; end;

Next(); end;

{ THorseBasicAuthenticationConfig }

constructor THorseBasicAuthenticationConfig.Create; begin FHeader := AUTHORIZATION; FRealmMessage := REALM_MESSAGE; FSkipRoutes := []; end;

function THorseBasicAuthenticationConfig.Header: string; begin Result := FHeader; end;

function THorseBasicAuthenticationConfig.Header(const AValue: string): IHorseBasicAuthenticationConfig; begin FHeader := AValue; Result := Self; end;

class function THorseBasicAuthenticationConfig.New: IHorseBasicAuthenticationConfig; begin Result := THorseBasicAuthenticationConfig.Create; end;

function THorseBasicAuthenticationConfig.RealmMessage(const AValue: string): IHorseBasicAuthenticationConfig; begin FRealmMessage := AValue; Result := Self; end;

function THorseBasicAuthenticationConfig.RealmMessage: string; begin Result := FRealmMessage; end;

function THorseBasicAuthenticationConfig.SkipRoutes(const AValue: string): IHorseBasicAuthenticationConfig; begin Result := SkipRoutes([AValue]); end;

function THorseBasicAuthenticationConfig.SkipRoutes(const AValues: TArray): IHorseBasicAuthenticationConfig; var I: Integer; begin FSkipRoutes := AValues; for I := 0 to Pred(Length(FSkipRoutes)) do if Copy(Trim(FSkipRoutes[I]), 1, 1) <> '/' then FSkipRoutes[I] := '/' + FSkipRoutes[I]; Result := Self; end;

function THorseBasicAuthenticationConfig.SkipRoutes: TArray; begin Result := FSkipRoutes; end;

end.

AndreaLai74 avatar Aug 03 '22 09:08 AndreaLai74

An example, use the authorization method.

function DoBasicAuthentication(const Username, Password: string; Req: THorseRequest): Boolean; var Users: TServiceUsers; Config: TConfigLogin; begin Users := TServiceUsers.Create; try {$IFDEF HORSE_APACHE} var sx : String := Req.RawWebRequest.PathInfo; var sp : String := Req.RawWebRequest.PathTranslated; sp := sp.Replace('',''); sx := sx.Replace('',''); sp := sp.Replace(sx,'') + '/BEEAPI_CFG.ini';

  Users.fHomeSetDir := sp;
  Config.HomeSetDir := sp;
{$ENDIF}

var LoggingDIR : String := Config.LogDir;

WriteLOG('CALL : DoBasicAuthentication .... ', LoggingDIR {$IFDEF HORSE_APACHE}, 'libbee_api'{$ENDIF});

Users.ConnectDB;
Result := Users.IsValid(Username, Password);

finally Users.Free; end; end;

AndreaLai74 avatar Aug 03 '22 09:08 AndreaLai74

Hello @AndreaLai74 Couldn't you send a Pull Request to the middleware? Or send us the file to facilitate. Thanks

viniciussanchez avatar Aug 03 '22 11:08 viniciussanchez

Horse.BasicAuthentication.zip

Ok, I attach the file here.

AndreaLai74 avatar Aug 03 '22 14:08 AndreaLai74

Thanks

viniciussanchez avatar Aug 03 '22 14:08 viniciussanchez

@dliocode isso resolveria com o seu PR ne?

viniciussanchez avatar Dec 15 '22 19:12 viniciussanchez

sim

dliocode avatar Dec 15 '22 20:12 dliocode