Added functionality to allow the definition of OR connected Roles

This commit is contained in:
janidan 2017-05-16 18:34:39 +02:00
parent 256a629360
commit 6d9e584926
5 changed files with 106 additions and 19 deletions

View File

@ -149,9 +149,9 @@ object Form7: TForm7
end
object Memo1: TMemo
Left = 8
Top = 248
Top = 262
Width = 540
Height = 153
Height = 139
Anchors = [akLeft, akTop, akRight, akBottom]
Lines.Strings = (
'Memo1')
@ -181,11 +181,12 @@ object Form7: TForm7
Left = 8
Top = 51
Width = 131
Height = 59
Height = 72
ItemHeight = 13
Items.Strings = (
'user1:user1pass'
'user2:user2pass'
'user1_2:user1_2pass'
'user3:user3pass'
'admin:adminpass')
TabOrder = 6
@ -203,7 +204,7 @@ object Form7: TForm7
end
object Button5: TButton
Left = 311
Top = 196
Top = 229
Width = 237
Height = 27
Anchors = [akLeft, akTop, akRight]
@ -221,6 +222,16 @@ object Form7: TForm7
TabOrder = 9
OnClick = Button6Click
end
object Button7: TButton
Left = 311
Top = 196
Width = 237
Height = 27
Anchors = [akLeft, akTop, akRight]
Caption = 'Call action allowed only for "role1 or role2"'
TabOrder = 10
OnClick = Button7Click
end
object ApplicationEvents1: TApplicationEvents
OnIdle = ApplicationEvents1Idle
Left = 16

View File

@ -27,8 +27,10 @@ unit MainClientFormU;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.AppEvnts, MVCFramework.RESTClient,
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.AppEvnts,
MVCFramework.RESTClient,
Vcl.ExtCtrls;
type
@ -54,6 +56,7 @@ type
Button4: TButton;
Button5: TButton;
Button6: TButton;
Button7: TButton;
procedure FormCreate(Sender: TObject);
procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
procedure btnLogInLogOutClick(Sender: TObject);
@ -64,6 +67,7 @@ type
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button7Click(Sender: TObject);
private
FRESTClient: TRESTClient;
FLogoutUrl: string;
@ -85,7 +89,6 @@ uses
{$R *.dfm}
procedure TForm7.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
begin
if FRESTClient.SessionID.IsEmpty then
@ -116,7 +119,8 @@ begin
try
lJObj.AddPair('username', edtUsername.Text);
lJObj.AddPair('password', edtPassword.Text);
lRes := FRESTClient.doPOST('/system/users/logged', [], TSystemJSON.JSONValueToString(lJObj, False));
lRes := FRESTClient.doPOST('/system/users/logged', [],
TSystemJSON.JSONValueToString(lJObj, False));
if lRes.HasError then
begin
ShowMessage(lRes.Error.ExceptionMessage);
@ -186,13 +190,18 @@ begin
FillMemo(lRes);
end;
procedure TForm7.Button7Click(Sender: TObject);
var
lRes: IRESTResponse;
begin
lRes := FRESTClient.doGET('/private/role1or2', []);
FillMemo(lRes);
end;
procedure TForm7.FillMemo(Response: IRESTResponse);
begin
Memo1.Lines.Add(
Format('[%s] [%s] %s',
[TimeToStr(Time),
Response.ResponseText,
Response.BodyAsString]));
Memo1.Lines.Add(Format('[%s] [%s] %s', [TimeToStr(Time),
Response.ResponseText, Response.BodyAsString]));
end;
procedure TForm7.FormCreate(Sender: TObject);

View File

@ -63,8 +63,15 @@ type
[MVCHTTPMethod([httpGET])]
[MVCRequiresRole('role1')]
[MVCRequiresRole('role2')]
// The following is the same as above (redundancy for the example)
[MVCRequiresRole('role1;role2', MVCRoleEval.reAND)]
procedure OnlyRole1And2;
[MVCPath('/role1or2')]
[MVCHTTPMethod([httpGET])]
[MVCRequiresRole('role1;role2', MVCRoleEval.reOR)]
procedure OnlyRole1or2;
[MVCPath('/role/($role)')]
[MVCHTTPMethod([httpGET])]
[MVCRequiresRole('($role)')]
@ -75,7 +82,7 @@ implementation
procedure TPrivateController.AccessThisByRole(const role: string);
begin
Render('OK This ressource was accessed by role: ' + role);
Render('OK This ressource was accessed by role: ' + role);
end;
procedure TPrivateController.AuthenticatedAction;
@ -99,6 +106,11 @@ begin
Render('OK from a "role1 and role2" action');
end;
procedure TPrivateController.OnlyRole1or2;
begin
Render('OK from a "role1 or role2" action');
end;
procedure TPrivateController.OnlyRole2;
begin
Render('OK from a "role2" action');

