Coverage report for Casbin.

Generated at 18/02/2019 12:10:18 by DelphiCodeCoverage - an open source tool for Delphi Code Coverage.

Statistics for Casbin.pas

Number of lines covered172
Number of lines with code gen187
Line coverage91%


1
// Copyright 2018 by John Kouraklis and Contributors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
unit Casbin;
15
16
interface
17
18
uses
19
  Casbin.Core.Base.Types, Casbin.Types, Casbin.Model.Types,
20
  Casbin.Adapter.Types, Casbin.Core.Logger.Types, Casbin.Functions.Types, Casbin.Policy.Types, System.TypInfo;
21
22
type
23
  TCasbin = class (TBaseInterfacedObject, ICasbin)
24
  private
25
    fModel: IModel;
26
    fPolicy: IPolicyManager;
27
    fLogger: ILogger;
28
    fEnabled: boolean;
29
    fFunctions: IFunctions;
30
31
    function rolesGsInternal(const Args: array of string): Boolean;
32
    function rolesG(const Args: array of string): Boolean;
33
    function rolesG2(const Args: array of string): Boolean;
34
  private
35
{$REGION 'Interface'}
36
    function getModel: IModel;
37
    function getPolicy: IPolicyManager;
38
    procedure setModel(const aValue: IModel);
39
    procedure setPolicy(const aValue: IPolicyManager);
40
    function getLogger: ILogger;
41
    procedure setLogger(const aValue: ILogger);
42
    function getEnabled: Boolean;
43
    procedure setEnabled(const aValue: Boolean);
44
45
    function enforce (const aParams: TEnforceParameters): boolean; overload;
46
    function enforce(const aParams: TEnforceParameters; const aPointer: PTypeInfo;
47
        const aRec): boolean; overload;
48
{$ENDREGION}
49
  public
50
    constructor Create; overload;
51
    constructor Create(const aModelFile, aPolicyFile: string); overload;  //PALOFF
52
    constructor Create(const aModel: IModel; const aPolicyAdapter: IPolicyManager);
53
        overload;
54
    constructor Create(const aModelFile: string; const aPolicyAdapter: IPolicyManager);
55
        overload;
56
    constructor Create(const aModel: IModel; const aPolicyFile: string);
57
        overload;
58
  end;
59
60
implementation
61
62
uses
63
  Casbin.Exception.Types, Casbin.Model, Casbin.Policy,
64
  Casbin.Core.Logger.Default, System.Generics.Collections, System.SysUtils,
65
  Casbin.Resolve, Casbin.Resolve.Types, Casbin.Model.Sections.Types,
66
  Casbin.Core.Utilities, System.Rtti, Casbin.Effect.Types, Casbin.Effect,
67
  Casbin.Functions, Casbin.Adapter.Memory, Casbin.Adapter.Memory.Policy, System.SyncObjs, System.Types, System.StrUtils, Casbin.Core.Defaults,
68
  ArrayHelper;
69
70
var
71
  criticalSection: TCriticalSection;
72
73
constructor TCasbin.Create(const aModelFile, aPolicyFile: string);
74
var
75
  model: IModel;
76
  policy: IPolicyManager;
77
begin
78
  if trim(aModelFile)='' then
79
    model:=TModel.Create(TMemoryAdapter.Create)
80
  else
81
    model:=TModel.Create(aModelFile);
82
83
  if Trim(aPolicyFile)='' then
84
    policy:=TPolicyManager.Create(TPolicyMemoryAdapter.Create)
85
  else
86
    policy:=TPolicyManager.Create(aPolicyFile);
87
88
  Create(model, policy);
89
end;
90
91
constructor TCasbin.Create(const aModel: IModel; const aPolicyAdapter:
92
    IPolicyManager);
93
begin
94
  if not Assigned(aModel) then
95
    raise ECasbinException.Create('Model Adapter is nil');
96
  if not Assigned(aPolicyAdapter) then
97
    raise ECasbinException.Create('Policy Manager is nil');
98
  inherited Create;
99
  fModel:=aModel;
100
  fPolicy:=aPolicyAdapter;
101
  fLogger:=TDefaultLogger.Create;
102
  fEnabled:=True;
103
  fFunctions:=TFunctions.Create;
104
  fFunctions.registerFunction('g', rolesG);
105
  fFunctions.registerFunction('g2', rolesG2);
106
end;
107
108
function TCasbin.enforce(const aParams: TEnforceParameters): boolean;
109
var
110
  rec: string;
111
begin
112
  Result:=enforce(aParams, nil, rec);
113
end;
114
115
constructor TCasbin.Create;
116
begin
117
  Create(TModel.Create(TMemoryAdapter.Create), TPolicyManager.Create(
118
                                                  TPolicyMemoryAdapter.Create));
119
end;
120
121
constructor TCasbin.Create(const aModelFile: string;
122
  const aPolicyAdapter: IPolicyManager);
123
var
124
  model: IModel;
125
begin
126
  if trim(aModelFile)='' then
