How can I update the roles for a given list of use

2019-06-13 06:02发布

问题:

I'm trying to move a whole load of users from 1 business unit to a newly setup 1. I'm not a CRM expert by any means, and I've been informed that to move the users we need to store their existing roles first, then move them to the new BU and then restore their roles that aren't BU specific and then modify the ones that are. An example for a user would be:

BU1
Roles:
BU1Admin
BU1Read-Only
Contracts

Move these to: BU2
Roles:
BU2Admin
BU2Read-Only
Contracts

So in this example the user would need to have their admin role and read-only role modified to BU2 but the contracts one stays the same. My initial thought was to construct a query to retrieve the systemuserid's along with the role names using something like this:

            Dictionary<Guid, string> UserRoles = new Dictionary<Guid, string>();

            QueryExpression query = new QueryExpression();
            query.EntityName = "systemuserroles";
            ColumnSet cols = new ColumnSet();
            cols.Attributes = new string[] { "systemuserid", "roleid" };
            query.ColumnSet = cols;

            LinkEntity le = new LinkEntity();
            le.LinkFromEntityName = "systemuserroles";
            le.LinkFromAttributeName = "roleid";
            le.LinkToEntityName = "role";
            le.LinkToAttributeName = "roleid";

            LinkEntity le2 = new LinkEntity();
            le2.LinkFromEntityName = "systemuserroles";
            le2.LinkFromAttributeName = "systemuserid";
            le2.LinkToEntityName = "systemuser";
            le2.LinkToAttributeName = "systemuserid";

            // Find only users in BU1
            ConditionExpression ce = new ConditionExpression();
            ce.AttributeName = "businessunitid";
            ce.Operator = ConditionOperator.Equal;
            ce.Values = new string[] { BU1 Guid };

            le2.LinkCriteria = new FilterExpression();
            le2.LinkCriteria.Conditions = new ConditionExpression[] { ce };

            query.LinkEntities = new LinkEntity[] { le, le2 };

            try
            {
                //This call doesn't work and fails saying RetrieveMultiple doesn't support entities of type systemuserroles.
                BusinessEntityCollection UserRolesCollection = crmService.RetrieveMultiple(query);
                foreach (BusinessEntity be in UserRolesCollection.BusinessEntities)
                {
                     //Do my stuff here     
                }
            }
            catch (SoapException se)
            {
                throw new Exception("Error occurred." + se.Detail);
            }

The next step, which I want to be able to do is then update the users with the new roles. I don't even know if this can be done based on the problems I'm having with the above. Any help would be much appreciated. I'm wondering if DynamicEntity would be of any use here after reading this?

UPDATE:

It looks like you can update a users roles using the AssignUserRolesRole Request class here. However, I'm still stuck on the retrieval bit at the moment as well. I'm wondering if I need to resort to sql?

回答1:

Here is an unsupported SQL solution for moving your users. I just tried it out and everything seems to work fine. You could try it out in a dev environment first to prove it works or it might be a starting point for your hybrid solution.

/* Switch Business Unit */
update su set businessunitid = (select top 1 businessunitid from businessunit where name = '[New Business Unit Name]')
--select fullname, businessunitidname 
from systemuser su
where businessunitidname =  '[Current Business Unit Name]'

/* Identifies users who have roles where the business unit does not match the user's business unit and updates them to match */
update bridge set roleid = newRole.roleid
--select su.fullname, oldRole.name, oldRole.businessunitidname, newRole.name, newRole.businessunitidname
from systemuser su
JOIN systemuserroles bridge ON su.systemuserid = bridge.systemuserid
JOIN [role] oldRole ON bridge.roleid = oldRole.roleid
JOIN [role] newRole ON oldRole.name = newRole.Name and newRole.BusinessUnitIdName = '[New Business Unit Name]'
where oldRole.BusinessUnitId <> su.BusinessUnitId


回答2:

I finally managed to find a way to do it through the API. It's essentially 2 queries, 1 to get the systemuserid of all the users and the second to get the roleids for each systemuserid. This will give me the current roles of all the users.

I create a dictionary that maps the old roles guids to the new roles guids.

I then change their business unit, which gets rid of all their roles, and using the dictionary and previously stored user roles I can then map to the new roles for each user. I came up with this code for anyone in a similar situation:

