|
| 1 | +# Switching Between Organization Units |
| 2 | + |
| 3 | +In ASP.NET Zero projects, the ability for users to switch between different organizational units is essential, especially for multi unit organizations.Allowing users to select and change their active unit during their sessions enhances both the user experience and operational efficiency. However, managing this transition correctly and securely can often be complex. |
| 4 | + |
| 5 | +## Implementing of Organization Unit Switching Between |
| 6 | + |
| 7 | +In this section, we'll delve into the implementation details of enabling users to switch between different organizational units within an ASP.NET Zero project. |
| 8 | + |
| 9 | +### Creating Claims Principal for Organization Unit |
| 10 | + |
| 11 | +In this section, we create this functionality by overriding the **CreateAsync** function within the `UserClaimsPrincipalFactory` class. This modification ensures that we can access the organizational unit information for logged-in users. |
| 12 | + |
| 13 | +```csharp |
| 14 | +public override async Task<ClaimsPrincipal> CreateAsync(User user) |
| 15 | +{ |
| 16 | + var userWithOrgUnits = await GetUserWithOrganizationUnitsAsync(user.Id); |
| 17 | + |
| 18 | + var claim = await base.CreateAsync(userWithOrgUnits); |
| 19 | + |
| 20 | + string organizationUnitsJson = JsonSerializer.Serialize(userWithOrgUnits.OrganizationUnits.First()); |
| 21 | + |
| 22 | + claim.Identities.First().AddClaim(new Claim("Organization_Unit", organizationUnitsJson)); |
| 23 | + |
| 24 | + return claim; |
| 25 | +} |
| 26 | +``` |
| 27 | + |
| 28 | +The first code snippet overrides the **CreateAsync** method to incorporate organization unit information into the claims principal. |
| 29 | + |
| 30 | +Within this context, we add the first element of the Organization Unit as a claim during user principal creation. |
| 31 | + |
| 32 | +```csharp |
| 33 | +private async Task<User> GetUserWithOrganizationUnitsAsync(long userId) |
| 34 | +{ |
| 35 | + return await UserManager.Users |
| 36 | + .Include(u => u.OrganizationUnits) |
| 37 | + .Where(x => x.Id == userId) |
| 38 | + .FirstOrDefaultAsync(); |
| 39 | +} |
| 40 | +``` |
| 41 | + |
| 42 | +The second snippet demonstrates the asynchronous method GetUserWithOrganizationUnitsAsync, which retrieves a user with their associated organization units from the database. |
| 43 | + |
| 44 | +### Modifying the Organization Unit in Session |
| 45 | + |
| 46 | +Here, we implement functionality to modify the organizational unit within the session, following the guidelines outlined in this [document](https://aspnetboilerplate.com/Pages/Documents/Articles%5CHow-To%5Cadd-custom-session-field-aspnet-core). |
| 47 | + |
| 48 | +```csharp |
| 49 | +public class MyAppSession : ClaimsAbpSession, ITransientDependency |
| 50 | +{ |
| 51 | + private const string OrganizationUnitClaimTypeName = "Organization_Unit"; |
| 52 | + private readonly UserManager _userManager; |
| 53 | + public MyAppSession( |
| 54 | + IPrincipalAccessor principalAccessor, |
| 55 | + IMultiTenancyConfig multiTenancy, |
| 56 | + ITenantResolver tenantResolver, |
| 57 | + IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider, |
| 58 | + UserManager userManager) : |
| 59 | + base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider) |
| 60 | + { |
| 61 | + _userManager = userManager; |
| 62 | + } |
| 63 | + |
| 64 | + public string OrganizationUnit => PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == OrganizationUnitClaimTypeName)?.Value; |
| 65 | + |
| 66 | + public async Task SwitchOrganizationUnitAsync(UserOrganizationUnit newUserOrganizationUnit) |
| 67 | + { |
| 68 | + var oldOrganizationUnit = JsonSerializer.Deserialize<UserOrganizationUnit>(OrganizationUnit); |
| 69 | + |
| 70 | + SetOrganizationUnit(newUserOrganizationUnit); |
| 71 | + |
| 72 | + await _userManager.RemoveFromOrganizationUnitAsync(oldOrganizationUnit.UserId, oldOrganizationUnit.OrganizationUnitId); |
| 73 | + } |
| 74 | + |
| 75 | + private void SetOrganizationUnit(string newOrganizationUnit) |
| 76 | + { |
| 77 | + var identity = PrincipalAccessor.Principal?.Identity as ClaimsIdentity; |
| 78 | + if (identity != null) |
| 79 | + { |
| 80 | + var oldOrganizationUnitClaim = identity.FindFirst(OrganizationUnitClaimTypeName); |
| 81 | + if (oldOrganizationUnitClaim != null) |
| 82 | + { |
| 83 | + identity.RemoveClaim(oldOrganizationUnitClaim); |
| 84 | + } |
| 85 | + |
| 86 | + identity.AddClaim(new Claim(OrganizationUnitClaimTypeName, newOrganizationUnit)); |
| 87 | + } |
| 88 | + } |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +With these additions, the `MyAppSession` class now includes methods to both retrieve and set the organization unit information in the session. This enables management of organization unit data throughout the user's session. |
| 93 | + |
| 94 | +### Usage Example: Switching Organization Units |
| 95 | + |
| 96 | +In this section, we will demonstrate how to use a method for switching a user's organization unit within a new service class. The following example shows the `OrganizationUnitManager` class, which uses `MyAppSession` to allow a user to be removed from their current organization unit and added to a new one. |
| 97 | + |
| 98 | +```csharp |
| 99 | +public class OrganizationUnitManager |
| 100 | +{ |
| 101 | + private readonly MyAppSession _myAppSession; |
| 102 | + private readonly UserManager _userManager; |
| 103 | + |
| 104 | + public OrganizationUnitManager(MyAppSession myAppSession, UserManager userManager) |
| 105 | + { |
| 106 | + _myAppSession = myAppSession; |
| 107 | + _userManager = userManager; |
| 108 | + } |
| 109 | + |
| 110 | + public async Task SwitchOrganizationUnitAsync(UserOrganizationUnit newUserOrganizationUnit) |
| 111 | + { |
| 112 | + var oldOrganizationUnit = JsonSerializer.Deserialize<UserOrganizationUnit>(_myAppSession.OrganizationUnit); |
| 113 | + |
| 114 | + _myAppSession.SetOrganizationUnit(newUserOrganizationUnit); |
| 115 | + |
| 116 | + if (oldOrganizationUnit != null) |
| 117 | + { |
| 118 | + await _userManager.RemoveFromOrganizationUnitAsync(oldOrganizationUnit.UserId, oldOrganizationUnit.OrganizationUnitId); |
| 119 | + } |
| 120 | + |
| 121 | + await _userManager.AddToOrganizationUnitAsync(newUserOrganizationUnit.UserId, newUserOrganizationUnit.OrganizationUnitId); |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +#### Usage Steps |
| 127 | + |
| 128 | +1. **Retrieve the Current Organization Unit:** The oldOrganizationUnit is deserialized from the current session's OrganizationUnit claim. |
| 129 | +2. **Set the New Organization Unit:** The SetOrganizationUnit method of MyAppSession is called to set the new organization unit in the session. |
| 130 | +3. **Remove from the Old Unit:** The user is removed from the old organization unit using _userManager.RemoveFromOrganizationUnitAsync. |
| 131 | +4. **Add to the New Unit:** The user is added to the new organization unit using _userManager.AddToOrganizationUnitAsync. |
| 132 | + |
| 133 | + |
| 134 | +## Conclusion |
| 135 | + |
| 136 | +Switching between organizational units in ASP.NET Zero projects can increase your organization's flexibility in transitions between organizations and facilitate organizational management. With a custom session, it is possible to switch between the user's current organization information and the organization. This work can also simplify organizational operational processes. |
0 commit comments