Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 83 additions & 8 deletions src/Horse.JWT.pas
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ interface
Horse.Commons;

type
TSkipWhen = reference to function(const APath: string; const AMethod: TMethodType): Boolean;
TSkipRouteMethod = record
Route: string;
Method: TMethodType;
constructor Create(const ARoute: string; AMethod: TMethodType);
end;

TSkipRouteMethods = TArray<TSkipRouteMethod>;
{$IF DEFINED(FPC)}
TOnResponse = {$IF DEFINED(HORSE_FPC_FUNCTIONREFERENCES)}reference to {$ENDIF}procedure(const AHorseResponse: THorseResponse; const AMessage: string; const AHTTPStatus: THTTPStatus; const APathInfo: string);
{$ELSE}
Expand All @@ -49,7 +57,10 @@ interface
function SkipRoutes: TArray<string>; overload;
function SkipRoutes(const ARoutes: TArray<string>): IHorseJWTConfig; overload;
function SkipRoutes(const ARoute: string): IHorseJWTConfig; overload;
function Header: string; overload;
function SkipWhen: TSkipWhen; overload;
function SkipWhen(const AValue: TSkipWhen): IHorseJWTConfig; overload;
function SkipRouteMethods: TSkipRouteMethods; overload;
function SkipRouteMethods(const AValue: TSkipRouteMethods): IHorseJWTConfig; overload; function Header: string; overload;
function Header(const AValue: string): IHorseJWTConfig; overload;
function IsRequiredSubject: Boolean; overload;
function IsRequiredSubject(const AValue: Boolean): IHorseJWTConfig; overload;
Expand All @@ -75,6 +86,8 @@ THorseJWTConfig = class(TInterfacedObject, IHorseJWTConfig)
private
FHeader: string;
FSkipRoutes: TArray<string>;
FSkipWhen: TSkipWhen;
FSkipRouteMethods: TSkipRouteMethods;
FIsRequireAudience: Boolean;
FExpectedAudience: TArray<string>;
FIsRequiredExpirationTime: Boolean;
Expand All @@ -86,6 +99,10 @@ THorseJWTConfig = class(TInterfacedObject, IHorseJWTConfig)
function SkipRoutes: TArray<string>; overload;
function SkipRoutes(const ARoutes: TArray<string>): IHorseJWTConfig; overload;
function SkipRoutes(const ARoute: string): IHorseJWTConfig; overload;
function SkipWhen: TSkipWhen; overload;
function SkipWhen(const AValue: TSkipWhen): IHorseJWTConfig; overload;
function SkipRouteMethods: TSkipRouteMethods; overload;
function SkipRouteMethods(const AValue: TSkipRouteMethods): IHorseJWTConfig; overload;
function Header: string; overload;
function Header(const AValue: string): IHorseJWTConfig; overload;
function IsRequiredSubject: Boolean; overload;
Expand Down Expand Up @@ -124,6 +141,12 @@ implementation
INVALID_AUTHORIZATION_TYPE = 'Invalid authorization type';
UNAUTHORIZED = 'Unauthorized';

constructor TSkipRouteMethod.Create(const ARoute: string; AMethod: TMethodType);
begin
Route := ARoute;
Method := AMethod;
end;

