While browsing Microsoft Dynamics CRM Development Forums, i have found an interesting security privilege issue post “Both the CSR Manager and Customer Service Representative can publish articles” , after performing a test i found that the security permissions are not working regardless of the Published Article Privileges. So i decided to write up a solution that will fix the issue to help the Microsoft Dynamics CRM customers.
Microsoft Dynamics CRM 2011 (On-Premise & Online) Solution Download & Installation Steps:
1. Click here to download solution file (ArticlePublishingSecurityPrivilegeFix_1_0_0_0.zip)
2. In CRM 2011, go to Settings -> Customization -> Solutions
3. Click on Import button and Browse for the ArticlePublishingSecurityPrivilegeFix_1_0_0_0.zip file
4. During wizard, in Import Options screen makes sure to select option “Activate any process and enable
any SDK message processing steps included in the solution.”
5. Now you can verify Article Publishing Privileges
using System; |
using System.Collections.Generic; |
using System.Linq; |
using System.Text; |
using Microsoft.Xrm.Sdk; |
using Microsoft.Xrm.Sdk.Metadata; |
using Microsoft.Crm.Sdk; |
using Microsoft.Crm.Sdk.Messages; |
using Microsoft.Xrm.Sdk.Query; |
using Microsoft.Xrm.Sdk.Messages; |
namespace wod.Crm.Article.Publishing.Fix |
{ |
public class wod_Plugin : IPlugin |
{ |
public void Execute(IServiceProvider serviceProvider) |
{ |
#region Variable Declaration |
// Obtain the execution context from the service provider. |
Microsoft.Xrm.Sdk.IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); |
IOrganizationServiceFactory wod_serviceFactory = null; |
IOrganizationService wod_CrmService = null; |
RetrieveUserPrivilegesRequest lclRetrieveUserPrivilegesRequest = null; |
RetrieveUserPrivilegesResponse lclRetrieveUserPrivilegesResponse = null; |
Entity wod_ArticlePublishPrivEntity = null; |
#endregion |
try |
{ |
// Check if SetState message is triggered on kbarticle entity |
if (context.InputParameters.Contains("EntityMoniker") |
&& context.InputParameters["EntityMoniker"] is EntityReference |
&& ((EntityReference)context.InputParameters["EntityMoniker"]).LogicalName == "kbarticle") |
{ |
switch (context.MessageName) |
{ |
case "SetStateDynamicEntity": |
#region Article Publishing Privileges Checking Code |
// check if input parameter contains status |
if (context.InputParameters.Contains("Status")) |
{ |
// Check if status paramter value is Published Article code |
if (((OptionSetValue)context.InputParameters["Status"]).Value == -1) |
{ |
// Obtain the service factory to get the service object |
wod_serviceFactory = (IOrganizationServiceFactory)serviceProvider |
.GetService(typeof(IOrganizationServiceFactory)); |
// Obtain service object Imporsonated as plugin calling user |
wod_CrmService = wod_serviceFactory.CreateOrganizationService(context.UserId); |
// Retrieve Article publishing privilege Id |
wod_ArticlePublishPrivEntity = RetrieveEntityByAttribute(ref wod_CrmService, "privilege" |
, new string[] { "privilegeid" }, new string[] { "name" } |
, new object[] { "prvPublishArticle" }); |
if (wod_ArticlePublishPrivEntity != null) |
{ |
lclRetrieveUserPrivilegesRequest = new RetrieveUserPrivilegesRequest(); |
// Pass current user id to for retrieving all privileges |
lclRetrieveUserPrivilegesRequest.UserId = context.InitiatingUserId; |
// Retrieve user all privileges |
lclRetrieveUserPrivilegesResponse = (RetrieveUserPrivilegesResponse) wod_CrmService.Execute(lclRetrieveUserPrivilegesRequest); |
// Search if user has privileges of publishing article |
RolePrivilege wod_UserArticlePublishPriv = lclRetrieveUserPrivilegesResponse.RolePrivileges |
ToList<RolePrivilege>().Find(delegate(RolePrivilege lclPublishPriv) |
{ |
return lclPublishPriv.PrivilegeId == wod_ArticlePublishPrivEntity.Id |
&& lclPublishPriv.BusinessUnitId == context.BusinessUnitId; |
}); |
// If user does not have privileges then throw error and abort execution |
if (wod_UserArticlePublishPriv == null) |
{ |
throw new InvalidPluginExecutionException("Not enough privileges to Publish article."); |
} |
} |
} |
} |
#endregion |
break; |
} |
} |
} |
catch (System.Web.Services.Protocols.SoapException ex) |
{ |
throw new InvalidPluginExecutionException(ex.Detail.InnerText); |
} |
catch (Exception ex) |
{ |
throw new InvalidPluginExecutionException(ex.Message); |
} |
} |
#region Helper Method |
#region Helper Method |
// Helper method used to fetch entity record by column searching |
private Entity RetrieveEntityByAttribute(ref IOrganizationService CrmWebService, string EntityName |
, string[] RetrieveColumns, string[] FilterByColumns |
, object[] FilterByColumnsValue) |
{ |
QueryByAttribute wod_Query = null; |
RetrieveMultipleRequest wod_Request = null; |
RetrieveMultipleResponse wod_Response = null; |
EntityCollection wod_RtrnEntyCollection = null; |
Entity wod_RtrnEntity = null; |
try |
{ |
if (EntityName != string.Empty || RetrieveColumns != null || FilterByColumnsValue != null || FilterByColumnsValue != null) |
{ |
wod_Query = new QueryByAttribute(); |
wod_Request = new RetrieveMultipleRequest(); |
wod_Query.EntityName = EntityName; |
wod_Query.ColumnSet = new ColumnSet(RetrieveColumns); |
wod_Query.Attributes.AddRange(FilterByColumns); |
wod_Query.Values.AddRange(FilterByColumnsValue); |
wod_Request.Query = wod_Query; |
wod_Response = (RetrieveMultipleResponse)CrmWebService.Execute(wod_Request); |
wod_RtrnEntyCollection = wod_Response.EntityCollection; |
if (wod_RtrnEntyCollection != null && wod_RtrnEntyCollection.Entities.Count > 0) |
{ |
wod_RtrnEntity = (Entity)wod_RtrnEntyCollection.Entities[0]; |
} |
} |
} |
catch (System.Web.Services.Protocols.SoapException _sopEx) |
{ |
throw new Exception(_sopEx.Detail.InnerText); |
} |
catch (Exception ex) |
{ |
throw ex; |
} |
return wod_RtrnEntity; |
} |
#endregion |
} |
} |