Skip to content

Commit

Permalink
added AccountService and removed EF dependency in OpentIddict-UI Iden…
Browse files Browse the repository at this point in the history
…tity Api project
  • Loading branch information
thomasduft committed Jan 26, 2022
1 parent ecd1b8c commit 0297762
Show file tree
Hide file tree
Showing 20 changed files with 317 additions and 134 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ services.AddOpenIddict()
}
})
// Register the EF based UI Store for the ASP.NET Identity related entities.
.AddUIIdentityStore(options =>
.AddUIIdentityStore<ApplicationUser>(options =>
{
options.OpenIddictUIIdentityContext = builder =>
builder.UseSqlite(Configuration.GetConnectionString("DefaultConnection"),
Expand Down
18 changes: 9 additions & 9 deletions notes.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
TODO:

- go full razor pages
- https://github.com/openiddict/openiddict-samples/tree/dev/samples/Contruum/Contruum.Server


- migrate to .net Program.cs file
- refactor DB-migrations in sample server


- load options.Permissions from database instead of configuring it
- for next release 1.5.0
- mention small API change when registering .AddUIIdentityStore<ApplicationUser> the TApplicationUser is not mandatory!


- client refactoring
Expand All @@ -18,6 +11,13 @@ TODO:
- check server stylings not removed


- load options.Permissions from database instead of configuring it


- go full razor pages
- https://github.com/openiddict/openiddict-samples/tree/dev/samples/Contruum/Contruum.Server


- organize APIs a bit more by feature
- i.e. identity -> AccountController
- AccountController
Expand Down
2 changes: 1 addition & 1 deletion samples/Server/ConfigureServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ string environmentName
};
})
// Register the EF based UI Store for the ASP.NET Identity related entities.
.AddUIIdentityStore(options =>
.AddUIIdentityStore<ApplicationUser>(options =>
{
options.OpenIddictUIIdentityContext = builder =>
builder.UseSqlite(configuration.GetConnectionString("DefaultConnection"),
Expand Down
116 changes: 23 additions & 93 deletions src/identity/OpenIddict.UI.Identity.Api/Account/AccountApiService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

using tomware.OpenIddict.UI.Identity.Core;
using tomware.OpenIddict.UI.Suite.Core;

namespace tomware.OpenIddict.UI.Identity.Api
{
public interface IAccountApiService
Expand All @@ -21,50 +22,36 @@ public interface IAccountApiService
public class AccountApiService<TIdentityUser> : IAccountApiService
where TIdentityUser : IdentityUser, new()
{
// TODO: move UserManager down to Infrastructure
// using Microsoft.EntityFrameworkCore is wrong in the Api layer!
// remove package dependency <PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.0.5" />
private readonly UserManager<TIdentityUser> _manager;
private readonly IUserCreationStrategy<TIdentityUser> _userCreationStrategy;
private readonly IAccountService _accountService;

public AccountApiService(
UserManager<TIdentityUser> manager,
IUserCreationStrategy<TIdentityUser> userCreationStrategy
IAccountService accountService
)
{
_manager = manager
?? throw new ArgumentNullException(nameof(manager));
_userCreationStrategy = userCreationStrategy
?? throw new ArgumentNullException(nameof(userCreationStrategy));
_accountService = accountService
?? throw new ArgumentNullException(nameof(accountService));
}

public async Task<IdentityResult> RegisterAsync(
RegisterUserViewModel model
)
{
var identiyUser = _userCreationStrategy.CreateUser(model);
var param = SimpleMapper.From<RegisterUserViewModel, RegisterUserParam>(model);

return await _manager.CreateAsync(identiyUser, model.Password);
return await _accountService.RegisterAsync(param);
}

public async Task<IdentityResult> ChangePasswordAsync(ChangePasswordViewModel model)
{
var user = await _manager.FindByNameAsync(model.UserName);
var param = SimpleMapper.From<ChangePasswordViewModel, ChangePasswordParam>(model);

return await _manager.ChangePasswordAsync(
user,
model.CurrentPassword,
model.NewPassword
);
return await _accountService.ChangePasswordAsync(param);
}

public async Task<IEnumerable<UserViewModel>> GetUsersAsync()
{
// TODO: Paging ???
var items = await _manager.Users
.OrderBy(u => u.UserName)
.AsNoTracking()
.ToListAsync();
var items = await _accountService.GetUsersAsync();

return items.Select(u => new UserViewModel
{
Expand All @@ -77,25 +64,21 @@ public async Task<IEnumerable<UserViewModel>> GetUsersAsync()

public async Task<UserViewModel> GetUserAsync(string id)
{
var user = await _manager.FindByIdAsync(id);
var roles = await _manager.GetRolesAsync(user);
var claims = await _manager.GetClaimsAsync(user);

var isLockedOut = await _manager.IsLockedOutAsync(user);
var user = await _accountService.GetUserAsync(id);

return new UserViewModel
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
LockoutEnabled = user.LockoutEnabled,
IsLockedOut = isLockedOut,
Claims = new List<ClaimViewModel>(claims.ToList().Select(x => new ClaimViewModel
IsLockedOut = user.IsLockedOut,
Claims = new List<ClaimViewModel>(user.Claims.Select(x => new ClaimViewModel
{
Type = x.Type,
Value = x.Value
})),
Roles = roles.ToList()
Roles = user.Roles
};
}

Expand All @@ -105,74 +88,21 @@ public async Task<IdentityResult> UpdateAsync(UserViewModel model)
if (string.IsNullOrWhiteSpace(model.Id))
throw new InvalidOperationException(nameof(model.Id));

var user = await _manager.FindByIdAsync(model.Id);
user.UserName = model.UserName;
user.Email = model.Email;
user.LockoutEnabled = model.LockoutEnabled;

var result = await _manager.UpdateAsync(user);
if (!result.Succeeded)
{
return result;
}

result = await AssignClaimsAsync(
user,
model.Claims.Select(x => new Claim(x.Type, x.Value)).ToList()
);
if (!result.Succeeded)
{
return result;
}

result = await AssignRolesAsync(user, model.Roles);
if (!result.Succeeded)
var param = SimpleMapper.From<UserViewModel, UserParam>(model);
param.Claims = new List<ClaimInfo>(model.Claims.Select(c => new ClaimInfo
{
return result;
}
Type = c.Type,
Value = c.Value
}));

return result;
return await _accountService.UpdateAsync(param);
}

public async Task<IdentityResult> DeleteAsync(string id)
{
if (string.IsNullOrWhiteSpace(id)) throw new InvalidOperationException(nameof(id));

var user = await _manager.FindByIdAsync(id);

return await _manager.DeleteAsync(user);
}

private async Task<IdentityResult> AssignClaimsAsync(
TIdentityUser user,
IEnumerable<Claim> claims
)
{
// removing all claims
var existingClaims = await _manager.GetClaimsAsync(user);
await _manager.RemoveClaimsAsync(user, existingClaims);

// assigning claims
return await _manager.AddClaimsAsync(
user,
claims
);
}

private async Task<IdentityResult> AssignRolesAsync(
TIdentityUser user,
IEnumerable<string> roles
)
{
// removing all roles
var existingRoles = await _manager.GetRolesAsync(user);
await _manager.RemoveFromRolesAsync(user, existingRoles);

// assigning roles
return await _manager.AddToRolesAsync(
user,
roles
);
return await _accountService.DeleteAsync(id);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authorization;
using tomware.OpenIddict.UI.Suite.Api;
using tomware.OpenIddict.UI.Identity.Core;

namespace tomware.OpenIddict.UI.Identity.Api
{
Expand Down Expand Up @@ -35,7 +36,7 @@ this OpenIddictBuilder builder
) where TApplicationUser : IdentityUser, new()
{
builder.Services
.AddTransient<IUserCreationStrategy<TApplicationUser>, UserNameUserCreationStrategy<TApplicationUser>>();
.AddUserNameUserCreationStrategy<TApplicationUser>();

return builder;
}
Expand All @@ -44,9 +45,8 @@ private static IServiceCollection AddApiServices<TApplicationUser>(
this IServiceCollection services
) where TApplicationUser : IdentityUser, new()
{
services.AddTransient<IUserCreationStrategy<TApplicationUser>, EmailUserCreationStrategy<TApplicationUser>>();
services.AddTransient<IAccountApiService, AccountApiService<TApplicationUser>>();
services.AddTransient<IRoleService, RoleService>();
services.AddTransient<IRoleApiService, RoleApiService>();
services.AddTransient<IClaimTypeApiService, ClaimTypeApiService>();

return services;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace tomware.OpenIddict.UI.Identity.Api
{
public interface IRoleService
public interface IRoleApiService
{
Task<IEnumerable<RoleViewModel>> GetRolesAsync();

Expand All @@ -19,11 +19,11 @@ public interface IRoleService
Task DeleteAsync(string id);
}

public class RoleService : IRoleService
public class RoleApiService : IRoleApiService
{
private readonly RoleManager<IdentityRole> _manager;

public RoleService(RoleManager<IdentityRole> manager)
public RoleApiService(RoleManager<IdentityRole> manager)
{
_manager = manager;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ namespace tomware.OpenIddict.UI.Identity.Api
[Route("roles")]
public class RoleController : IdentityApiController
{
private readonly IRoleService _service;
private readonly IRoleApiService _service;

public RoleController(IRoleService service)
public RoleController(IRoleApiService service)
{
_service = service;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="6.0.1" />
<PackageReference Include="OpenIddict.AspNetCore" Version="3.1.1" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.1.1" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace tomware.OpenIddict.UI.Identity.Core
{
public class ChangePasswordParam
{
public string CurrentPassword { get; set; }
public string NewPassword { get; set; }
public string UserName { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace tomware.OpenIddict.UI.Identity.Core
{
public class RegisterUserParam
{
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
}
27 changes: 27 additions & 0 deletions src/identity/OpenIddict.UI.Identity.Core/DTOs/UserInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Generic;

namespace tomware.OpenIddict.UI.Identity.Core
{
public class ClaimInfo
{
public string Type { get; set; }
public string Value { get; set; }
}

public class UserInfo
{
public string Id { get; set; }

public string UserName { get; set; }

public string Email { get; set; }

public bool LockoutEnabled { get; set; }

public bool IsLockedOut { get; set; }

public List<ClaimInfo> Claims { get; set; } = new List<ClaimInfo>();

public List<string> Roles { get; set; } = new List<string>();
}
}
4 changes: 4 additions & 0 deletions src/identity/OpenIddict.UI.Identity.Core/DTOs/UserParam.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace tomware.OpenIddict.UI.Identity.Core
{
public class UserParam : UserInfo { }
}
19 changes: 17 additions & 2 deletions src/identity/OpenIddict.UI.Identity.Core/DependencyInjection.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;

namespace tomware.OpenIddict.UI.Identity.Core
{
[ExcludeFromCodeCoverage]
public static class OpenIddictUIIdentityCoreServicesExtensions
{
public static IServiceCollection AddOpenIddictUIIdentityCoreServices(
public static IServiceCollection AddOpenIddictUIIdentityCoreServices<TApplicationUser>(
this IServiceCollection services
)
) where TApplicationUser : IdentityUser, new()
{
services.AddTransient<IClaimTypeService, ClaimTypeService>();
services.AddTransient<IUserCreationStrategy<TApplicationUser>, EmailUserCreationStrategy<TApplicationUser>>();

return services;
}

/// <summary>
/// Registers the UserName to UserName UserCreationStrategy.
/// </summary>
public static IServiceCollection AddUserNameUserCreationStrategy<TApplicationUser>(
this IServiceCollection services
) where TApplicationUser : IdentityUser, new()
{
services
.AddTransient<IUserCreationStrategy<TApplicationUser>, UserNameUserCreationStrategy<TApplicationUser>>();

return services;
}
Expand Down
Loading

0 comments on commit 0297762

Please sign in to comment.