The European Union has introduced strict data protection rules last month, for which companies had to become legally compliant to avoid fines. We have a set of patches to apply to get a GDPR tool for Microsoft Dynamics AX 2012, which has been released to assist us:
- KB4056903 Privacy Policy update
- KB4074643 DAPIA Security tool
- KB4057507 SQM Data collection
The part relevant for us is the tool, which allows capturing which interactive users have logged on to AX, who are using a security role that may access sensitive information.
Unfortunately Microsoft only provides a high-level guideline on what shall be included and provides very little tangible assistance. Due to this I have felt we needed some way to identify what security roles could really be accessing sensitive data, so I came up with an X++ job that does exactly this. You may pass in menu items for forms, reports and also tables that may access details such as Customers, Global Address Book, Vendors, Address and Contact details. The tool is using the Security framework to determine which roles can edit such data, but you may change filter criteria to also include View access.
static void WIK_GDPR_enable_roles(Args _args) { #AOT // List of tables which might contain sensitive data container tables = [ [menuitemDisplayStr(CustTable), UtilElementType::DisplayTool] ,[menuitemDisplayStr(CustTableListPage), UtilElementType::DisplayTool] ,[menuitemDisplayStr(CustTableEdit), UtilElementType::DisplayTool] ,[menuitemDisplayStr(CustTableDetails), UtilElementType::DisplayTool] ,[menuitemDisplayStr(GlobalAddressBookListPage), UtilElementType::DisplayTool] ,[menuitemDisplayStr(DirPartyTable), UtilElementType::DisplayTool] ,[menuitemDisplayStr(DirPartyTableEdit), UtilElementType::DisplayTool] ]; // Replace role settings? boolean update = NoYes::Yes; UtilElementType objectType; str objectName; int i = 1; SysSecFlatDataTable objects; SysSecFlatDataTable allObjects; SysUserLogRoleSettings roleSettings; SecurityRole securityRole; allObjects.setTmp(); while (i <= conLen(tables)) { objectName = conPeek(conPeek(tables, i), 1); objectType = conPeek(conPeek(tables, i), 2); switch (objectType) { // Implemented from \Forms\SysSecObjectsInRole\init case UtilElementType::DisplayTool: SysSecObjectsFromEntryPoint::GenerateData( SysSecObjectsAnalyzeType::SecViewRelatedRoles, objectName, enum2int(objectType)); break; case UtilElementType::OutputTool: SysSecObjectsFromEntryPoint::GenerateData( SysSecObjectsAnalyzeType::SecViewRelatedRoles, objectName, enum2int(objectType)); break; case UtilElementType::ActionTool: SysSecObjectsFromEntryPoint::GenerateData( SysSecObjectsAnalyzeType::SecViewRelatedRoles, objectName, enum2int(objectType)); break; case UtilElementType::Table: SysSecObjectsFromSecurableObject::GenerateData( objectName, enum2int(objectType)); break; } while select objects { allObjects.clear(); buf2Buf(objects, allObjects); allObjects.doInsert(); } i++; } if (update) { i = 0; ttsBegin; update_recordSet roleSettings setting HasAccessToSensitiveData = NoYes::No; // No join for Tmp object, must use nested loop while select allObjects group by Role//, IsOverride where allObjects.IsOverride == NoYes::No && ((allObjects.AccessRight != AccessRight::View && allObjects.AccessRight != AccessRight::NoAccess) && (allObjects.EntryPointAccess != AccessRight::View && allObjects.EntryPointAccess != AccessRight::NoAccess)) { select firstOnly forUpdate roleSettings join RecId from securityRole where securityRole.AotName == allObjects.Role && roleSettings.SecurityRole == securityRole.RecId; if (roleSettings) { roleSettings.HasAccessToSensitiveData = NoYes::Yes; roleSettings.doUpdate(); i++; } } ttsCommit; info(strFmt('%1 security roles have been updated', i)); } while select Role, RoleName from allObjects group by RoleName, Role//, AccessRight, EntryPointAccess where allObjects.IsOverride == NoYes::No && ((allObjects.AccessRight != AccessRight::View && allObjects.AccessRight != AccessRight::NoAccess) && (allObjects.EntryPointAccess != AccessRight::View && allObjects.EntryPointAccess != AccessRight::NoAccess)) { info(strFmt('%1 (%2)', allObjects.Role, allObjects.RoleName)); } }
The XPO could be downloaded from GitHub.
Could you let me know if your XPO can be deployed on AX 2012 R2 ?
In the same way do you know if we can deploy the Microsoft patches on AX 2012 R2 ?
Hi Stephane,
AX 2012 R2 is ending its’ support lifecycle in a couple of days (2018-10-09 https://support.microsoft.com/en-us/lifecycle/search/640)
As per the KB information on LifeCycle Services only R3 is supported for the DAPIA tool. For earlier versions you only get to turn off the SQM data collection and get an updated license for installations.
https://fix.lcs.dynamics.com/Issue/Details?kb=0&bugId=3909273
Since the DAPIA tool content is just a couple of extra tables and code for user logins, you could most likely backport it yourself from an R3 installation to R2, and use my job to populate settings.
Hi Vilmos,
Can I ask where SysUserLogRoleSettings comes from? It’s not compiling in my R3 implementation.
Perhaps I need to install the 3 KB’s from Microsoft?
regards
Mike
Hi,
The job is for the DAPIA security tool framework which comes with the KB hotfix.
Cheers,
Vilmos