* Patch from Simon Ameis to actually implement parameter checking

git-svn-id: trunk@44142 -
This commit is contained in:
michael 2020-02-09 17:51:06 +00:00
parent 12d015a935
commit d295ee3427

View File

@ -54,7 +54,7 @@ Type
function GetP(AIndex : Integer): TJSONParamDef; function GetP(AIndex : Integer): TJSONParamDef;
procedure SetP(AIndex : Integer; const AValue: TJSONParamDef); procedure SetP(AIndex : Integer; const AValue: TJSONParamDef);
Public Public
Function AddParamDef(Const AName : TJSONStringType; AType : TJSONType = jtString) : TJSONParamDef; Function AddParamDef(Const AName : TJSONStringType; AType : TJSONType = jtString; ARequired: Boolean = False) : TJSONParamDef;
Function IndexOfParamDef(Const AName : TJSONStringType) : Integer; Function IndexOfParamDef(Const AName : TJSONStringType) : Integer;
Function FindParamDef(Const AName : TJSONStringType) : TJSONParamDef; Function FindParamDef(Const AName : TJSONStringType) : TJSONParamDef;
Function ParamDefByName(Const AName : TJSONStringType) : TJSONParamDef; Function ParamDefByName(Const AName : TJSONStringType) : TJSONParamDef;
@ -63,7 +63,7 @@ Type
{ TCustomJSONRPCHandler } { TCustomJSONRPCHandler }
TJSONParamErrorEvent = Procedure (Sender : TObject; Const E : Exception; Var Fatal : boolean) of Object; TJSONParamErrorEvent = Procedure (Sender : TObject; Const E : Exception; Var Fatal : boolean) of Object;
TJSONRPCOption = (jroCheckParams,jroObjectParams,jroArrayParams); TJSONRPCOption = (jroCheckParams,jroObjectParams,jroArrayParams,jroIgnoreExtraFields);
TJSONRPCOptions = set of TJSONRPCOption; TJSONRPCOptions = set of TJSONRPCOption;
{ TJSONRPCCallContext } { TJSONRPCCallContext }
@ -94,6 +94,8 @@ Type
Protected Protected
function CreateParamDefs: TJSONParamDefs; virtual; function CreateParamDefs: TJSONParamDefs; virtual;
Procedure DoCheckParams(Const Params : TJSONData); virtual; Procedure DoCheckParams(Const Params : TJSONData); virtual;
Procedure DoCheckParamDefsOnObject(Const ParamObject: TJSONObject); virtual;
Procedure DoCheckParamArray(const ParamArray: TJSONArray); virtual;
Function DoExecute(Const Params : TJSONData; AContext : TJSONRPCCallContext): TJSONData; virtual; Function DoExecute(Const Params : TJSONData; AContext : TJSONRPCCallContext): TJSONData; virtual;
Property BeforeExecute : TNotifyEvent Read FBeforeExecute Write FBeforeExecute; Property BeforeExecute : TNotifyEvent Read FBeforeExecute Write FBeforeExecute;
Property AfterExecute : TNotifyEvent Read FAfterExecute Write FAfterExecute; Property AfterExecute : TNotifyEvent Read FAfterExecute Write FAfterExecute;
@ -332,8 +334,10 @@ Type
TJSONErrorObject = Class(TJSONObject); TJSONErrorObject = Class(TJSONObject);
// Raise EJSONRPC exceptions. // Raise EJSONRPC exceptions.
Procedure JSONRPCError(Msg : String); Procedure JSONRPCError(const Msg : String);
Procedure JSONRPCError(Fmt : String; Args : Array of const); Procedure JSONRPCError(const Fmt : String; const Args : Array of const);
Procedure JSONRPCParamError(const Msg: String);
Procedure JSONRPCParamError(const Fmt: String; const Args: array of const);
// Create an 'Error' object for an error response. // Create an 'Error' object for an error response.
function CreateJSONErrorObject(Const AMessage : String; Const ACode : Integer) : TJSONObject; function CreateJSONErrorObject(Const AMessage : String; Const ACode : Integer) : TJSONObject;
@ -371,6 +375,10 @@ resourcestring
SErrParamsMustBeArrayorObject = 'Parameters must be passed in an object or an array.'; SErrParamsMustBeArrayorObject = 'Parameters must be passed in an object or an array.';
SErrParamsMustBeObject = 'Parameters must be passed in an object.'; SErrParamsMustBeObject = 'Parameters must be passed in an object.';
SErrParamsMustBeArray = 'Parameters must be passed in an array.'; SErrParamsMustBeArray = 'Parameters must be passed in an array.';
SErrParamsRequiredParamNotFound = 'Required parameter "%s" not found.';
SErrParamsDataTypeMismatch = 'Expected parameter "%s" having type "%s", got "%s".';
SErrParamsNotAllowd = 'Parameter "%s" is not allowed.';
SErrParamsOnlyObjectsInArray = 'Array elements must be objects, got %s at position %d.';
SErrRequestMustBeObject = 'JSON-RPC Request must be an object.'; SErrRequestMustBeObject = 'JSON-RPC Request must be an object.';
SErrNoIDProperty = 'No "id" property found in request.'; SErrNoIDProperty = 'No "id" property found in request.';
SErrInvalidIDProperty = 'Type of "id" property is not correct.'; SErrInvalidIDProperty = 'Type of "id" property is not correct.';
@ -402,13 +410,15 @@ implementation
uses dbugintf; uses dbugintf;
{$ENDIF} {$ENDIF}
function CreateJSONErrorObject(Const AMessage : String; Const ACode : Integer) : TJSONObject; function CreateJSONErrorObject(const AMessage: String; const ACode: Integer
): TJSONObject;
begin begin
Result:=TJSONErrorObject.Create(['code',ACode,'message',AMessage]) Result:=TJSONErrorObject.Create(['code',ACode,'message',AMessage])
end; end;
function CreateJSON2ErrorResponse(Const AMessage : String; Const ACode : Integer; ID : TJSONData = Nil; idname : TJSONStringType = 'id' ) : TJSONObject; function CreateJSON2ErrorResponse(const AMessage: String; const ACode: Integer;
ID: TJSONData; idname: TJSONStringType): TJSONObject;
begin begin
If (ID=Nil) then If (ID=Nil) then
@ -418,7 +428,8 @@ begin
Result:=TJSONErrorObject.Create(['jsonrpc','2.0','error',CreateJSONErrorObject(AMessage,ACode),idname,ID]); Result:=TJSONErrorObject.Create(['jsonrpc','2.0','error',CreateJSONErrorObject(AMessage,ACode),idname,ID]);
end; end;
function CreateJSON2ErrorResponse(Const AFormat : String; Args : Array of const; Const ACode : Integer; ID : TJSONData = Nil; idname : TJSONStringType = 'id' ) : TJSONObject; function CreateJSON2ErrorResponse(const AFormat: String; Args: array of const;
const ACode: Integer; ID: TJSONData; idname: TJSONStringType): TJSONObject;
begin begin
If (ID=Nil) then If (ID=Nil) then
@ -428,7 +439,7 @@ begin
Result:=TJSONErrorObject.Create(['jsonrpc','2.0','error',CreateJSONErrorObject(Format(AFormat,Args),ACode),idname,ID]); Result:=TJSONErrorObject.Create(['jsonrpc','2.0','error',CreateJSONErrorObject(Format(AFormat,Args),ACode),idname,ID]);
end; end;
Function CreateErrorForRequest(Const Req,Error : TJSONData) : TJSONData; function CreateErrorForRequest(const Req, Error: TJSONData): TJSONData;
Var Var
I : Integer; I : Integer;
@ -456,18 +467,29 @@ begin
JSONRPCHandlerManager:=TheHandler; JSONRPCHandlerManager:=TheHandler;
end; end;
Procedure JSONRPCError(Msg : String); procedure JSONRPCError(const Msg: String);
begin begin
Raise EJSONRPC.Create(Msg); Raise EJSONRPC.Create(Msg);
end; end;
Procedure JSONRPCError(Fmt : String; Args : Array of const); procedure JSONRPCError(const Fmt: String; const Args: array of const);
begin begin
Raise EJSONRPC.CreateFmt(Fmt,Args); Raise EJSONRPC.CreateFmt(Fmt,Args);
end; end;
procedure JSONRPCParamError(const Msg: String);
begin
raise EJSONRPC.CreateFmt(SErrParams, [Msg]);
end;
procedure JSONRPCParamError(const Fmt: String; const Args: array of const);
begin
raise EJSONRPC.CreateFmt(SErrParams, [Format(Fmt, Args)]);
end;
{ TJSONParamDef } { TJSONParamDef }
procedure TJSONParamDef.SetName(const AValue: TJSONStringType); procedure TJSONParamDef.SetName(const AValue: TJSONStringType);
@ -529,13 +551,14 @@ begin
Items[AIndex]:=AValue; Items[AIndex]:=AValue;
end; end;
function TJSONParamDefs.AddParamDef(const AName: TJSONStringType; AType: TJSONType function TJSONParamDefs.AddParamDef(const AName: TJSONStringType;
): TJSONParamDef; AType: TJSONType; ARequired: Boolean): TJSONParamDef;
begin begin
Result:=Add as TJSONParamDef; Result:=Add as TJSONParamDef;
try try
Result.Name:=AName; Result.Name:=AName;
Result.DataType:=Atype; Result.DataType:=Atype;
Result.Required:=ARequired;
except except
FReeAndNil(Result); FReeAndNil(Result);
Raise; Raise;
@ -626,10 +649,76 @@ end;
procedure TCustomJSONRPCHandler.DoCheckParams(const Params: TJSONData); procedure TCustomJSONRPCHandler.DoCheckParams(const Params: TJSONData);
begin begin
If (jroObjectParams in Options) and Not (Params is TJSONobject) then if (Params is TJSONObject) then
JSONRPCError(SErrParams,[SErrParamsMustBeObject]); begin
If (jroArrayParams in Options) and Not (Params is TJSONArray) then if (jroArrayParams in Options) then
JSONRPCError(SErrParams,[SErrParamsMustBeArray]); JSONRPCParamError(SErrParamsMustBeArray);
DoCheckParamDefsOnObject(Params as TJSONObject);
end else
if (Params is TJSONArray) then
begin
If (jroObjectParams in Options) then
JSONRPCParamError(SErrParamsMustBeArray);
DoCheckParamArray(Params as TJSONArray);
end;
end;
procedure TCustomJSONRPCHandler.DoCheckParamDefsOnObject(
const ParamObject: TJSONObject);
var
def: TJSONParamDef;
Param: TJSONData;
PropEnum: TJSONEnum;
begin
for TCollectionItem(def) in ParamDefs do
begin
// assert the typecast in for loop
Assert(def is TJSONParamDef,'Unexpected ParamDef item class.');
Param:=ParamObject.Find(def.Name);
// check required parameters
if not Assigned(Param) then
begin
if def.Required then
JSONRPCParamError(SErrParamsRequiredParamNotFound,[def.Name])
else
Continue;
end;
// jtUnkown accepts all data types
if (def.DataType<>jtUnknown) and not (Param.JSONType=def.DataType) then
JSONRPCParamError(SErrParamsDataTypeMismatch,[def.Name,JSONTypeName(def.DataType),JSONTypeName(Param.JSONType)]);
end;
// check if additional parameters are given
if not (jroIgnoreExtraFields in Options) then
begin
for PropEnum in ParamObject do
begin
// only check for name is required other specs are checked before
if ParamDefs.FindParamDef(PropEnum.Key)=nil then
JSONRPCParamError(SErrParamsNotAllowd,[PropEnum.Key]);
end;
end;
end;
procedure TCustomJSONRPCHandler.DoCheckParamArray(const ParamArray: TJSONArray);
var
element: TJSONEnum;
begin
for element in ParamArray do
begin
// check object parameters if objects given
if (element.Value.JSONType=jtObject) then
begin
DoCheckParamDefsOnObject(element.Value as TJSONObject);
end else
// not an object
if (jroObjectParams in Options) then
JSONRPCParamError(SErrParamsOnlyObjectsInArray,[JSONTypeName(element.Value.JSONType),element.KeyNum]);
end;
end; end;
function TCustomJSONRPCHandler.DoExecute(Const Params: TJSONData;AContext : TJSONRPCCallContext): TJSONData; function TCustomJSONRPCHandler.DoExecute(Const Params: TJSONData;AContext : TJSONRPCCallContext): TJSONData;