program SimpleAPIWithBasicAuthentication;
{$mode objfpc}{$H+}
{$define debug}
uses
base64,
fpjson,
httpprotocol,
BrookUtils,
BrookLogger,
BrookAction,
BrookHttpDefs,
BrookFCLHTTPAppBroker,
BrookApplication;
const
ValidUsername = 'root';
ValidPassword = 'admin';
type
{ TAPIAction }
TAPIAction = class(TBrookAction)
protected
FJSONResponse: TJSONObject;
public
procedure DoRequest(ARequest: TBrookRequest; AResponse: TBrookResponse); override;
end;
{ TAuthenticatedAction }
TAuthenticatedAction = class(TAPIAction)
private
function IsValidToken(const AToken: String): Boolean;
public
procedure Request(ARequest: TBrookRequest; AResponse: TBrookResponse); override;
end;
{ TEchoAction }
TEchoAction = class(TAuthenticatedAction)
procedure Get; override;
end;
{ TAPIAction }
procedure TAPIAction.DoRequest(ARequest: TBrookRequest;
AResponse: TBrookResponse);
var
Log: String;
begin
FJSONResponse := TJSONObject.Create;
try
inherited DoRequest(ARequest, AResponse);
// Every output is considered a log
Log := AResponse.Content;
if (Length(Log) > 0) and BrookSettings.LogActive then begin
TBrookLogger.Service.Info(Log);
end;
// Override content by JSON
AResponse.Content := FJSONResponse.{$ifndef debug}AsJSON{$else}FormatJSON{$endif};
finally
FJSONResponse.Free;
end;
end;
{ TAuthenticatedAction }
function TAuthenticatedAction.IsValidToken(const AToken: String): Boolean;
begin
Result := EncodeStringBase64(ValidUsername + ':' + ValidPassword) = AToken;
end;
procedure TAuthenticatedAction.Request(ARequest: TBrookRequest; AResponse: TBrookResponse);
var
AuthHeader, TokenPrefix, Token: String;
Authenticated: Boolean;
begin
AuthHeader := ARequest.GetHeader(hhAuthorization);
// I love System.Copy's "no error" behavior :)
TokenPrefix := Copy(AuthHeader,1,6);
Token := Copy(AuthHeader,7,Length(AuthHeader));
Authenticated := (TokenPrefix = 'Basic ') and IsValidToken(Token);
if Authenticated then
inherited Request(ARequest, AResponse)
else begin
AResponse.Code := 401;
AResponse.CodeText := 'Unauthorized';
AResponse.SetHeader(hhWWWAuthenticate,'Basic realm="API"');
FJSONResponse.Add('status',AResponse.Code);
FJSONResponse.Add('message',AResponse.CodeText);
end;
end;
{ TEchoAction }
procedure TEchoAction.Get;
var
Msg: String;
begin
Msg := HttpRequest.QueryFields.Values['message'];
if Length(Msg) > 0 then begin
FJSONResponse.Add('status',200);
FJSONResponse.Add('message',Msg);
end else begin
FJSONResponse.Add('status',400);
FJSONResponse.Add('message','Please provide "message" field');
end;
end;
begin
TEchoAction.Register('/echo');
BrookSettings.Port := 4444;
BrookApp.Run;
end.