Dynamics 365需要的最小的权限用来更改用户的业务部门和角色
我是微软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实体的读取权限,全局级别
- Reparent User,这个是杂项权限,拥有这个权限才能看到【Change Business Unit】按钮,权限级别根据需要而定,比如你如果需要授予用户的角色只能是本业务部门的角色,那么设置为业务部门级别,如果包括下级业务部门的角色,则设置为上:下级业务部门级别,任何业务部门的角色的话,则授予全局级别。
截图说明如下:





如果还需要显示用户表单的【REASSIGN RECORDS】的话,需要授予【Enable or Disable User】权限。

如果要求更改业务部门,除了系统管理员操作外,只能更改为操作者一样的业务部门的话,我这里使用插件来验证,我写了一个名称为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='false' 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 == )
{
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 来查询。

如果是限定只能更改到哪些业务部门,可以参考下面代码:
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"];
Entity preImgEntity;
//获取组织服务
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 == )
{
tracingService.Trace($"Current user does not have system administrator role {DateTime.UtcNow.ToString()}");
if (context.PreEntityImages.Contains("PreImg"))
{
preImgEntity = context.PreEntityImages["PreImg"];
}
else
{
throw new InvalidPluginExecutionException("Pre Update image - PreImg does not exist!");
}
var userOrigBU = preImgEntity.GetAttributeValue<EntityReference>("businessunitid");
var currentUserBU = orgSvc.Retrieve("systemuser", context.UserId, new ColumnSet("businessunitid")).GetAttributeValue<EntityReference>("businessunitid");
tracingService.Trace($"Current user's business unit name is {currentUserBU.Name}");
//找到出根业务部门
var buRootQE = new QueryExpression("businessunit");
buRootQE.NoLock = true;
buRootQE.TopCount = ;
buRootQE.ColumnSet = new ColumnSet("businessunitid");
buRootQE.Criteria.AddCondition("parentbusinessunitid", ConditionOperator.Null);
var buRootId = orgSvc.RetrieveMultiple(buRootQE).Entities[].Id;
tracingService.Trace($"Root BU id= {buRootId.ToString()} {DateTime.UtcNow.ToString()}");
var canChange = false;
Guid parentBUId = Guid.Empty;
Entity buEntity;
var newBUId = currentEntity.GetAttributeValue<EntityReference>("businessunitid").Id;
if (userOrigBU.Id == buRootId)//如果用户的原部门是根业务部门
{
//只能将用户改到操作者所在的业务部门及其下级业务部门
if(newBUId == currentUserBU.Id)
{
canChange = true;
}
else
{
buEntity = orgSvc.Retrieve("businessunit", newBUId, new ColumnSet("parentbusinessunitid"));
while (buEntity.Contains("parentbusinessunitid"))
{
parentBUId = buEntity.GetAttributeValue<EntityReference>("parentbusinessunitid").Id;
if (parentBUId == currentUserBU.Id)
{
canChange = true;
break;
}
buEntity = orgSvc.Retrieve("businessunit", parentBUId, new ColumnSet("parentbusinessunitid"));
}
}
}
else
{
//只允许将本部门或者下级部门用户的业务部门改到根业务部门
if(newBUId == buRootId)
{
if(userOrigBU.Id == currentUserBU.Id)
{
canChange = true;
}
else
{
buEntity = orgSvc.Retrieve("businessunit", userOrigBU.Id, new ColumnSet("parentbusinessunitid"));
while (buEntity.Contains("parentbusinessunitid"))
{
parentBUId = buEntity.GetAttributeValue<EntityReference>("parentbusinessunitid").Id;
if (parentBUId == currentUserBU.Id)
{
canChange = true;
break;
}
buEntity = orgSvc.Retrieve("businessunit", parentBUId, new ColumnSet("parentbusinessunitid"));
}
}
}
}
if (!canChange)
{
throw new InvalidPluginExecutionException($"更改业务部门不合规!");
}
}
}
}
tracingService.Trace($"Leave PreSystemUserUpdate on {DateTime.UtcNow.ToString()}");
}
}
}
注意这个代码需要添加一个Pre阶段的Image,请参考下图注册:


