Skip to content

Commit d3513cc

Browse files
skip routes #8
1 parent 3dbe145 commit d3513cc

File tree

3 files changed

+116
-16
lines changed

3 files changed

+116
-16
lines changed

samples/delphi/Samples.dpr

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@ begin
1919

2020
// The default header for receiving credentials is "Authorization".
2121
// You can change, if necessary:
22-
// THorse.Use(HorseBasicAuthentication(MyCallbackValidation, 'X-My-Header-Authorization'));
22+
// THorse.Use(HorseBasicAuthentication(MyCallbackValidation, THorseBasicAuthenticationConfig.New.Header('X-My-Header-Authorization')));
23+
24+
// You can also ignore routes:
25+
// THorse.Use(HorseBasicAuthentication(MyCallbackValidation, THorseBasicAuthenticationConfig.New.SkipRoutes(['/ping'])));
2326

2427
THorse.Get('/ping',
2528
procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
2629
begin
2730
Res.Send('pong');
2831
end);
2932

30-
THorse.Listen(9000);
33+
THorse.Listen(9000,
34+
procedure(Horse: THorse)
35+
begin
36+
Writeln(Format('Server is runing on port %d', [Horse.Port]));
37+
end);
3138
end.

samples/lazarus/Console.lpr

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ procedure GetPing(Req: THorseRequest; Res: THorseResponse; Next: TNextProc);
1717

1818
procedure OnListen(Horse: THorse);
1919
begin
20-
Writeln(Format('Server is runing on %s:%d', [Horse.Host, Horse.Port]));
20+
Writeln(Format('Server is runing on port %d', [Horse.Port]));
2121
end;
2222

2323
function DoLogin(const AUsername, APassword: string): Boolean;
@@ -32,7 +32,10 @@ function DoLogin(const AUsername, APassword: string): Boolean;
3232

3333
// The default header for receiving credentials is "Authorization".
3434
// You can change, if necessary:
35-
// THorse.Use(HorseBasicAuthentication(MyCallbackValidation, 'X-My-Header-Authorization'));
35+
// THorse.Use(HorseBasicAuthentication(MyCallbackValidation, THorseBasicAuthenticationConfig.New.Header('X-My-Header-Authorization')));
36+
37+
// You can also ignore routes:
38+
// THorse.Use(HorseBasicAuthentication(MyCallbackValidation, THorseBasicAuthenticationConfig.New.SkipRoutes(['/ping'])));
3639

3740
THorse.Get('/ping', GetPing);
3841

src/Horse.BasicAuthentication.pas

Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,64 @@ interface
88

99
uses
1010
{$IF DEFINED(FPC)}
11-
SysUtils, base64, Classes,
11+
SysUtils, StrUtils, base64, Classes,
1212
{$ELSE}
13-
System.SysUtils, System.NetEncoding, System.Classes,
13+
System.SysUtils, System.NetEncoding, System.Classes, System.StrUtils,
1414
{$ENDIF}
1515
Horse, Horse.Commons;
1616

1717
const
1818
AUTHORIZATION = 'authorization';
19+
REALM_MESSAGE = 'Enter credentials';
20+
21+
type
22+
IHorseBasicAuthenticationConfig = interface
23+
['{DB16765F-156C-4BC1-8EDE-183CA9FE1985}']
24+
function Header(const AValue: string): IHorseBasicAuthenticationConfig; overload;
25+
function Header: string; overload;
26+
function RealmMessage(const AValue: string): IHorseBasicAuthenticationConfig; overload;
27+
function RealmMessage: string; overload;
28+
function SkipRoutes(const AValues: TArray<string>): IHorseBasicAuthenticationConfig; overload;
29+
function SkipRoutes: TArray<string>; overload;
30+
end;
31+
32+
THorseBasicAuthenticationConfig = class(TInterfacedObject, IHorseBasicAuthenticationConfig)
33+
private
34+
FHeader: string;
35+
FRealmMessage: string;
36+
FSkipRoutes: TArray<string>;
37+
function Header(const AValue: string): IHorseBasicAuthenticationConfig; overload;
38+
function Header: string; overload;
39+
function RealmMessage(const AValue: string): IHorseBasicAuthenticationConfig; overload;
40+
function RealmMessage: string; overload;
41+
function SkipRoutes(const AValues: TArray<string>): IHorseBasicAuthenticationConfig; overload;
42+
function SkipRoutes: TArray<string>; overload;
43+
public
44+
constructor Create;
45+
class function New: IHorseBasicAuthenticationConfig;
46+
end;
1947

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