procedure Middleware(AHorseRequest: THorseRequest; AHorseResponse: THorseResponse; ANext: {$IF DEFINED(FPC)}TNextProc{$ELSE}TProc{$ENDIF}; const ASecretJWT: string; const AConfig: IHorseJWTConfig);
var
{$IF DEFINED(FPC)}
Expand Down Expand Up @@ -190,6 +213,26 @@ procedure Middleware(AHorseRequest: THorseRequest; AHorseResponse: THorseRespons
LPathInfo := AHorseRequest.RawWebRequest.PathInfo;
if LPathInfo = EmptyStr then
LPathInfo := '/';

for var I := 0 to High(LConfig.SkipRouteMethods) do
begin
if MatchRoute(LPathInfo, [LConfig.SkipRouteMethods[I].Route]) and
(AHorseRequest.MethodType = LConfig.SkipRouteMethods[I].Method) then
begin
ANext();
Exit;
end;
end;

if Assigned(LConfig.SkipWhen) then
begin
if LConfig.SkipWhen()(LPathInfo,AHorseRequest.MethodType) then
begin
ANext();
Exit;
end;
end;

if MatchRoute(LPathInfo, LConfig.SkipRoutes) then
begin
ANext();
Expand All @@ -210,7 +253,7 @@ procedure Middleware(AHorseRequest: THorseRequest; AHorseResponse: THorseRespons
LHeaderNormalize, LToken) then
begin
if Assigned(LConfig.OnResponse()) then
LConfig.OnResponse()(AHorseResponse, TOKEN_NOT_FOUND, THTTPStatus.Unauthorized,LPathInfo)
LConfig.OnResponse()(AHorseResponse, TOKEN_NOT_FOUND, THTTPStatus.Unauthorized, LPathInfo)
else
AHorseResponse.Send(TOKEN_NOT_FOUND).Status(THTTPStatus.Unauthorized);
raise EHorseCallbackInterrupted.Create(TOKEN_NOT_FOUND);
Expand All @@ -219,7 +262,7 @@ procedure Middleware(AHorseRequest: THorseRequest; AHorseResponse: THorseRespons
if Pos('bearer', LowerCase(LToken)) = 0 then
begin
if Assigned(LConfig.OnResponse()) then
LConfig.OnResponse()(AHorseResponse, INVALID_AUTHORIZATION_TYPE, THTTPStatus.Unauthorized,LPathInfo)
LConfig.OnResponse()(AHorseResponse, INVALID_AUTHORIZATION_TYPE, THTTPStatus.Unauthorized, LPathInfo)
else
AHorseResponse.Send(INVALID_AUTHORIZATION_TYPE).Status(THTTPStatus.Unauthorized);
raise EHorseCallbackInterrupted.Create(INVALID_AUTHORIZATION_TYPE);
Expand Down Expand Up @@ -248,7 +291,7 @@ procedure Middleware(AHorseRequest: THorseRequest; AHorseResponse: THorseRespons
LJWT := TJOSEContext.Create(LToken, TJWTClaims);
except
if Assigned(LConfig.OnResponse()) then
LConfig.OnResponse()(AHorseResponse, UNAUTHORIZED, THTTPStatus.Unauthorized,LPathInfo)
LConfig.OnResponse()(AHorseResponse, UNAUTHORIZED, THTTPStatus.Unauthorized, LPathInfo)
else
AHorseResponse.Send(UNAUTHORIZED).Status(THTTPStatus.Unauthorized);
raise EHorseCallbackInterrupted.Create(UNAUTHORIZED);
Expand All @@ -258,7 +301,7 @@ procedure Middleware(AHorseRequest: THorseRequest; AHorseResponse: THorseRespons
if LJWT.GetJOSEObject = nil then
begin
if Assigned(LConfig.OnResponse()) then
LConfig.OnResponse()(AHorseResponse, UNAUTHORIZED, THTTPStatus.Unauthorized,LPathInfo)
LConfig.OnResponse()(AHorseResponse, UNAUTHORIZED, THTTPStatus.Unauthorized, LPathInfo)
else
AHorseResponse.Send(UNAUTHORIZED).Status(THTTPStatus.Unauthorized);
raise EHorseCallbackInterrupted.Create(UNAUTHORIZED);
Expand Down Expand Up @@ -333,7 +376,7 @@ procedure Middleware(AHorseRequest: THorseRequest; AHorseResponse: THorseRespons
on E: Exception do
begin
if Assigned(LConfig.OnResponse()) then
LConfig.OnResponse()(AHorseResponse, UNAUTHORIZED, THTTPStatus.Unauthorized,LPathInfo)
LConfig.OnResponse()(AHorseResponse, UNAUTHORIZED, THTTPStatus.Unauthorized, LPathInfo)
else
AHorseResponse.Send(UNAUTHORIZED).Status(THTTPStatus.Unauthorized);
raise EHorseCallbackInterrupted.Create(UNAUTHORIZED);
Expand All @@ -348,7 +391,7 @@ procedure Middleware(AHorseRequest: THorseRequest; AHorseResponse: THorseRespons
on E: Exception do
begin
if Assigned(LConfig.OnResponse()) then
LConfig.OnResponse()(AHorseResponse, 'Invalid token authorization. ' + E.Message, THTTPStatus.Unauthorized,LPathInfo)
LConfig.OnResponse()(AHorseResponse, 'Invalid token authorization. ' + E.Message, THTTPStatus.Unauthorized, LPathInfo)
else
AHorseResponse.Send('Invalid token authorization. ' + E.Message).Status(THTTPStatus.Unauthorized);
raise EHorseCallbackInterrupted.Create;
Expand Down Expand Up @@ -503,6 +546,38 @@ function THorseJWTConfig.SkipRoutes(const ARoute: string): IHorseJWTConfig;
Result := SkipRoutes([ARoute]);
end;

function THorseJWTConfig.SkipWhen: TSkipWhen;
begin
Result := FSkipWhen;
end;

function THorseJWTConfig.SkipWhen(const AValue: TSkipWhen): IHorseJWTConfig;
begin
FSkipWhen := AValue;
Result := Self;
end;

function THorseJWTConfig.SkipRouteMethods: TSkipRouteMethods;
begin
Result := FSkipRouteMethods;
end;

function THorseJWTConfig.SkipRouteMethods(
const AValue: TSkipRouteMethods): IHorseJWTConfig;
var
I: Integer;
begin
FSkipRouteMethods := AValue;

// نرمال‌سازی مثل SkipRoutes
for I := 0 to High(FSkipRouteMethods) do
if (FSkipRouteMethods[I].Route <> '') and
(FSkipRouteMethods[I].Route[1] <> '/') then
FSkipRouteMethods[I].Route := '/' + FSkipRouteMethods[I].Route;

Result := Self;
end;

constructor THorseJWTConfig.Create;
begin
FHeader := 'authorization';
Expand All @@ -529,4 +604,4 @@ function THorseJWTConfig.OnResponse(const AValue: TOnResponse): IHorseJWTConfig;
Result := Self;
end;

end.
end.