Esempio n. 1
0
        /// <summary>
        /// This method is used to update the sharing permissions of a record granted to users as Read-Write, Read-only, or grant full access.
        /// </summary>
        /// <param name="moduleAPIName">The API Name of the module to update share permissions.</param>
        /// <param name="recordId">The ID of the record to be obtained.</param>
        public static void UpdateSharePermissions(string moduleAPIName, long recordId)
        {
            //example
            //string moduleAPIName = "Leads";
            //long recordId = 34770615177002;

            //Get instance of ShareRecordsOperations Class that takes recordId and moduleAPIName as parameter
            ShareRecordsOperations shareRecordsOperations = new ShareRecordsOperations(recordId, moduleAPIName);

            //Get instance of BodyWrapper Class that will contain the request body
            BodyWrapper request = new BodyWrapper();

            //List of ShareRecord instances
            List <ShareRecord> shareList = new List <ShareRecord>();

            //Get instance of ShareRecord Class
            ShareRecord share1 = new ShareRecord();

            share1.ShareRelatedRecords = true;

            share1.Permission = "full_access";

            User user = new User();

            user.Id = 34770615791024;

            share1.User = user;

            shareList.Add(share1);

            request.Share = shareList;

            //Call UpdateSharePermissions method that takes BodyWrapper instance as parameter
            APIResponse <ActionHandler> response = shareRecordsOperations.UpdateSharePermissions(request);

            if (response != null)
            {
                //Get the status code from response
                Console.WriteLine("Status Code: " + response.StatusCode);

                //Check if expected response is received
                if (response.IsExpected)
                {
                    //Get object from response
                    ActionHandler actionHandler = response.Object;

                    if (actionHandler is ActionWrapper)
                    {
                        //Get the received ActionWrapper instance
                        ActionWrapper actionWrapper = (ActionWrapper)actionHandler;

                        //Get the list of obtained ActionResponse instances
                        List <ActionResponse> actionResponses = actionWrapper.Share;

                        foreach (ActionResponse actionResponse in actionResponses)
                        {
                            //Check if the request is successful
                            if (actionResponse is SuccessResponse)
                            {
                                //Get the received SuccessResponse instance
                                SuccessResponse successResponse = (SuccessResponse)actionResponse;

                                //Get the Status
                                Console.WriteLine("Status: " + successResponse.Status.Value);

                                //Get the Code
                                Console.WriteLine("Code: " + successResponse.Code.Value);

                                Console.WriteLine("Details: ");

                                //Get the details map
                                foreach (KeyValuePair <string, object> entry in successResponse.Details)
                                {
                                    //Get each value in the map
                                    Console.WriteLine(entry.Key + ": " + JsonConvert.SerializeObject(entry.Value));
                                }

                                //Get the Message
                                Console.WriteLine("Message: " + successResponse.Message.Value);
                            }
                            //Check if the request returned an exception
                            else if (actionResponse is APIException)
                            {
                                //Get the received APIException instance
                                APIException exception = (APIException)actionResponse;

                                //Get the Status
                                Console.WriteLine("Status: " + exception.Status.Value);

                                //Get the Code
                                Console.WriteLine("Code: " + exception.Code.Value);

                                Console.WriteLine("Details: ");

                                //Get the details map
                                foreach (KeyValuePair <string, object> entry in exception.Details)
                                {
                                    //Get each value in the map
                                    Console.WriteLine(entry.Key + ": " + JsonConvert.SerializeObject(entry.Value));
                                }

                                //Get the Message
                                Console.WriteLine("Message: " + exception.Message.Value);
                            }
                        }
                    }
                    //Check if the request returned an exception
                    else if (actionHandler is APIException)
                    {
                        //Get the received APIException instance
                        APIException exception = (APIException)actionHandler;

                        //Get the Status
                        Console.WriteLine("Status: " + exception.Status.Value);

                        //Get the Code
                        Console.WriteLine("Code: " + exception.Code.Value);

                        Console.WriteLine("Details: ");

                        //Get the details map
                        foreach (KeyValuePair <string, object> entry in exception.Details)
                        {
                            //Get each value in the map
                            Console.WriteLine(entry.Key + ": " + JsonConvert.SerializeObject(entry.Value));
                        }

                        //Get the Message
                        Console.WriteLine("Message: " + exception.Message.Value);
                    }
                }
                else
                { //If response is not as expected
                    //Get model object from response
                    Model responseObject = response.Model;

                    //Get the response object's class
                    Type type = responseObject.GetType();

                    //Get all declared fields of the response class
                    Console.WriteLine("Type is: {0}", type.Name);

                    PropertyInfo[] props = type.GetProperties();

                    Console.WriteLine("Properties (N = {0}):", props.Length);

                    foreach (var prop in props)
                    {
                        if (prop.GetIndexParameters().Length == 0)
                        {
                            Console.WriteLine("{0} ({1}) : {2}", prop.Name, prop.PropertyType.Name, prop.GetValue(responseObject));
                        }
                        else
                        {
                            Console.WriteLine("{0} ({1}) : <Indexed>", prop.Name, prop.PropertyType.Name);
                        }
                    }
                }
            }
        }
        private void ParseError()
        {
            noMatchPanel.Visible           = true;
            recordPermissionsPanel.Visible = false;
            errorPanel.Visible             = false;
            retryPanel.Visible             = false;
            resolutionsListView.Items.Clear();

            scintilla1.StartStyling(0);
            scintilla1.SetStyling(scintilla1.TextLength, 0);

            if (ConnectionDetail == null)
            {
                MessageBox.Show("Please connect to an instance first", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            var regexes = new[]
            {
                // SecLib::AccessCheckEx failed. Returned hr = -2147187962, ObjectID: <guid>, OwnerId: <guid>,  OwnerIdType: <int> and CallingUser: <guid>. ObjectTypeCode: <int>, objectBusinessUnitId: <guid>, AccessRights: <accessrights>
                // Target: <objectid>,<objectidtype>
                // Principal: <callinguserid>
                // Privilege: <accessrights>,<objectidtype>
                // Depth: <objectid>,<callinguserid>
                new Regex("ObjectID: (?<objectid>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+), OwnerId: (?<ownerid>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+),  OwnerIdType: (?<owneridtype>[0-9]+) and CallingUser: (?<callinguser>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+). ObjectTypeCode: (?<objectidtype>[0-9]+), objectBusinessUnitId: (?<objectbusinessunitid>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+), AccessRights: (?<accessrights>[a-z]+)", RegexOptions.IgnoreCase),

                // SecLib::CheckPrivilege failed. User: <guid>, PrivilegeName: <prvName>, PrivilegeId: <guid>, Required Depth: <depth>, BusinessUnitId: <guid>, MetadataCache Privileges Count: <int>, User Privileges Count: <int>
                // Target: <privilegename>
                // Principal: <userid>
                // Privilege: <privilegename>
                // Depth: <privilegedepth>
                new Regex("User: (?<callinguser>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+), PrivilegeName: (?<privilegename>prv[a-z0-9_]+), PrivilegeId: (?<privilegeid>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+), Required Depth: (?<privilegedepth>Basic|Local|Deep|Global)", RegexOptions.IgnoreCase),

                // Principal user (Id=<guid>, type=<int>, roleCount=<int>, privilegeCount=<int>, accessMode=<int>), is missing <prvName> privilege (Id=<guid>) on OTC=<int> for entity '<logicalname>'. context.Caller=<guid>. Or identityUser.SystemUserId=<guid>, identityUser.Privileges.Count=<int>, identityUser.Roles.Count=<int> is missing <prvName> privilege (Id=<guid>) on OTC=<int> for entity '<logicalname>'.
                // Target: <logicalname>
                // Principal: <userid>
                // Privilege: <privilegename>
                // Depth: Basic
                new Regex("Principal user \\(Id=\\s*(?<callinguser>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+)(\\s*,\\s*type=(?<usertype>[0-9]+))?(\\s*,\\s*roleCount=[0-9]+)?(\\s*,\\s*privilegeCount=[0-9]+)?(\\s*,\\s*accessMode=[0-9]+)?\\)\\s*,?\\s*is missing (?<privilegename>prv[a-z0-9_]+)\\sprivilege( \\(Id=(?<privilegeid>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+)\\))?( on OTC=(?<objectidtype>[0-9]+))?( for entity '(?<objectidtypename>[a-z0-9_]+)')?", RegexOptions.IgnoreCase),

                // Principal with id <guid> does not have <accessrights> right(s) for record with id <guid> of entity <entityname>
                // Target <objectid>,<objectidtype>
                // Principal: <callinguserid>
                // Privilege: <accessrights>,<objectidtype>
                // Depth: <objectid>,<callinguserid>
                new Regex("Principal with id (?<callinguser>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+) does not have (?<accessrights>[a-z]+) right\\(s\\) for record with id (?<objectid>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+) of entity (?<objectidtype>[a-z0-9_]+)", RegexOptions.IgnoreCase),

                // RoleService::VerifyCallerPrivileges failed. User: <guid>, UserBU: <guid>, PrivilegeName: <privilegename>, PrivilegeId: <guid>, Depth: <depth>, BusinessUnitId: <guid>, MissingPrivilegeCount: 16
                // Target: None
                // Principal: <userid>
                // Privilege: <privilegename>
                // Depth: <privilegedepth>
                new Regex("User: (?<callinguser>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+), UserBU: ([a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+), PrivilegeName: (?<privilegename>prv[a-z0-9_]+), PrivilegeId: (?<privilegeid>[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+), Depth: (?<privilegedepth>Basic|Local|Deep|Global)", RegexOptions.IgnoreCase),
            };

            foreach (var regex in regexes)
            {
                var match = regex.Match(scintilla1.Text);

                if (match.Success)
                {
                    foreach (Group group in match.Groups)
                    {
                        if (Int32.TryParse(group.Name, out _))
                        {
                            continue;
                        }

                        scintilla1.StartStyling(group.Index);
                        scintilla1.SetStyling(group.Length, 1);
                    }

                    WorkAsync(new WorkAsyncInfo
                    {
                        Message = "Extracting Details...",
                        Work    = (bw, args) =>
                        {
                            var result = new Results();

                            try
                            {
                                // Extract the details from the error message
                                _principalReference = ExtractPrincipal(match, out var principalTypeDisplayName);
                                var target          = ExtractTarget(match, out var targetTypeDisplayName);
                                var privilege       = ExtractPrivilege(match, ref target, ref targetTypeDisplayName);
                                var privilegeDepth  = ExtractPrivilegeDepth(match, _principalReference, target);

                                result.Target = target;
                                result.PrincipalTypeDisplayName = principalTypeDisplayName;
                                result.TargetTypeDisplayName    = targetTypeDisplayName;
                                result.Privilege      = privilege;
                                result.PrivilegeDepth = privilegeDepth;

                                result.AccessRights = (AccessRights)privilege.GetAttributeValue <int>("accessright");
                                _targetReference    = target as EntityReference;
                                var privilegeRef    = privilege.ToEntityReference();
                                privilegeRef.Name   = privilege.GetAttributeValue <string>("name");

                                // Check if the problem should still exist
                                try
                                {
                                    if (_targetReference != null && result.AccessRights != AccessRights.None)
                                    {
                                        var recordAccess = (RetrievePrincipalAccessResponse)Service.Execute(new RetrievePrincipalAccessRequest
                                        {
                                            Principal = _principalReference,
                                            Target    = _targetReference
                                        });

                                        if ((recordAccess.AccessRights & result.AccessRights) == result.AccessRights)
                                        {
                                            result.HasAccess = true;
                                        }
                                    }
                                    else
                                    {
                                        var priv = (RetrieveUserPrivilegeByPrivilegeIdResponse)Service.Execute(new RetrieveUserPrivilegeByPrivilegeIdRequest
                                        {
                                            UserId      = _principalReference.Id,
                                            PrivilegeId = privilege.Id
                                        });

                                        if (priv.RolePrivileges.Any())
                                        {
                                            result.HasAccess = true;
                                        }
                                    }
                                }
                                catch (FaultException <OrganizationServiceFault> )
                                {
                                    // In case the service doesn't support the RetrieveUserPrivilegeByPrivilegeId message
                                }

                                // Check permission is available at the minimum calculated depth. If not, increase the depth
                                // to the next available value.
                                if (privilegeDepth == PrivilegeDepth.Basic && !privilege.GetAttributeValue <bool>("canbebasic"))
                                {
                                    privilegeDepth = PrivilegeDepth.Local;
                                }

                                if (privilegeDepth == PrivilegeDepth.Local && !privilege.GetAttributeValue <bool>("canbelocal"))
                                {
                                    privilegeDepth = PrivilegeDepth.Deep;
                                }

                                if (privilegeDepth == PrivilegeDepth.Deep && !privilege.GetAttributeValue <bool>("canbedeep"))
                                {
                                    privilegeDepth = PrivilegeDepth.Global;
                                }

                                switch (privilegeDepth)
                                {
                                case PrivilegeDepth.Basic:
                                    requiredDepthImage.Image = Properties.Resources.Basic;
                                    break;

                                case PrivilegeDepth.Local:
                                    requiredDepthImage.Image = Properties.Resources.Local;
                                    break;

                                case PrivilegeDepth.Deep:
                                    requiredDepthImage.Image = Properties.Resources.Deep;
                                    break;

                                case PrivilegeDepth.Global:
                                    requiredDepthImage.Image = Properties.Resources.Global;
                                    break;
                                }

                                var whoAmI = (WhoAmIResponse)Service.Execute(new WhoAmIRequest());
                                if (_principalReference.LogicalName == "systemuser" && _principalReference.Id == whoAmI.UserId)
                                {
                                    result.IsCurrentUser = true;
                                }
                                else
                                {
                                    var resolutions = new List <Resolution>();

                                    // Find roles that include the required permission and suggest to add them to the user
                                    var depthQuery = Math.Pow(2, (int)privilegeDepth);

                                    // Only suggest roles in the correct business unit
                                    var principal      = Service.Retrieve(_principalReference.LogicalName, _principalReference.Id, new ColumnSet("businessunitid"));
                                    var businessUnitId = principal.GetAttributeValue <EntityReference>("businessunitid").Id;

                                    var sufficientRoleQry = new FetchExpression($@"
                                    <fetch xmlns:generator='MarkMpn.SQL4CDS'>
                                        <entity name='role'>
                                            <attribute name='name' />
                                            <link-entity name='role' from='roleid' to='parentrootroleid'>
                                                <link-entity name='roleprivileges' to='roleid' from='roleid' alias='rp' link-type='inner'>
                                                    <filter>
                                                        <condition attribute='privilegeid' operator='eq' value='{privilege.Id}' />
                                                        <condition attribute='privilegedepthmask' operator='ge' value='{depthQuery}' />
                                                    </filter>
                                                </link-entity>
                                            </link-entity>
                                            <link-entity name='{_principalReference.LogicalName}roles' to='roleid' from='roleid' alias='sur' link-type='outer'>
                                                <filter>
                                                    <condition attribute='{_principalReference.LogicalName}id' operator='eq' value='{_principalReference.Id}' />
                                                </filter>
                                            </link-entity>
                                            <filter>
                                                <condition attribute='businessunitid' operator='eq' value='{businessUnitId}' />
                                                <condition entityname='sur' attribute='roleid' operator='null' />
                                            </filter>
                                            <order attribute='name' />
                                        </entity>
                                    </fetch>");
                                    var sufficientRoles   = Service.RetrieveMultiple(sufficientRoleQry);

                                    foreach (var role in sufficientRoles.Entities)
                                    {
                                        var roleRef  = role.ToEntityReference();
                                        roleRef.Name = role.GetAttributeValue <string>("name");

                                        var addRole = new AddSecurityRole
                                        {
                                            UserReference = _principalReference,
                                            RoleReference = roleRef
                                        };

                                        resolutions.Add(addRole);
                                    }

                                    // Find roles currently assigned to the user and suggest them to be edited to include the required permission
                                    var existingRoleQry = new FetchExpression($@"
                                    <fetch xmlns:generator='MarkMpn.SQL4CDS'>
                                        <entity name='role'>
                                            <attribute name='parentrootroleid' />
                                            <link-entity name='role' from='roleid' to='parentrootroleid'>
                                                <link-entity name='roleprivileges' to='roleid' from='roleid' alias='rp' link-type='outer'>
                                                    <attribute name='privilegedepthmask' />
                                                    <filter>
                                                        <condition attribute='privilegeid' operator='eq' value='{privilege.Id}' />
                                                    </filter>
                                                </link-entity>
                                            </link-entity>
                                            <link-entity name='{_principalReference.LogicalName}roles' to='roleid' from='roleid' alias='sur' link-type='inner'>
                                                <filter>
                                                    <condition attribute='{_principalReference.LogicalName}id' operator='eq' value='{_principalReference.Id}' />
                                                </filter>
                                            </link-entity>
                                            <filter type='or'>
                                                <condition entityname='rp' attribute='privilegeid' operator='null' />
                                                <condition entityname='rp' attribute='privilegedepthmask' operator='lt' value='{depthQuery}' />
                                            </filter>
                                            <order attribute='name' />
                                        </entity>
                                    </fetch>");
                                    var existingRoles   = Service.RetrieveMultiple(existingRoleQry);

                                    foreach (var role in existingRoles.Entities)
                                    {
                                        var roleRef       = role.GetAttributeValue <EntityReference>("parentrootroleid");
                                        var existingDepth = role.GetAttributeValue <AliasedValue>("rp.privilegedepthmask");

                                        var editRole = new EditSecurityRole
                                        {
                                            RoleReference      = roleRef,
                                            PrivilegeReference = privilegeRef,
                                            Depth         = privilegeDepth,
                                            ExistingDepth = existingDepth == null ? (PrivilegeDepth?)null : (PrivilegeDepth)Math.Log((int)existingDepth.Value, 2)
                                        };

                                        resolutions.Add(editRole);
                                    }

                                    if (_targetReference != null)
                                    {
                                        // Suggest sharing the record with the required permission
                                        var sharing = new ShareRecord
                                        {
                                            UserReference   = _principalReference,
                                            TargetReference = _targetReference,
                                            AccessRights    = result.AccessRights
                                        };

                                        resolutions.Add(sharing);
                                    }

                                    result.Resolutions = resolutions;
                                }
                            }
                            catch (Exception ex)
                            {
                                result.Exception = ex;
                            }

                            args.Result = result;
                        },
                        PostWorkCallBack = args =>
                        {
                            var result = (Results)args.Result;

                            if (result.Exception != null)
                            {
                                errorLabel.Text = result.Exception.Message;

                                // If this is an ObjectDoesNotExist error, it's likely that the error is from a different instance
                                unchecked
                                {
                                    if (result.Exception is FaultException <OrganizationServiceFault> fault && fault.Detail.ErrorCode == (int)0x80040217)
                                    {
                                        errorLabel.Text += "\r\n\r\nAre you connected to the same instance the error message came from?";
                                    }
                                }

                                noMatchPanel.Visible = false;
                                errorPanel.Visible   = true;
                            }
                            else
                            {
                                retryPanel.Visible = result.HasAccess;

                                var userPrefix         = $"The {result.PrincipalTypeDisplayName} ";
                                var userName           = _principalReference.Name;
                                userLinkLabel.Text     = userPrefix + userName;
                                userLinkLabel.LinkArea = new LinkArea(userPrefix.Length, userName.Length);

                                if (result.AccessRights == AccessRights.None)
                                {
                                    missingPrivilegeLinkLabel.Text = $"does not have {result.Privilege.GetAttributeValue<string>("name")} permission";
                                }
                                else
                                {
                                    missingPrivilegeLinkLabel.Text = $"does not have {result.AccessRights.ToString().Replace("Access", "")} permission";
                                }

                                if (result.Target == null)
                                {
                                    targetLinkLabel.Visible = false;
                                }
                                else
                                {
                                    var prefix = $"on the {result.TargetTypeDisplayName} ";
                                    var link   = "";

                                    if (_targetReference != null)
                                    {
                                        link = _targetReference.Name ?? "<Unnamed>";
                                    }
                                    else
                                    {
                                        prefix += String.Join(", ", ((EntityMetadataCollection)result.Target).Select(m => m.DisplayName.UserLocalizedLabel.Label));
                                    }

                                    targetLinkLabel.Text     = prefix + link;
                                    targetLinkLabel.LinkArea = new LinkArea(prefix.Length, link.Length);
                                    targetLinkLabel.Visible  = true;
                                }

                                requiredPrivilegeLabel.Text = $"To resolve this error, the user needs to be granted the {result.Privilege.GetAttributeValue<string>("name")} privilege to {result.PrivilegeDepth} depth";

                                if (result.IsCurrentUser)
                                {
                                    errorLabel.Text    = "Because you are the affected user you cannot use this tool to automatically resolve any security problems. Please ask a system administrator to run this tool on your behalf.";
                                    errorPanel.Visible = true;
                                }
                                else
                                {
                                    foreach (var resolution in result.Resolutions)
                                    {
                                        var lvi        = resolutionsListView.Items.Add(resolution.ToString());
                                        lvi.ImageIndex = resolution.ImageIndex;
                                        lvi.Tag        = resolution;
                                    }

                                    resolutionsListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
                                }

                                noMatchPanel.Visible           = false;
                                recordPermissionsPanel.Visible = true;
                            }
                        }
                    });