Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Dexterity Trigger Procedure - Custom Security 1

Status
Not open for further replies.

Borvik

Programmer
Jan 2, 2002
1,392
US
I'm hoping to build a custom security module to replace the password prompt for SOP processing options such as:

Allow Invoicing of Unfulfilled or Partially Fulfilled Orders
Delete Documents
Edit Printed Documents
Override Document Numbers
Void Document

I'm looking for the procedure(s) that I can call Trigger_RegisterProcedure on and implement my own security instead.

I've found the SOP_Delete_Document and SOP_Void_TRX procedures, but I wasn't seeing anything obvious for the other options. Perhaps there is a single procedure I can tack on to, that I can check the 'TRX Source' on and see if my module should kick in? Perhaps something the like Activity Tracking example from the Function library reference for Trigger_RegisterProcedure?

Thanks.
 
Hi Borvik

Did you log scripts to see what code is being called.

I found this one that might be helpful.

Global Procedure: SOP_Password

{--------------------------------------------------------------------------------
Public SOP_Password

This script is passed the constant for a particular option and it asks the
question and returns if the person knew the password or not. Out_Err = 1
if the password was not entered or cancel was pushed, and Out_Err = 0 if
they knew the password. The option not being allowed results in an error
(OUT_Err = 1). IN_User is passed in as true if there is user interaction,
else it is passed in as false (Batch processing).

--------------------------------------------------------------------------------}

in boolean IN_Check_Password; {check for password}
in boolean IN_Warnings; {give warnings}
in boolean IN_Generic_Opt; {generic option}
in integer IN_Opt; {Options Number}
in integer IN_Type; {Document Type}
out integer OUT_Err; {1 = not allowed}
inout anonymous file SOP_Anon; {positioned, SOP Allow, SOP Password required}


David Musgrave [MSFT]
Escalation Engineer - Microsoft Dynamics GP
Microsoft Dynamics Support - Asia Pacific

Microsoft Dynamics (formerly Microsoft Business Solutions)

mailto:David dot Musgrave at microsoft dot com

Any views contained within are my personal views and not necessarily Microsoft policy.
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Hey Dave.

Unfortunately no I don't log scripts - though I seem to remember something about being able to now. I program sporadically in Dexterity as my company has need, so between instances I sometimes forget I can do that.

I had been hunting through CoreParams_1000.txt for functions and it didn't even occur to me to search for "password" - that's perfect.

Thanks Dave.
 
I'm a little confused - your description gives references to a parameter IN_User, but that isn't in the parameter list.
 
I've found the description in the SDK and that's absolutely perfect - except options don't appear to be matching up.

I add a before trigger to the procedure to log various action with the following code:
Code:
in boolean IN_Check_Password;
in boolean IN_Warnings;
in boolean IN_Generic_Opt;
in integer IN_Opt;
in integer IN_Type;
out integer OUT_Err;
inout anonymous table SOP_Anon;

local long l_characters_written;
local boolean l_result;
local string l_pathname;
local integer l_file_ID;
local string l_str;

l_pathname = "C:\Users\user_name\Desktop\password_log.txt";
l_str = "";
if IN_Type = SOP_ORDER then
  l_str = l_str + "SOP_ORDER/";
  if IN_Opt = SOP_OPT_ORDER_VOID_DOC then
    l_str = l_str + "VOID_DOC";
  elseif IN_Opt = SOP_OPT_ORDER_OVER_DOC_NUM then
    l_str = l_str + "OVER_DOC_NUM";
  elseif IN_Opt = SOP_OPT_ORDER_INVOICE_UNFULFILLED_ORDERS then
    l_str = l_str + "INVOICE_UNFULFILLED_ORDERS";
  elseif IN_Opt = SOP_OPT_ORDER_EDIT_PRINTED_DOC then
    l_str = l_str + "EDIT_PRINTED_DOC";
  elseif IN_Opt = SOP_OPT_ORDER_DOC_NUM then
    l_str = l_str + "DOC_NUM";
  elseif IN_Opt = SOP_OPT_ORDER_DELETE_DOC then
    l_str = l_str + "DELETE_DOC";
  else
    l_str = l_str + "UNKNOWN";
  end if;
  l_str = l_str + " ";