View File

@ -76,11 +76,16 @@ begin
IsValid := True;
UserRoles.Add('role2');
end
else if (UserName = 'user3') and (Password = 'user3pass') then
else if (UserName = 'user1_2') and (Password = 'user1_2pass') then
begin
IsValid := True;
UserRoles.Add('role1');
UserRoles.Add('role2');
end
else if (UserName = 'user3') and (Password = 'user3pass') then
begin
IsValid := True;
UserRoles.Add('role3');
end;
// if you dont have "roles" concept in your system, you can also avoid to use them and

View File

@ -36,12 +36,23 @@ uses
type
MVCRequiresAuthenticationAttribute = class(MVCBaseAttribute);
MVCRoleEval = (reOR, reAND);
MVCRequiresRoleAttribute = class(MVCRequiresAuthenticationAttribute)
public const
DefaultListSeparator = ';';
private
FRole: string;
FRoleEval: MVCRoleEval;
FListSep: Char;
public
constructor Create(const aRole: string);
property Role: string read FRole;
constructor Create(const aRole: string); overload;
constructor Create(const aRole: string;
const aRoleEval: MVCRoleEval); overload;
constructor Create(const aRole: string; const aRoleEval: MVCRoleEval;
const aListSep: Char); overload;
function GetRoles: TArray<string>;
property RoleEval: MVCRoleEval read FRoleEval;
end;
IMVCRoleBasedAuthenticationHandler = interface(IMVCAuthenticationHandler)
@ -115,9 +126,28 @@ uses
{ MVCRequiresRoleAttribute }
constructor MVCRequiresRoleAttribute.Create(const aRole: string);
begin
Self.Create(aRole, MVCRoleEval.reAND, DefaultListSeparator);
end;
constructor MVCRequiresRoleAttribute.Create(const aRole: string;
const aRoleEval: MVCRoleEval);
begin
Self.Create(aRole, aRoleEval, DefaultListSeparator);
end;
constructor MVCRequiresRoleAttribute.Create(const aRole: string;
const aRoleEval: MVCRoleEval; const aListSep: Char);
begin
inherited Create;
FRole := aRole;
FRoleEval := aRoleEval;
FListSep := aListSep;
end;
function MVCRequiresRoleAttribute.GetRoles: TArray<string>;
begin
Result := FRole.Split([FListSep]);
end;
{ TRoleBasedAuthHandler }
@ -127,6 +157,7 @@ function TRoleBasedAuthHandler.CheckUserRoles(const AContext: TWebContext;
const aRoleAttributes: TArray<MVCRequiresRoleAttribute>): Boolean;
var
vAttribute: MVCRequiresRoleAttribute;
vSingleRole: string;
begin
// By default we will say that you are good to go.
Result := True;
@ -135,8 +166,27 @@ begin
// All Attributes MUST match -> AND evaluation
for vAttribute in aRoleAttributes do
if not AUserRoles.Contains(ResolveRole(AContext, vAttribute.Role)) then
Exit(False);
// if not AUserRoles.Contains(ResolveRole(AContext, vAttribute.Role)) then
// Exit(False);
begin
if (vAttribute.RoleEval = MVCRoleEval.reAND) then
begin
for vSingleRole in vAttribute.GetRoles do
if not AUserRoles.Contains(ResolveRole(AContext, vSingleRole)) then
Exit(False);
end
else // OR evaluation
begin
// By default we assume we have not found the role.
Result := False;
for vSingleRole in vAttribute.GetRoles do
if AUserRoles.Contains(ResolveRole(AContext, vSingleRole)) then
Result := True;
// If one of the roles does not match we exit the check.
if not Result then
Exit;
end;
end;
end;
constructor TRoleBasedAuthHandler.Create;