我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面的微软最有价值专家(Microsoft MVP),欢迎关注我的微信公众号 MSFTDynamics365erLuoYong ,回复385或者20191218可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!
我前面的博文 探索Dynamics 365 用户能够登录使用的最小权限需求 讲述了需要登录系统的最小权限,我们再来配置一个角色让拥有这个角色的用户可以更改用户的业务部门及角色。
在这之前我已经将 Common Data Service minimum privilege security role 解决方案导入了我的Dynamics 365中,导入后会有一个名称为 min priv apps use 的角色。
首先我们用系统管理员账号来克隆出来一个角色,打开 min priv apps use 这个角色,点击【Actions】 > 【Copy Role】按钮。
输入角色名称,然后点击【OK】按钮就会复制成功,默认情况下会打开该角色。
经过一番测试,我找到了需要能够更改用户的业务部门和角色需要的增加的权限如下:
- Service Management Tab的Calendar实体的全部权限
- Business Management Tab的User实体的写权限,全局级别,因为用户进入后默认挂在根业务部门上,操作更改的用户很可能已经不挂在根业务部门上了,要能更改用户信息需要全局写权限
- Business Management Tab 的Security Role的分派权限,全局级别
- Business Management Tab 的User Settings实体的写权限,全局级别
- Customization Tab 的Activate Real-time Processes权限,全局级别,此权限为杂项权限
- Customization Tab 的System Job实体的追加到权限,全局级别
- Customization Tab 的System Job实体的分派权限,全局级别
- Customization Tab 的System Job实体的读权限,全局级别
- Customization Tab 的System Job实体的写权限,全局级别
- Customization Tab 的Process Session实体的追加到权限,全局级别
- Customization Tab 的Process Session实体的追加权限,全局级别
- Customization Tab 的Process Session实体的分派权限,全局级别
- Customization Tab 的Process Session实体的创建权限,全局级别
- Customization Tab 的Process Session实体的删除权限,全局级别
- Customization Tab 的Process Session实体的共享权限,全局级别
- Customization Tab 的Process Session实体的写权限,全局级别
- Customization Tab 的Process实体的追加权限,全局级别
- Customization Tab 的Process实体的分派权限,全局级别
- Customization Tab 的Process实体的创建权限,全局级别
- Customization Tab 的Process实体的删除权限,全局级别
- Customization Tab 的Process实体的共享权限,全局级别
- Customization Tab 的Process实体的写权限,全局级别
- Core Records Tab 的Report实体的读取权限,全局级别
截图说明如下:
如果要求更改业务部门,除了系统管理员操作外,只能更改为操作者一样的业务部门的话,我这里使用插件来验证,我写了一个名称为PreSystemUserUpdate.cs的类,代码如下:
using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Query; using System; namespace PluginDemo { public class PreSystemUserUpdate : IPlugin { public void Execute(IServiceProvider serviceProvider) { //获取日志服务 ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); //写一些日志,方便跟踪 tracingService.Trace($"Enter PreSystemUserUpdate on {DateTime.UtcNow.ToString()}"); IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) { //插件针对的当前实体记录,对于Pre Update消息来讲,该对象包括了所有设置的字段值,若字段没有设置值,在该对象中会不存在 Entity currentEntity = (Entity)context.InputParameters["Target"]; //获取组织服务 IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService orgSvc = serviceFactory.CreateOrganizationService(context.UserId); //如果更新的字段包括Business Unit if (currentEntity.Contains("businessunitid")) { tracingService.Trace($"Current user will change Business Unit on {DateTime.UtcNow.ToString()}"); //检查当前用户是否具有System Administrator角色 string fetchXml = string.Format(@"<fetch version='1.0' mapping='logical' distinct='true' no-lock='true' top='1'> <entity name='systemuser'> <attribute name='systemuserid' /> <attribute name='businessunitid' /> <filter type='and'> <condition attribute='systemuserid' operator='eq' value='{0}' /> </filter> <link-entity name='systemuserroles' from='systemuserid' to='systemuserid' visible='false' intersect='true'> <link-entity name='role' from='roleid' to='roleid' alias='ac'> <filter type='and'> <condition attribute='name' operator='eq' value='System Administrator' /> </filter> </link-entity> </link-entity> </entity> </fetch>", context.UserId); //不具有System Administrator角色的用户更改用户的业务部门时候,只能更改为与当前用户相同的业务部门 if (orgSvc.RetrieveMultiple(new FetchExpression(fetchXml)).Entities.Count == 0) { var currentUserBU = orgSvc.Retrieve("systemuser", context.UserId, new ColumnSet("businessunitid")).GetAttributeValue<EntityReference>("businessunitid"); tracingService.Trace($"Current user's business unit name is {currentUserBU.Name}"); if (currentEntity.GetAttributeValue<EntityReference>("businessunitid").Id != currentUserBU.Id) { throw new InvalidPluginExecutionException($"你只能更改当前用户的业务部门为你所在的业务部门-{currentUserBU.Name}"); } } } } tracingService.Trace($"Leave PreSystemUserUpdate on {DateTime.UtcNow.ToString()}"); } } }
然后还是注册插件,大部分步骤可以参考我前面的博文 Dynamics 365中开发和注册插件介绍 ,还是先注册程序集,如下图:
然后右击插件类注册新步骤,如下图:
设置如下图,可以看到是注册到SystemUser实体的Update消息的Pre Operation阶段,注意要筛选字段,我这里筛选字段为 businessunitid,很多人注册Update消息上的插件不筛选字段,那么触发会非常频繁,这是不好的做法。
注册后去测试,如果报错,效果如下:
如果你能授予他人的的角色权限包括了你没有的权限,那就是安全漏洞了。如果你要授予他人的的角色权限包括了你没有的权限系统会报错,下面是一个报错的截图。
至于权限名称,比如prvActivateBusinessProcessFlow 对应界面上的那个项目,可以通过 Security role UI to privilege mapping 来查询。