注册好Image后的截图如下:

如果是更新插件代码的话,选择对应得程序集,右击,选择【Update】进行更新。

Dynamics 365需要的最小的权限用来更改用户的业务部门和角色的更多相关文章
- Dynamics 365中的分派(Assign)。
我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...
- SpringMVC+Apache Shiro+JPA(hibernate)案例教学(四)基于Shiro验证用户权限,且给用户授权
最新项目比较忙,写文章的精力就相对减少了,但看到邮箱里的几个催更,还是厚颜把剩下的文档补上. 一.修改ShiroDbRealm类,实现它的doGetAuthorizationInfo方法 packag ...
- 探索Dynamics 365 用户能够登录使用的最小权限需求
我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...
- 为实施了IFD的Dynamics 365更换自签名的SSL证书以符合Chrome的要求
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复259或者20170704可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- Dynamics 365中部分账号使用系统明显缓慢怎么办?先这么干!
摘要: 本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复263或者20170828可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyon ...
- 如何让用户登录Dynamics 365 Customer Engagement后自动登录到Unified Interface App?
微软动态CRM专家罗勇 ,回复324或者20190422可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me! Dynamics 365 Customer Engagement ...
- Dynamics 365 Customer Engagement中插件的调试
微软动态CRM专家罗勇 ,回复319或者20190319可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 本文主要根据官方的教 ...
- Dynamics 365 启用跟踪及读取跟踪文件工具
微软动态CRM专家罗勇 ,回复315或者20190313可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 当根据错误提示排查问 ...
- Dynamics 365 POA表记录的产生
微软动态CRM专家罗勇 ,回复314或者20190311可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 前面的博文 Dyna ...
随机推荐
- 上手spring boot项目(四)之springboot如何返回json数据
在springboot整合thymeleaf中,经常会在HTML页面中接收来自服务器的json数据,然后处理json数据并在页面上渲染.那么如何在服务器中返回json类型的数据呢? 1.使用@Resp ...
- SpringBoot-了解微服务(二)
什么是微服务? 微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合: 可以通过http的方式进行互通. 要说微服务架构,先了解一下以前的单体应用架构 单体应用架 ...
- solr集群与项目实战
什么是 SolrCloud : SolrCloud(solr 云)是 Solr 提供的分布式搜索方案,当你需要大规模,容错,分布式索引和检索能力时使用 SolrCloud.当一个系统的索引数据量少的时 ...
- 移动开发在路上-- IOS移动开发系列 网络交互四(1)
最近一段时间上班忙的没日没夜的,不是披星戴月,就是头天早上出门,第二天早上回家...一直没出处时间来更新博客,码农之苦,说了都是泪,废话不多说,直接本主题,经过之前三篇的讲述,ios开发的东西大家或多 ...
- [TimLinux] Python nonlocal和global的作用
1. 执行代码 以下实例都是通过执行以下代码,需要把以下执行代码放在后面实例代码的后面. a = outer_func()print("call a()") a() a() a() ...
- openlayers6结合geoserver实现地图属性查询(附源码下载)
前言 之前写过一篇 openlayers4 版本的地图属性查询文章,但是由于是封装一层 js 代码写的,很多初学者看起来比较有点吃力,所以本篇文章重新写一篇地图属性查询文章,直接基于最新版本 open ...
- iSensor APP 之 摄像头调试 OV9655
iSensor APP 之 摄像头调试 OV9655 iSensor app 非常适合调试各种摄像头,已测试通过的sensor有: l OV7670.OV7725.OV9650.OV9655.OV ...
- elasticsearch搜索QueryStringQueryBuilder时的一些问题记录
首先看下原始数据 但是 如果使用英文查询的时候又和上面有点区别了,感觉还是分词器的问题
- LNMP环境搭建(<=PHP7.2)
目录 准备工作 安装wget 安装net-tools 安装vim 配置显示行号 关闭防火墙 安装Nginx 安装依赖 编译安装Nginx 配置环境变量 Systemd管理 安装MySQL 安装依赖 下 ...
- CSS动态表达式
样式:style:expression(脚本) 如: 1 _top: expression(this.offsetHeight); _top: expression(eval(document.doc ...