I realised that there's a second database for AX2012; i.e. the model. After poking about I uncovered some security tables and have taken a stab at finding the relationships between them. I couldn't find any documentation on these tables by Googling, so if anyone else uses this, please note that there may be many oversights and issues.
--use your model database
use [AxDbName_Model]
go
--ensure you don't cause locking when running this script
set transaction isolation level read uncommitted
go
--I got these IDs by comparing the TypeId fields for results with what I saw in the AOT and guessing on relationships.
declare @SubRoleType table (Id int, SubRoleDesc nvarchar(32))
insert @SubRoleType (Id, SubRoleDesc)
values (133, 'Role / SubRole')
, (134, 'Privilege')
, (135, 'Duty')
, (136, 'Process Cycle')
declare @KernelType table (Id int, KernelTypeDesc nvarchar(32))
insert @KernelType (Id, KernelTypeDesc)
values (11, 'Class')
, (44, 'Table')
, (45, 'ServerMethod')
--here's the actual code to fetch the security model/
--it could probably be improved to make it hierarchical, but
--for our company's purposes we don't seem to require that so
--I didn't put any time into investigating that route.
;with permissionsModelCte (ParentId, ItemId, ItemName, ItemTypeId, ItemType, IsEnabled) as
(
--duties & privileges (sub role type describes what type of permission this is; this seems to hold all security related groupings of aot objects)
select mssr.ROLEHANDLE
, mssr.RECID
, mssr.SUBROLENAME
, mssr.SUBROLETYPE
, srt.SubRoleDesc
, mssr.ISENABLED
from ModelSecuritySubRole mssr
left outer join @SubRoleType srt on srt.id = mssr.SUBROLETYPE
union
--permissions (kernel type defines the related object type; this seems to hold everything in the AOT)
select msp.OWNERHANDLE
, msp.RECID
, msp.OBJECTNAME
, msp.KERNELTYPE
, 'Permission\' + kt.KernelTypeDesc
, msp.ISENABLED
from ModelSecurityPermission msp
left outer join @KernelType kt on kt.Id = msp.KERNELTYPE
)
select msr.Name
, pmc.ItemType ChildItemType
, pmc.ItemName ChildItemName
, pmc.IsEnabled
--, pmc.ItemId ChildItemId --interesting for investigating the script, but causes duplicate results
, msr.ROLEHANDLE ItemId
, pmc.ItemTypeId ChildItemTypeId
--, *
from ModelSecurityRole msr
left outer join permissionsModelCte pmc on pmc.ParentId = msr.ROLEHANDLE
--where msr.Name in ('CustInvoiceAccountsReceivableClerk', 'CCIARCollections')
where msr.UTILTYPE = 133 --Roles only
group by msr.Name
, msr.ROLEHANDLE
, pmc.ItemName
--, pmc.ItemId --see select statement's ChildItemId
, pmc.IsEnabled
, pmc.ItemType
, pmc.ItemTypeId
order by msr.Name, pmc.ItemType, pmc.ItemName
go