end if;
l_str = l_str + "(";
if IN_Check_Password then
  l_str = l_str + "true, ";
else
  l_str = l_str + "false, ";
end if;
if IN_Warnings then
  l_str = l_str + "true, ";
else
  l_str = l_str + "false, ";
end if;
if IN_Generic_Opt then
  l_str = l_str + "true, ";
else
  l_str = l_str + "false, ";
end if;
l_str = l_str + str(IN_Opt) + ", ";
l_str = l_str + str(IN_Type) + ")";

l_file_ID = TextFile_Open(l_pathname, 1, 2);
l_characters_written = TextFile_WriteLine(l_file_ID, l_str);
l_result = TextFile_Close(l_file_ID);
I then went and tried to open an order that has printed (requiring password). Here is what got output into the log file:
Code:
SOP_ORDER/DELETE_DOC (true, true, true, 2, 2)
I did not attempt to delete a document.

Is the IN_Opt parameter possibly zero-based and I need to add a 1 to it in order to compare it the constants?
 
Ah, I think I got it - generic is set to TRUE so it should be using those options. Those match up.
 
I'm not sure if this will get answered here or not (as I've already starred a response), but I'll give it a go here first.

I'm a little confused as to how SOP_Password is supposed to work, I'll lay out what I think is happening - and mark the points of confusion.

If IN_Check_Password is false, and a password IS required - no prompt would occur and OUT_Err would show not allowed.
If IN_Warnings is true and OUT_Err ends up being not allowed, then the appropriate warning is displayed. When false the warning would not display.
The combination of IN_Type and IN_Opt identify which option is being checked - IN_Opt would correspond to the index number of the 'SOP Password' array as passed in the SOP_Anon parameter.

Generics throws a kink into my understanding of how this works. For SOP_INVOICE, SOP_FULFILLMENT_ORDER, SOP_BACK_ORDER, and SOP_QUOTE - generics follows exactly. SOP_RETURN has a different value for _VOID_DOC (6 vs. 5 for _GENERIC_VOID_DOC), and SOP_ORDER has every value 1 higher.

I modified the logging procedure above to account for generics:
Code:
if (IN_Type = SOP_ORDER) and (IN_Generic_Opt) then
  l_str = l_str + "SOP_GENERIC/";
  if IN_Opt = SOP_OPT_GENERIC_DELETE_DOC then
    l_str = l_str + "DELETE_DOC";
  elseif IN_Opt = SOP_OPT_GENERIC_EDIT_PRINTED_DOC then
    l_str = l_str + "EDIT_PRINTED_DOC";
  elseif IN_Opt = SOP_OPT_GENERIC_OVER_DOC_NUM then
    l_str = l_str + "OVER_DOC_NUM";
  elseif IN_Opt = SOP_OPT_GENERIC_VOID_DOC then
    l_str = l_str + "VOID_DOC";
  else
    l_str = l_str + "UNKNOWN_GENERIC(" + str(IN_Opt) + ")";
  end if;
  l_str = l_str + " ";
elseif (IN_Type = SOP_ORDER) and (not IN_Generic_Opt) then
  l_str = l_str + "SOP_ORDER/";

Running some tests yielded:
SOP_GENERIC/VOID_DOC (true, true, true, 5, 2)
SOP_GENERIC/DELETE_DOC (true, true, true, 1, 2)

Voiding and deleting was carried out from the Sales Transaction Entry window on an order document.
The appropriate row in SOP_ID_SETP shows the password for void (for the docid in question) was in SOPPSSWD_6, which corresponds to the non-generic constant - though clearly generics were used.

I'd say 1 was added to IN_Opt during checking, but this throws off all the other document types, but if it's left alone then the order type doesn't work (obviously it works - I just can't see the logic it's using).

I just can't seem to wrap my head around how SOP_Password is verifying against the correct password.

