@@ -8,32 +8,64 @@ interface
88
99uses
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
1717const
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
2048type
2149 THorseBasicAuthentication = { $IF NOT DEFINED(FPC)} reference to { $ENDIF} function(const AUsername, APassword: string): Boolean;
2250
2351procedure 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
2655implementation
2756
2857var
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;
3462begin
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;
3971end ;
@@ -47,14 +79,20 @@ procedure Middleware(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINED
4779 LBasicAuthenticationDecode: TStringList;
4880 LIsAuthenticated: Boolean;
4981begin
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();
90128end ;
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+
92182end .
0 commit comments