I am trying to implement authorization in a Delphi XE DataSnap application. I broke this down into a very simple example, but still do not see the effects of the TRoleAuth attribute for a method or class.
Here is a simple DSServerMethods class that includes the generated sample methods. The class has been decorated with the guest and anyone authorized roles, and the unwelcome denied role. The ReverseString method has been decorated with the readonly denied role:
type
[TRoleAuth('guest,anyone','unwelcome')]
TMyDSServerMethods = class(TDSServerModule)
DataSetProvider1: TDataSetProvider;
...
public
{ Public declarations }
function EchoString(Value: string): string;
[TRoleAuth('','readonly')]
function ReverseString(Value: string): string;
...
end;
I am assigning roles on the OnUserAuthenticate method. For example, I have a user for whom I am assigning the readonly role from OnUserAuthenticate, a role which I believe should deny that user permission to execute the ReverseString function.
From what I understand, my code should compare the user's roles against the EventObject.AuthorizedRoles and EventObject.DeniedRoles TStrings from the OnUserAuthorize method of the TDSAuthenticationManager, and set the valid formal parameter of this method accordingly.
Here is a simple OnUserAuthorize method I am using for tesing. When I step into it using the debugger in response to a user with the readonly role attempting to invoke ReverseString, EventObject.AuthorizedRoles and EventObject.DeniedRoles are both nil, and EventObject.Roles contains the readonly role.
procedure TServerContainer1.DSAuthenticationManager1UserAuthorize(
Sender: TObject; EventObject: TDSAuthorizeEventObject;
var valid: Boolean);
begin
outputdebugstring(PChar(Eventobject.UserName));
if EventObject.UserRoles <> nil then
outputdebugstring(PChar(eventobject.UserRoles.Text));
if EventObject.AuthorizedRoles <> nil then
outputdebugstring(PChar(eventobject.AuthorizedRoles.Text));
if EventObject.DeniedRoles <> nil then
outputdebugstring(PChar(eventobject.DeniedRoles.Text));
valid := True;
end;
Am I missing the point, or is there a property that I need to set somewhere to enable the TRoleAuth attribute to function?
= = = = = = = = = = Edit: Mat DeLong provided the answer. The DSAuth unit (where the TRoleAuth custom attribute class is declared) was missing from the interface section of the unit in which the DSServerModule descendant was defined.
One thing to make sure of is that in your server methods class you have the "DSAuth" unit in the uses clause of the interface section. If you don't, you should see a compile time warning saying "Unsupported language feature: ‘custom attribute’". If this is happening, it means your attributes are being ignored because the TRoleAuth type is unknown.
If that isn't the case, then I'm not sure what else it would be. If working properly, in your OnUserAuthorize event, you should see "EventObject.DeniedRoles" containing the "readonly" role defined in the code attribute. You should also see "EventObject.UserRoles" containing this role. If this is the case, then you wouldn't need to implement OnUserAuthorize at all, and the code would automatically deny this user authorization.
A couple things to note:
If you put a TRoleAuth attribute on a function or procedure, it replaces the attribute put on the class (only for that one method.) It doesn't add to it.
If you set a design-time attribute that ends up apply to the method (by modifying the 'Roles' collection on the TDSAuthenticationManager component) then the attribute(s) you added in code will be ignored.
Hope that helps,
Mat