127
    model:=TModel.Create(TMemoryAdapter.Create)
128
  else
129
    model:=TModel.Create(aModelFile);
130
131
  Create(model, aPolicyAdapter);
132
end;
133
134
function TCasbin.enforce(const aParams: TEnforceParameters; const aPointer:
135
    PTypeInfo; const aRec): boolean;
136
var
137
  item: string;
138
  request: TList<string>;
139
  requestDict: TDictionary<string, string>;
140
  policyDict: TDictionary<string, string>;
141
  requestStr: string;
142
  matcherResult: TEffectResult;
143
  policyList: TList<string>;
144
  effectArray: TEffectArray;
145
  matchString: string;
146
  reqDomain: string;
147
  domainsArrayRec: TArrayRecord<string>;
148
  requestArrayRec: TArrayRecord<string>;
149
  ctx: TRttiContext;
150
  cType: TRttiType;
151
  cField: TRttiField;
152
  abacList: TList<string>;
153
begin
154
  result:=true;
155
  if Length(aParams) = 0 then
156
    Exit;
157
  if not fEnabled then
158
    Exit;
159
160
  criticalSection.Acquire;
161
  try
162
    requestArrayRec:=TArrayRecord<string>.Create(aParams);
163
164
    request:=TList<string>.Create;
165
    requestArrayRec.List(request);
166
167
    requestStr:=string.Join(',', aParams);
