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
Related
I have created a stored procedure with the behavior execute as OWNER (I need the owner’s rights).
I am looking to get the original role of the user that called it, is there any way to do that?
Unfortunately, as expected the select current_role() statement returns the owner's role ...
If a workaround exists, I would appreciate if you could describe it to me.
Following Mike's advice:there the code I done to test Harsh's solution:
Failed again ^^
There's my code. May be you ll see my mistake.
create or replace procedure do_noting_proc()
returns string
language javascript
execute as OWNER
AS
$$
var retour="Resultat";
try{
rs0=snowflake.createStatement({sqlText:"SELECT $QUERY_TAG;"}).execute();
cR=rs0.getColumnValue(1);
return "QRValue= "+callerRole;
}catch(err){
return "do_noting_proc KO: "+ err.message+";";
}
$$;
create or replace procedure test_proc()
returns string
language javascript
execute as CALLER
AS
$$
var rString="Result -->";
try{
rs0=snowflake.createStatement({sqlText:"SELECT CURRENT_ROLE();"}).execute();
rs0.next();
cr="testQT"+rs0.getColumnValue(1);
snowflake.createStatement({sqlText:"alter session set QUERY_TAG="+cr+";"}).execute();
rs=snowflake.createStatement({sqlText:"call PUBLIC.do_noting_proc();"}).execute();
rs.next();
rString+=rs.getColumnValue(1);
}catch(err){
return "test_proc: "+ err.message+";";
}
return "test_proc : "+rString;
$$;
Thencall share_view_or_table();
==>test_proc : ** Result -->do_noting_proc KO: Use of session variable '$QUERY_TAG' is not allowed in owners rights stored procedure;**
(procedure created by sysadmin and Executed as sysadmin)
Also if I run 'code'
select $QUERY_TAG;
I get the error: SQL compilation error: error line 1 at position 7 Session variable '$QUERY_TAG' does not exist
whereas
SHOW PARAMETERs LIKE 'QUERY_TAG' IN SESSION ;
works fine and print TESTQTSYSADMIN
#Harsh many thanks for your time
When using EXECUTE AS OWNER the rules do not permit reading the caller's session details, including their identity or role:
Owner’s rights stored procedures adhere to the following rules within a session:
[…]
Cannot access most caller-specific information. For example:
Cannot view, set, or unset the caller’s session variables.
Can read only specific session parameters, and cannot set or unset any of the caller’s session parameters.
Cannot query INFORMATION_SCHEMA table functions, such as AUTOMATIC_CLUSTERING_HISTORY, that return results based on the current user.
While inconvenient, you can use logic within the stored procedure to enforce that the role name of the caller should be passed as an argument:
CREATE OR REPLACE PROCEDURE PROC(ROLE_NAME STRING)
[…]
var VALID_ROLES_ARRAY = ["FOO", "BAR"];
var IS_VALID_CALLER_ROLENAME = VALID_ROLES_ARRAY.includes(ROLE_NAME);
[…];
CALL PROC(CURRENT_ROLE());
Delphi-Mocks has a WillReturnDefault method when you don't care about the parameters of a function. I can't figure out how to do that with Spring4D mocking. Grateful for help!
You either use the mock in its default dynamic mode where it allows any call and just returns defaults from its methods or use param matchers - see the following example:
uses
Spring.Mocking;
type
{$M+}
ITest = interface
function GiveNumber(const s: string): Integer;
end;
var
m: Mock<ITest>;
begin
// mocks are dynamic by default so they let all calls happen and return the default
Writeln(m.Instance.GiveNumber(''));
// parameter matcher can be either applied to the When call -
// here we are using the built-in Args.Any to let any parameter happen
// the actual values passed to GiveNumber does not matter then
m.Setup.Returns(42).When(Args.Any).GiveNumber('');
Writeln(m.Instance.GiveNumber('whatever'));
// when specifying a specific param matcher you basically add this to the existing behavior
// when calling the mock checks for any given behavior that matches starting from the
// most recently defined
m.Setup.Returns(77).When.GiveNumber(Arg.IsEqual('this'));
Writeln(m.Instance.GiveNumber('this')); // 77 as we just specified
Writeln(m.Instance.GiveNumber('something')); // 42 as we specified before for any arguments
// so you should always start with the broader matcher and then the more specific ones
// as a broader one would "override" a more specific one as you can see now
m.Setup.Returns(42).When(Args.Any).GiveNumber('');
// we get 42 now as the Args.Any matcher was added last and matches the parameter
Writeln(m.Instance.GiveNumber('this'));
Readln;
end.
I have a TEmbeddedWB (https://sourceforge.net/projects/embeddedwb/) with an iFrame in it. I have to find out that a specific HTML Tag is inside that iFrame or not. My iFrame object is a IHTMLFrameBase2, while the Tag is a IHTMLElement. I know that the iFrame.contentWindow.document (which is a IHTMLDocument2) is the same as Tag.document. But the Tag.document is an IDispatch object, therefore the following gives a false:
if iFrame.contentWindow.document = Tag.document then ShowMessage('In iFrame')
else ShowMessage('Not in iFrame');
I know that the two object is the same, because the Watch List can show their memory address:
But I can't get their addresses from code. What I've tried:
Addr(iFrame.contentWindow.document) // Gives variable required error
#iFrame.contentWindow.document // Gives variable required error
Pointer(iFrame.contentWindow.document) //Compiles, but gives wrong address
Format('%p',[iFrame.contentWindow.document]) //Compiles, but gives EConvertError
Note: If I run line-by-line the addresses that the Watch List is showing change after EVERY line of code, no matter the code affects the WebBrowser or not.
From the rules of COM:
It is required that any call to QueryInterface on any interface for a given object instance for the specific interface IUnknown must always return the same physical pointer value. This enables calling QueryInterface(IID_IUnknown, ...) on any two interfaces and comparing the results to determine whether they point to the same instance of an object (the same COM object identity).
So, ask them both for their IUnknown interface, and compare.
var
disp: IDispatch;
doc: IHTMLDocument2;
....
if (disp as IUnknown) = (doc as IUnknown) then
....
I have a TPushEvent and TKinveyProvider declared on my firemonkey form
I'm trying to manually set the value of the Provider in code. I realize that by default when you drop those controls on a form the the PushEvent's Provider property is automatically set to the TKinveyProvider. However, I'm working around an apparent bug and I'd like set it later.
Am I setting the provider property correctly in this snippet?
//In my form class
//...
myPushEvents: TPushEvents;
myKinveyProvider: TKinveyProvider;
//later on in one of my procedures/methods
//...
myPushEvents.Provider := myKinveyProvider;
When I look a the value else later on after it should have been set, it still appears to be nil.
Provider appears to be defined as a IBackendProvider which is an interface and I'm not sure if I have to assign it its provider differently than I would with a simple data type like an Integer or a String.
Yes, this is the right way to do it.
myPushEvents.Provider := myKinveyProvider
In this cases myPushEvents.Provider is being assigned a reference to myKinveyProvider. You don't have to do any special casting because myPushEvents.Provider expects something that conforms to the IBackendProvider interfcase, and myKinveyProvider (a TKinveyProvider) does.
Note: In my specific case, thanks to myPushEvents.Provider being set as the result of a timer finishing, it was indeed still nil.
Adding an
if (myPushEvents.Provider <> nil) then
begin
// ... use myPushEvents.Provider
end;
protected the usage of it until the value had been set properly after the timer ran.
I am using ORM Lite's .SqlList method to call a stored procedure and map the results to my custom object. When I am using only one parameter, and calling the stored procedure as follow it works fine:
var results = conn.SqlList<CustomObject>("EXEC MyStoredProcedure #paramOne"), new { paramOne = "someParam" });
When I want to call a stored procedure with more than one parameter and I call it as shown bellow I get an error stating that "The given key was not present in the dictionary.".
var results = conn.SqlList<CustomObject>("EXEC MyStoredProcedure #paramOne, #paramTwo"), new { paramOne = "someParam", paramTwo = "someOtherParam" });
I had a look at the sql stored procedure test page on the ORM Lite Github repo but it does not show how to call stored procedures with more that one param.
Thanks.
EDIT:
I should add that the second parameter on the sql side is a custom table type, and I am sending in a DataTable type in C#.