private void UpdateUsers()
{
    //Create mapping of old guid to new guid.
    Dictionary<Guid, Guid> LookupRoleGuids = new Dictionary<Guid, Guid>();
    //Add guid mapping
    LookupRoleGuids.Add(new Guid(Old Guid), new Guid(New Guid));

    QueryExpression query = new QueryExpression();
    query.EntityName = "systemuser";
    query.ColumnSet = new AllColumns();

    ConditionExpression ce2 = new ConditionExpression();
    ce2.AttributeName = "systemuserid";
    ce2.Operator = ConditionOperator.Equal;
    ce2.Values = new string[] { User Id }; //Can alter to retrieve for a BU

    FilterExpression filter = new FilterExpression();
    filter.Conditions = new ConditionExpression[] { ce2 };
    query.Criteria = filter;

    try
    {
        BusinessEntityCollection UserCollection = crmService.RetrieveMultiple(query);

        //store the roles of the Users.
        StoreUserRoles(UserCollection);

        foreach (BusinessEntity be in UserCollection.BusinessEntities)
        {
            //Update users BU.
            Guid newBu = new Guid(New BU Guid);
            systemuser su = (systemuser)be;
            SetBusinessSystemUserRequest setBUreq = new SetBusinessSystemUserRequest();
            setBUreq.BusinessId = newBu;
            setBUreq.UserId = su.systemuserid.Value;
            SecurityPrincipal assignee = new SecurityPrincipal();
            assignee.PrincipalId = new Guid(su.systemuserid.Value.ToString());
            setBUreq.ReassignPrincipal = assignee;
            SetBusinessSystemUserResponse assigned = (SetBusinessSystemUserResponse)crmService.Execute(setBUreq);

            //Get users existing roles
            if (UserRolesList.Count() > 0)
            {
                UserRoles ur = UserRolesList.Where(x => x.Value == su.systemuserid.Value)
                                            .Select(x => x).First();

                //Get new role guids based on mapping
                Guid[] roleguids = LookupRoleGuids.Where(x => ur.Roles.Contains(x.Key))
                                                  .Select(x => x.Value)
                                                  .ToArray();

                //Assign new roles 
                AssignUserRolesRoleRequest addRoles = new AssignUserRolesRoleRequest();
                addRoles.UserId = su.systemuserid.Value;
                addRoles.RoleIds = roleguids;
                crmService.Execute(addRoles);
            }
        }
    }
    catch (Exception ex)
    {
        throw new Exception("Error occurred updating users BU or assigning new roles. Error: " + ex.Message);
    }
}

public void StoreUserRoles(BusinessEntityCollection UserRolesCollection)
{
    UserRolesList = new List<UserRoles>(); 

    foreach (BusinessEntity be in UserRolesCollection.BusinessEntities)
    {
        systemuser u = (systemuser)be;
        UserRoles ur = new UserRoles();
        ur.Username = u.domainname;
        ur.Value = u.systemuserid.Value;
        UserRolesList.Add(ur);
    }

    AddRolesToList(ref UserRolesList);
}

private void AddRolesToList(ref List<UserRoles> URList)
{
    //Get all roles for a given user guid
    QueryExpression query = new QueryExpression();
    query.EntityName = "role";
    ColumnSet cols = new ColumnSet();
    cols.Attributes = new string[] { "roleid" };
    query.ColumnSet = cols;

    LinkEntity le = new LinkEntity();
    le.LinkFromEntityName = "role";
    le.LinkToEntityName = "systemuserroles";
    le.LinkFromAttributeName = "roleid";
    le.LinkToAttributeName = "roleid";

    LinkEntity le2 = new LinkEntity();
    le2.LinkFromEntityName = "systemuserroles";
    le2.LinkToEntityName = "systemuser";
    le2.LinkFromAttributeName = "systemuserid";
    le2.LinkToAttributeName = "systemuserid";

    foreach(UserRoles ur in URList)
    {
        //loop through the list of userroles and alter the conditional expression with the user's guid.
        ConditionExpression ce = new ConditionExpression();
        ce.AttributeName = "systemuserid";
        ce.Operator = ConditionOperator.Equal;
        ce.Values = new string[] { ur.Value.ToString() };

        le2.LinkCriteria = new FilterExpression();
        le2.LinkCriteria.Conditions = new ConditionExpression[] { ce };

        le.LinkEntities = new LinkEntity[] { le2 };
        query.LinkEntities = new LinkEntity[] { le };

        try
        {
            BusinessEntityCollection RoleGuidsCollection = crmService.RetrieveMultiple(query);
            foreach (BusinessEntity be in RoleGuidsCollection.BusinessEntities)
            {
                role r = (role)be;
                ur.Roles.Add(r.roleid.Value);
            }
        }
        catch (SoapException se)
        {
            throw new Exception("Error occurred retrieving Role Ids for " + ur.Username + " (" + ur.Value + "). " + se.Detail.InnerXml);
        }
        catch (Exception ex)
        {
            throw new Exception("Error occurred retrieving Role Guids for " + ur.Username + " (" + ur.Value + "). " + ex.Message);
        }
    }
}

To store the roles for each user I've created a class called UserRoles:

class UserRoles
{
    public string Username { get; set; }
    public Guid Value { get; set; }
    public List<Guid> Roles { get; set; }

    public UserRoles()
    {
        Roles = new List<Guid>();
    }
}

NOTE: This has not been fully tested, but does seem to do what I want, which is allow me to move a user to a different BU and set new roles for them based on their old ones.