168
169
    fLogger.log('Enforcing request '''+requestStr+'''');
170
171
    fLogger.log('   Resolving Request...');
172
173
    // Resolve Request
174
  {$IFDEF DEBUG}
175
    fLogger.log('   Request: '+requestStr);
176
    fLogger.log('      Assertions: ');
177
    if fModel.assertions(stRequestDefinition).Count=0 then
178
      fLogger.log('         No Request Assertions found')
179
    else
180
      for item in fModel.assertions(stRequestDefinition) do
181
        fLogger.log('         '+item);
182
  {$ENDIF}
183
    requestDict:=resolve(request, rtRequest,
184
                            fModel.assertions(stRequestDefinition));
185
186
    // Resolve ABAC record
187
    abacList:=TList<string>.Create;
188
    if Assigned(aPointer) and Assigned(@aRec) then
189
    begin
190
      fLogger.log('Record Identified');
191
      ctx:=TRttiContext.Create;
192
      cType:=ctx.GetType(aPointer);
193
194
      if fModel.assertions(stRequestDefinition).Count>0 then
195
      begin
196
        abacList.AddRange(fModel.assertions(stRequestDefinition));
197
        fLogger.log('Request identifiers retrieved ('+string.Join(',', abacList.ToArray)+')');
198
      end
199
      else
200
      begin
201
        // This assumes the request uses the letter 'r' and typical 'sub,obj,act'
202
        abacList.Add('r.sub');
203
        abacList.Add('r.obj');
204
        abacList.Add('r.act');
205
        fLogger.log('Default identifiers used (r)');
206
      end;
207
208
      fLogger.log('Retrieving content of '+cType.Name+' record');
209
      for cField in cType.GetFields do
210
      begin
211
        for item in abacList do
212
        begin
213
          requestDict.Add(UpperCase(item)+'.'+UpperCase(cField.Name),
214
                          UpperCase(cField.GetValue(@aRec).AsString));
215
        end;
216
      end;
217
    end;
218
219
    fLogger.log('   Resolving Policies...');
220
221
  {$IFDEF DEBUG}
222
    fLogger.log('   Policies: ');
223
    fLogger.log('      Assertions: ');
224
    if fPolicy.policies.Count=0 then
225
      fLogger.log('         No Policy Assertions found')
226
    else
227
      for item in fPolicy.policies do
228
        fLogger.log('         '+item);
229
230
    fLogger.log('      Assertions: '+requestStr);
231
    for item in fModel.assertions(stPolicyDefinition) do
232
      fLogger.log('         '+item);
233
  {$ENDIF}
234
235
  {$IFDEF DEBUG}
236
    fLogger.log('   Matchers: '+requestStr);
237
    fLogger.log('      Assertions: ');
238
    if fModel.assertions(stMatchers).Count=0 then
239
      fLogger.log('         No Matcher Assertions found')
240
    else
241
      for item in fModel.assertions(stMatchers) do
242
        fLogger.log('         '+item);
243
  {$ENDIF}
244
    if fModel.assertions(stMatchers).Count>0 then
245
    begin
246
      matchString:=fModel.assertions(stMatchers).Items[0];
247
248
      // Check for builtin accounts
249
      for item in builtinAccounts do
250
        if matchString.Contains(item) and requestStr.Contains(item) then
251
          Exit;
252
253
    end
254
    else
255
      matchString:='';
256
257
    domainsArrayRec:=TArrayRecord<string>.Create(fPolicy.domains.ToArray);
258
    for item in fPolicy.policies do
259
    begin
260
      fLogger.log('   Processing policy: '+item);
261
      // Resolve Policy
262
      policyList:=TList<string>.Create;   //PALOFF
263
      policyList.AddRange(item.Split([',']));     //PALOFF
264
265
      // Item 0 has p,g, etc
266
      policyList.Delete(0);
267
      // We look at the relevant policies only
268
      // by working out the domains
269
      reqDomain:=DefaultDomain;
270
      domainsArrayRec.ForEach(procedure(var Value: string; Index: integer)
271
                              var
272
                                item: string;
273
                              begin
274
                                for item in policyList do
275
                                  if Trim(Value) = Trim(item) then
276
                                  begin
277
                                    reqDomain:=Trim(Value);
278
                                    Break;
279
                                  end;
280
                              end);
281
282
      if fPolicy.linkExists(request[0], reqDomain, policyList[0]) or
283
        soundexSimilar(Trim(request[0]), Trim(policyList[0]),
284
                                        Trunc(0.50 * Length(request[0]))) then
285
      begin
286
        policyDict:=resolve(policyList, rtPolicy,
287
                              fModel.assertions(stPolicyDefinition));
288
289
        fLogger.log('   Resolving Functions and Matcher...');
290
291
        // Resolve Matcher
292
        if string.Compare('indeterminate', Trim(policyList[policyList.Count-1]),
293
                                                    [coIgnoreCase])=0 then
294
          matcherResult:=erIndeterminate
295
        else
296
          if matchString<>'' then
297
            matcherResult:=resolve(requestDict, policyDict, fFunctions, matchString)
298
          else
299
            matcherResult:=erIndeterminate;
300
        SetLength(effectArray, Length(effectArray)+1);
301
        effectArray[Length(effectArray)-1]:=matcherResult; //PALOFF
302
303
        policyDict.Free;
304
        end;
305
      policyList.Free;
306
    end;
307
308
    //Resolve Effector
309
    fLogger.log('   Merging effects...');
310
311
    Result:=mergeEffects(fModel.effectCondition, effectArray);
312
313
    fLogger.log('Enforcement completed (Result: '+BoolToStr(Result, true)+')');
314
315
    abacList.Free;
316
    request.Free;
317
    requestDict.Free;
318
319
  finally
320
    criticalSection.Release;
321
  end;
322
end;
323
324
{ TCasbin }
325
326
function TCasbin.getEnabled: Boolean;
327
begin
328
  Result:=fEnabled;
329
end;
330
331
function TCasbin.getLogger: ILogger;
332
begin
333
  Result:=fLogger;
334
end;
335
336
function TCasbin.getModel: IModel;
337
begin
338
  Result:=fModel;
339
end;
340
341
function TCasbin.getPolicy: IPolicyManager;
342
begin
343
  Result:=fPolicy;
344
end;
345
346
function TCasbin.rolesG(const Args: array of string): Boolean;
347
begin
348
  Result:=rolesGsInternal(Args);
349
end;
350
351
function TCasbin.rolesG2(const Args: array of string): Boolean;
352
begin
353
  Result:=rolesGsInternal(Args);
354
end;
355
356
function TCasbin.rolesGsInternal(const Args: array of string): Boolean;
357
begin
358
  result:=False;
359
  if (Length(Args)<2) or (Length(Args)>3) then
360
    raise ECasbinException.Create('The arguments are different than expected in '+
361
                                    'g''s functions');
362
  if Length(Args)=3 then
363
    Result:=fPolicy.linkExists(Args[0], Args[2], Args[1]);
364
  if Length(Args)=2 then
365
    Result:=fPolicy.linkExists(Args[0], Args[1]);
366
end;
367
368
procedure TCasbin.setEnabled(const aValue: Boolean);
369
begin
370
  fEnabled:=aValue;
371
end;
372
373
procedure TCasbin.setLogger(const aValue: ILogger);
374
begin
375
  fLogger:=nil;
376
  if Assigned(aValue) then
377
    fLogger:=aValue
378
  else
379
    fLogger:=TDefaultLogger.Create;
380
end;
381
382
procedure TCasbin.setModel(const aValue: IModel);
383
begin
384
  if not Assigned(aValue) then
385
    raise ECasbinException.Create('Model in nil');
386
  fModel:=aValue;
387
end;
388
389
procedure TCasbin.setPolicy(const aValue: IPolicyManager);
390
begin
391
  if not Assigned(aValue) then
392
    raise ECasbinException.Create('Policy Manager in nil');
393
  fPolicy:=aValue;
394
end;
395
396
constructor TCasbin.Create(const aModel: IModel; const aPolicyFile: string);
397
var
398
  policy: IPolicyManager;
399
begin
400
  if Trim(aPolicyFile)='' then
401
    policy:=TPolicyManager.Create(TPolicyMemoryAdapter.Create)
402
  else
403
    policy:=TPolicyManager.Create(aPolicyFile);
404
405
  Create(aModel, policy);
406
end;
407
408
initialization
409
  criticalSection:=TCriticalSection.Create;
410
411
finalization
412
  criticalSection.Free;
413
414
end.