mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
Added functionality to allow the definition of OR connected Roles
This commit is contained in:
parent
256a629360
commit
6d9e584926
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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');
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user