2351
procedure Middleware(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINED(FPC)} TNextProc {$ELSE} TProc {$ENDIF} );
24-
function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication; const AHeader: string = AUTHORIZATION; const ARealmMessage: string = 'Enter credentials'): THorseCallback;
52+
function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication): THorseCallback; overload;
53+
function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication; const AConfig: IHorseBasicAuthenticationConfig): THorseCallback; overload;
2554

2655
implementation
2756

2857
var
29-
Header: string;
30-
RealmMessage: string;
58+
Config: IHorseBasicAuthenticationConfig;
3159
Authenticate: THorseBasicAuthentication;
3260

33-
function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication; const AHeader: string = AUTHORIZATION; const ARealmMessage: string = 'Enter credentials'): THorseCallback;
61+
function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication): THorseCallback;
3462
begin
35-
Header := AHeader;
36-
RealmMessage := ARealmMessage;
63+
Result := HorseBasicAuthentication(AAuthenticate, THorseBasicAuthenticationConfig.New);
64+
end;
65+
66+
function HorseBasicAuthentication(const AAuthenticate: THorseBasicAuthentication; const AConfig: IHorseBasicAuthenticationConfig): THorseCallback;
67+
begin
68+
Config := AConfig;
3769
Authenticate := AAuthenticate;
3870
Result := Middleware;
3971
end;
@@ -47,14 +79,20 @@ procedure Middleware(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINED
4779
LBasicAuthenticationDecode: TStringList;
4880
LIsAuthenticated: Boolean;
4981
begin
50-
LBasicAuthenticationEncode := Req.Headers[Header];
51-
if LBasicAuthenticationEncode.Trim.IsEmpty and not Req.Query.TryGetValue(Header, LBasicAuthenticationEncode) then
82+
if MatchText(Req.RawWebRequest.PathInfo, Config.SkipRoutes) then
83+
begin
84+
Next();
85+
Exit;
86+
end;
87+
88+
LBasicAuthenticationEncode := Req.Headers[Config.Header];
89+
if LBasicAuthenticationEncode.Trim.IsEmpty and not Req.Query.TryGetValue(Config.Header, LBasicAuthenticationEncode) then
5290
begin
5391
Res.Send('Authorization not found').Status(THTTPStatus.Unauthorized).RawWebResponse
5492
{$IF DEFINED(FPC)}
55-
.WWWAuthenticate := Format('Basic realm=%s', [RealmMessage]);
93+
.WWWAuthenticate := Format('Basic realm=%s', [Config.RealmMessage]);
5694
{$ELSE}
57-
.Realm := RealmMessage;
95+
.Realm := Config.RealmMessage;
5896
{$ENDIF}
5997
raise EHorseCallbackInterrupted.Create;
6098
end;
@@ -89,4 +127,56 @@ procedure Middleware(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINED
89127
Next();
90128
end;
91129

130+
{ THorseBasicAuthenticationConfig }
131+
132+
constructor THorseBasicAuthenticationConfig.Create;
133+
begin
134+
FHeader := AUTHORIZATION;
135+
FRealmMessage := REALM_MESSAGE;
136+
FSkipRoutes := [];
137+
end;
138+
139+
function THorseBasicAuthenticationConfig.Header: string;
140+
begin
141+
Result := FHeader;
142+
end;
143+
144+
function THorseBasicAuthenticationConfig.Header(const AValue: string): IHorseBasicAuthenticationConfig;
145+
begin
146+
FHeader := AValue;
147+
Result := Self;
148+
end;
149+
150+
class function THorseBasicAuthenticationConfig.New: IHorseBasicAuthenticationConfig;
151+
begin
152+
Result := THorseBasicAuthenticationConfig.Create;
153+
end;
154+
155+
function THorseBasicAuthenticationConfig.RealmMessage(const AValue: string): IHorseBasicAuthenticationConfig;
156+
begin
157+
FRealmMessage := AValue;
158+
Result := Self;
159+
end;
160+
161+
function THorseBasicAuthenticationConfig.RealmMessage: string;
162+
begin
163+
Result := FRealmMessage;
164+
end;
165+
166+
function THorseBasicAuthenticationConfig.SkipRoutes(const AValues: TArray<string>): IHorseBasicAuthenticationConfig;
167+
var
168+
I: Integer;
169+
begin
170+
FSkipRoutes := AValues;
171+
for I := 0 to Pred(Length(FSkipRoutes)) do
172+
if Copy(Trim(FSkipRoutes[I]), 1, 1) <> '/' then
173+
FSkipRoutes[I] := '/' + FSkipRoutes[I];
174+
Result := Self;
175+
end;
176+
177+
function THorseBasicAuthenticationConfig.SkipRoutes: TArray<string>;
178+
begin
179+
Result := FSkipRoutes;
180+
end;
181+
92182
end.

0 commit comments

Comments
 (0)