The only thing I can think of is that it is internally running a check on everything to convert between generic and the appropriate SOP to the appropriate number - this seems like it would be highly inefficient.

Can you help me understand this so I can identify the array index to use?

Thanks.
 
Hi Borvik

Here is an example call from the Delete() of form SOP_Entry form function script.

{Check if there is a restriction on doing a delete.}
call SOP_Password,
true, {ask for password}
true, {give warnings}
true, {generic option}
SOP_OPT_GENERIC_DELETE_DOC,
nSOPType,
l_error,
table SOP_ID_SETP;

if l_error <> OKAY then
{Option not allowed.}
Delete = SOP_ABORT_SCRIPT;
abort script;
end if;

Looking at the SOP_Password script, it actually calls the SOP_Check_Password script when it is going to actually ask for a password.

call SOP_Check_Password,
'SOP Password'[l_Opt] of file SOP_Anon,
l_question,
IN_Warnings,
l_error;

The parameters for SOP_Check_Password are

in Password IN_Password;
in string IN_Message;
in boolean IN_Give_Warning; {true=give warning,false=no warnings}
out integer OUT_Error;


Hope this helps.

David

 
Yes and no. That works pretty much the way I expected it.

I'm trying to determine which index to use. I can see that the call to the SOP_Check_Password script is using l_Opt - a local variable (probably an integer).

My guess then is that l_Opt is declared, and then a series of checks is run to determine the actual array index to use - rather than just the IN_Opt parameter passed to the SOP_Password script.
 
The l_Opt value of the array is based on the option constants eg SOP_OPT_GENERIC_DELETE_DOC.

Please note that while the passwords are stored as an array in the setup file, they are edited using a temporary table which converts the array elements to records.

David
 
The l_Opt value of the array is based on the option constants eg SOP_OPT_GENERIC_DELETE_DOC."

Right - that I get.

What I wasn't 100% sure on was how that worked with order documents.

When I voided a document my logging script did in fact match it to SOP_OPT_GENERIC_VOID_DOC (voiding from Sales Transaction Entry window). SOP_OPT_GENERIC_VOID_DOC is equal to 5. However, the password for deleting an order was stored in SOPPSSWD_6 - indicating the 6th index of the array, which matches the value of SOP_OPT_ORDER_VOID_DOC.

I've come up with this code - which matches it up (correctly?):
Code:
if (IN_Generic_Opt) then
  if IN_Type = SOP_ORDER then
    if IN_Opt = SOP_OPT_GENERIC_DELETE_DOC then
      l_Opt = SOP_OPT_ORDER_DELETE_DOC;
    elseif IN_Opt = SOP_OPT_GENERIC_EDIT_PRINTED_DOC then
      l_Opt = SOP_OPT_ORDER_EDIT_PRINTED_DOC;
    elseif IN_Opt = SOP_OPT_GENERIC_OVER_DOC_NUM then
      l_Opt = SOP_OPT_ORDER_OVER_DOC_NUM;
    elseif IN_Opt = SOP_OPT_GENERIC_VOID_DOC then
      l_Opt = SOP_OPT_ORDER_VOID_DOC;
    end if;
  elseif IN_Type = SOP_RETURN then
    if IN_Opt = SOP_OPT_GENERIC_VOID_DOC then
      l_Opt = SOP_OPT_RETURN_VOID_DOC;
    else
      l_Opt = IN_Opt;
    end if;
  end if;
else
  l_Opt = IN_Opt;
end if;

I wanted to verify I had to do something like that - or perhaps I have to do something different?

Thanks.
 
Hi Borvik

I think some testing (like you have been doing) is the best approach.

I would suggest using a nested case statement rather than if's and elseif's. It would be cleaner to read.

I am going on leave.... so have a great Christmas and new year.

Good luck

David
 
Alright - thanks.

Yeah, I normally prefer case statements - I couldn't find it in the pdf doc I was looking in. I'll look again in the comprehensive index.

Have yourself a great Christmas and New Year as well.

Thanks.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top