Tips on AX 2012 Security Development Tool – Part 8
Last year I posted some tips on the Microsoft Dynamics AX Security Development Tool. Recently my colleague Boye Walters also wrote an episode and named it Tips on AX 2012 Security Development Tool – Part 7. Well… he fanned the fire on his topic and here is another edition: Tips on AX 2012 Security Development Tool – Part 8; focusing on the view type Duties and Privileges.
Security Development Tool – Part 7
After reading the six posts about the Security Development Tool from André, I got excited about the tool and started using it on a regular basis. It proved to be a powerful tool which cut down development time quite a bit. Almost everything you need for security development is available. But there was one scenario where I missed a functionality in the tool. I was redesigning a few security roles to fit the users need and because the basis was done very poorly I decided to create new roles. Everything worked fine except for 1 tiny little thing that wasn’t picked up by the registration. So what I wanted to do is compare the old role to the new role to figure out what was missing. There is no functionality in the tool for this, and as I’m firmly against using excel, I made it my mission to get this information directly from the tool. In my search I found a ‘dirty and limited’ way to do it and a ‘nicer and more extended’ way to get the job done. I’ll explain them both for a complete picture.
Option 1: ‘Dirty but effective’ way to compare roles in Security Development Tool
If the only security objects you want to compare are Roles then there is a simple but ‘dirty way’ to do it. When you open the ‘Security entry point permissions’ form it will show you the entry point permissions of the System User Role. We could fill this column of the form with a different role to compare it to the role we’re going to edit.
The way to accomplish this is fairly easy. In the AOT open up the form ‘Security entry point permissions’ and under ‘methods’ find the method ‘LoadSystemUserPermissions’. In line 12 you see :
Select firstonly Recid from systemUserRole Where systemUserRole == 'SystemUser';
Simply change ‘SystemUser’ to the AOTname of the desired Role. After a quick compile the rights of the choosen role will be presented in the column ‘System User Role Acces Level’ next to the access level of the role you are developing.
Option 2: ‘Nicer and more extended’ way to compare object in Security Development Tool
The first way is good for developers and gives fast results for when you only need this functionality ones. Now let’s build something lasting, that consultants and end users can use as well. And to make things even more interesting let’s build some filtering options as well.
So the general idea is to build something like this:
Security Development Tool including Comparison and filteringWhere you can compare roles/duties and privileges and use filters to easily find the differences between to objects of the same type.
More elaborated what we want to achieve is:
- A possibility to add a column with access rights to the grid based on the selection in the ‘Compare’ combobox. The list of the combobox should depend on the selection made in the ‘Type’ combobox, like the list of the ‘Name’ combobox.
- A filtering mechanism to quickly identify the differences between the ‘acess level’ column and the newly created ‘Compare Results’ column in the grid.
Part 1 comparison in Security Development Tool
To be able to extend functionality you first need to understand how the existing functionality (technically) works. As mentioned before the behavior of our ‘Compare’ combobox has to look quite similar to the behavior of the ‘Name’ combobox. So let’s see what the flow of the functionality is:
- On initiating the form ‘SysSecEntryPointManager’ a ‘in memory’ temp table is being filled with all security objects and their access rights for the ‘SystemUser’.
- After choosing a ‘Type’ in the combobox the list of the combox is changed.
- After choosing a security object in the ‘Name’ combobox, the code will check if it is a role otherwise (duty or priviledge) create a custom role. And insert the data in the temp table.
Data dictionary
First thing to do is create a column in the forms datasource for storing the access rights associated with the security object we are comparing with.
In the Table ‘SysSecEntryPointTmp’ we have to create a column called ‘CompareUserRight’ based on the Enum ‘AccessRight’. The label property should contain something like ‘Comparison Results’.
(The ‘Name’ object populates the field ‘AccessRight’.)
Creating a new field on the datasource of the Security Development Tool formPut the field in the ‘DataManager’ group to make it automatically available on the form.
DesignFunctionality change
Let us first set a few variables in the ClassDeclaration that we are going to need throughout the next steps:
In the classDeclaration insert the following at line 65
//BWAL Security Development Tool Addon: compare User rights Map compareUserAccessRightsMap; SecurityRoleAotName compareRoleAOTName; recid compareRoleID; IdentifierName compareDevelopmentObject; SecurityRoleName compareRoleName; //END BWAL Security Development Tool Addon: compare User rights
Create a method called ‘CompareDevelopmentObject’ like this in the root of the form to be able to use it later on:
//BWAL Security Development Tool Addon: compare User rights public IdentifierName CompareDevelopmentObject() { return compareDevelopmentObject; } //END BWAL Security Development Tool Addon: compare User rights
In the form ‘SysSecEntryPointManager’ in the design node create a new group beneath ‘Group:Settings’ and call this one ‘Compare’.
Create a new group for Security Development Tool ComparisonSet its properties as:
Width = Column Width RightMargin = 2 ColumnSpace = 2
Let’s create a combobox similar to the ‘Name’ combobox including it’s methods.
In the group ‘Compare’ we create a stringEdit called ‘Compareobject’ with the following properties:
AutoDeclaration = yes
On this stringEdit we create 2 methods:
First:
//BWAL Security Development Tool Addon: modified method compareObject control. public boolean modified() { boolean ret; ret = super(); element.SelectObject(); return ret; } //END BWAL Security Development Tool Addon: modified method compareObject control.
What does this do? If you choose a security object it will then insert or update the access rights in the temp table.
Second:
//BWAL Security Development Tool Addon: Lookup form for compareObject control. public void lookup() { str lookupForm; SysSecDevelopmentType selectedType = ObjectType.selection(); FormRun formRun; Args args; switch(selectedType) { case SysSecDevelopmentType::Role: lookupForm = formStr(SysSecRoleLookup); break; case SysSecDevelopmentType::Duty: lookupForm = formStr(SysSecDutyLookup); break; case SysSecDevelopmentType::Privilege: lookupForm = formStr(SysSecPrivilegeLookup); break; } if(lookupForm != '') { args = new Args(lookupForm); args.caller(this); formRun = classfactory.formRunClass(args); formRun.init(); this.performFormLookup(formRun); } } //END BWAL Security Development Tool Addon: Lookup form for compareObject control.
What does it do? This method changes the list of the combobox making it dependable on the selection in the ‘Type’ combobox.
To make sure that when you choose a different type the ‘Compare’ combobox selection is deleted insert the following code:
In the ‘modified’ method of the ‘Combox:ObjectType’ at line 8 insert:
//BWAL Security Development Tool Addon: Clear CompareObject CompareObject.text(''); //END BWAL Security Development Tool Addon: Clear CompareObject
Next thing to do is change properties on the previously created column available in the grid. To do this go to ‘MaingroupRightGroupEntryPointsGridEntryPointsGrid_CompareUserRight’. The properties should look like this:
AutoDeclaration = Yes Visible = No
To make the contextmenu visible on this column as it is on other columns create a method on this column like this:
//BWAL Security Development Tool Addon: compare User rights public int showContextMenu(int _menuHandle) { return EntryPointsGrid.showContextMenu(_menuHandle); } //END BWAL Security Development Tool Addon: compare User rights
The earlier created ‘modified’ method on the ‘Compare’ combobox calls the ‘SelectObject’ method of the form.
When looking at this method we see that it needs some modifications like:
insert at line 15(after: element.MapDutyOrPrivilegeToRole();)
//BWAL Security Development Tool Addon: compare User rights if(CompareObject.text() != '') { element.MapCompareDutyOrPrivilegeToRole(); } //END BWAL Security Development Tool Addon: compare User rights
What does it do? This is a call to a method that we are going to create. That method will hold the code that creates a role from a duty or privilege.
And insert at line 38 (after: else
{
error(strFmt(“@SDT107”, CurrentObject.text()));
}
}):
//BWAL Security Development Tool Addon: compare User rights if(CompareObject.text() != '') { //Verify role exists select firstOnly recid, Name, AOTName from role where role.Name == CompareObject.text(); if(role.RecId != 0) { compareRoleID = role.RecId; compareRoleAOTName = role.AotName; } else { error(strFmt("@SDT107", CompareObject.text())); } } //BWAL Security Development Tool Addon: compare User rights
What does it do? The actual insert/update of data in the temp table.
Now let’s create the method we’re calling for in the ‘SelectObject’ method. Create it under ‘methods’ in the root of the form.
Create a method called ‘MapCompareDutyOrPrivilegeToRole’:
//BWAL Security Development Tool Addon: compare User rights public void MapCompareDutyOrPrivilegeToRole() { TreeNode devRole = SysSecEntryPointManager::FindOrCreateUniqueRoleForCompareUser(); SecurityRole securityRole; TreeNode childNode; TreeNodeIterator tIterator; str typeToAdd; SecurityTask task; if(!SysSecEntryPointManager::ObjectEditableVCS(devRole)) { error(strFmt("@SDT89", devRole.AOTname())); return; } //Remove existing duty/privileges tIterator = devRole.AOTfindChild('Duties').AOTiterator(); childNode = tIterator.next(); while(childNode) { childNode.AOTdelete(); childNode = tIterator.next(); } tIterator = devRole.AOTfindChild('Privileges').AOTiterator(); childNode = tIterator.next(); while(childNode) { childNode.AOTdelete(); childNode = tIterator.next(); } if(CompareObject.text() != '') { //Add duty/privilege typeToAdd = 'Privileges'; if(element.CurrentDevelopmentType() == SysSecDevelopmentType::Duty) { typeToAdd = 'Duties'; select firstOnly AOTName from task where task.Name == CompareObject.text() && task.Type == SecurityTaskType::Duty; } else { select firstOnly AOTName from task where task.Name == CompareObject.text() && task.Type == SecurityTaskType::Privilege; } if(task.AotName == '') { devRole.AOTrestore(); error(strFmt("@SDT90", element.CurrentDevelopmentType(), CompareObject.text())); return; } //Validate object exists if(TreeNode::findNode(strFmt('%1\%2', #SecurityPath, typeToAdd)).AOTfindChild(task.AotName) == null) { devRole.AOTrestore(); error(strFmt("@SDT91", CompareObject.text())); return; } compareDevelopmentObject = task.AotName; compareRoleAOTName = devRole.AOTname(); compareRoleName = devRole.AOTgetProperty(#PropertyLabel); select RecId from securityRole where securityRole.AotName == compareRoleAOTName; compareRoleId = securityRole.RecId; childNode = devRole.AOTfindChild(typeToAdd).AOTadd(task.AotName); childNode.AOTsetProperty('Name', element.CompareDevelopmentObject()); } //Avoid current form loosing focus due to compiler output WinAPI::enableWindow(infolog.hWnd(), false); try { devRole.AOTsave(); } catch { WinAPI::enableWindow(infolog.hWnd(), true); throw Exception::Error; } WinAPI::enableWindow(infolog.hWnd(), true); } //END BWAL Security Development Tool Addon: compare User rights
Great the first line of this code is calling a method that doesn’t exist on the class ‘SysSecEntryPointManager’… Yes I know, Let’s create it then.
Have a look at the ‘FindOrCreateUniqueRoleForCurrentUser’ it creates a Role in the AOT for the security object chosen on the form. We have to do almost the same for the ‘CompareObject’ we just created on the form. So let’s copy it and rename it to ‘FindOrCreateUniqueRoleForCompareUser’. (I know this isn’t best practice not even good practice but I didn’t want to change the parameters of the method already used because then I had to replace more code of the Security Development Tool and had to explain it which would be beside the point of this blogpost).
Only thing we need to change is in line 6 we replace ‘%1%2’ with ‘%1%2Compare’ to make it look like this:
str roleAOTName = strfmt('%1%2Compare',#SecGeneratedRoleAOTNamePrefix,CurUserId());
Now a bit more difficult to find but we need to change the ‘SetPermissions’ method as well. Because :
- In the ‘SelectObject’ method we call the ‘LoadPermissions’ method which calls the ‘SetPermissions’ method.
- The ‘Refresh’ button calls it.
//BWAL Security Development Tool Addon: compare User rights if(CompareObject.text() != '') { compareDevelopmentObject = ''; compareRoleName = ''; compareRoleAOTName = ''; element.MapCompareDutyOrPrivilegeToRole(); } //END BWAL Security Development Tool Addon: compare User rights
And at line 94 (after: EntryPointsGrid_AccessRight.visible((currentRoleAOTName != ”));):
//BWAL Security Development Tool Addon: compare User rights EntryPointsGrid_CompareUserRight.visible((compareRoleAOTName != '')); //END BWAL Security Development Tool Addon: compare User rights
This line toggles the columns visibility.
The ‘SetPermissions’ method in it’s turn calls the method ‘LoadEntryPointPermissions’ which we also have to alter:
Insert in method ‘LoadEntryPointPermissions’ at line 116 (before: //Update permissions that no longer exist and so are now NoAccess
):
//BWAL Security Development Tool Addon: compare User rights entryPointPermissions = SysSecEntryPointManager::GetEntryPointPermissionsForRole(compareRoleID); for(i = 1; i <= conLen(entryPointPermissions); i = i+2) { objectKey = conPeek(entryPointPermissions, i); accessRightVar = conPeek(entryPointPermissions, i+1); update_recordSet SysSecEntryPointTmp setting compareUserRight = accessRightVar where SysSecEntryPointTmp.ObjectKey == objectKey; } //END BWAL Security Development Tool Addon: compare User rights
Part 2 filtering in Security Development Tool
Data Dictionary Change
We first create an enum called ‘SysSecFilterCompare’:
Enum created for Security Development Tool FilteringChange the ‘Style’ property to ‘Radio button’ so we can drop it on the form later. Don’t forget to fill out the label properties as well.
Designfunctionality change
Next we drop the previously create enum ‘SysSecFilterCompare’ into the previously created group ‘Compare’ and rename the enum to ‘FilterCompare’.
Set the following properties accordingly:
Auto Declaration = yes Columns = 5
On this radiobutton we only need 1 method:
//BWAL Security Development Tool Addon: modified method Fiter control. public boolean modified() { boolean ret; ret = super(); element.AddCompareFilter(); SysSecEntryPointTmp_ds.executeQuery(); return ret; } //END BWAL Security Development Tool Addon: modified method Filter control.
What does it do? It calls the functionality to add a filter on the datasource and re-executes the query.
Create a method ‘AddCompareFilter’ to make the filter work, like this:
//BWAL Security Development Tool Addon: Actual Filter code public void AddCompareFilter() { QueryBuildDataSource qbds; QueryBuildRange qbr; str compareFieldStr = fieldStr(SysSecEntryPointTmp,SystemUserRight); str currentFieldStr = fieldStr(SysSecEntryPointTmp,AccessRight); if(CurrentObject.text() != '') { if(Compareobject.text() != '') { compareFieldStr = fieldStr(SysSecEntryPointTmp,CompareUserRight); } qbds = SysSecEntryPointTmp_ds.query().dataSourceTable(tableNum(SysSecEntryPointTmp)); qbds.clearRange(fieldNum(SysSecEntryPointTmp, DataAreaId); qbr = qbds.addRange(fieldNum(SysSecEntryPointTmp, DataAreaId)); switch(FilterCompare.selection()) { case SysSecFilterCompare::Equal : qbr.value(strFmt('(%1 == %2)',currentFieldStr,compareFieldStr)); break; case SysSecFilterCompare::NotEqual : qbr.value(strFmt('(%1 != %2)',currentFieldStr,compareFieldStr)); break; case SysSecFilterCompare::More : qbr.value(strFmt('(%1 > %2)',currentFieldStr,compareFieldStr)); break; case SysSecFilterCompare::Less : qbr.value(strFmt('(%1 < %2)',currentFieldStr,compareFieldStr)); break; default : break; } qbr.status(RangeStatus::Locked); } } //END BWAL Security Development Tool Addon: Actual Filter Code
What does it do? If there is a value in the ‘Compare’ combobox it will compare the rights in the ‘Compare Results’ column with the ‘Acces Right’ column. The type of comparison is depending on the option selected. If there is no selection in the ‘compare’ combobox then the ‘SystemUser’ rights column is compared to the ‘Access right’ column.
TIP: Use Comparison with filter and recording
You can even use this new feature in combination with the standard filter which is applied after recording security object. So if you want know the difference in access rights between 2 security objects in a certain area of AX. Just record it and use comparison and filtering.
Interesting links:
Installing the Security Development Tool
Security Development Tool Explained by Andre – Part 1
Security Development Tool Explained by Andre – Part 2
Security Development Tool Explained by Andre – Part 3
Security Development Tool Explained by Andre – Part 4
Security Development Tool Explained by Andre – Part 5
Security Development Tool Explained by Andre – Part 6