mirror of
https://github.com/bitwarden/server.git
synced 2024-11-24 12:35:25 +01:00
Move identity endpoints to Identity service (#1807)
This commit is contained in:
parent
56ee3bd290
commit
0def1830af
@ -81,8 +81,11 @@ EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedWeb", "src\SharedWeb\SharedWeb.csproj", "{713D44C0-1BC1-4024-96A3-A98A49F33908}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.EntityFramework", "src\Infrastructure.EntityFramework\Infrastructure.EntityFramework.csproj", "{ED880735-0250-43C7-9662-FDC7C7416E7F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Test", "test\Billing.Test\Billing.Test.csproj", "{B8639B10-2157-44BC-8CE1-D9EB4B50971F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity.Test", "test\Identity.Test\Identity.Test.csproj", "{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -195,6 +198,10 @@ Global
|
||||
{B8639B10-2157-44BC-8CE1-D9EB4B50971F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B8639B10-2157-44BC-8CE1-D9EB4B50971F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B8639B10-2157-44BC-8CE1-D9EB4B50971F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -226,6 +233,7 @@ Global
|
||||
{713D44C0-1BC1-4024-96A3-A98A49F33908} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
||||
{ED880735-0250-43C7-9662-FDC7C7416E7F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
||||
{B8639B10-2157-44BC-8CE1-D9EB4B50971F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||
{310A1D8E-2D3F-4FA0-84D4-FFE31FCE193E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
||||
|
@ -11,6 +11,8 @@ using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Api.Request.Accounts;
|
||||
using Bit.Core.Models.Api.Response.Accounts;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
@ -64,6 +66,9 @@ namespace Bit.Api.Controllers
|
||||
_sendService = sendService;
|
||||
}
|
||||
|
||||
#region DEPRECATED (Moved to Identity Service)
|
||||
|
||||
[Obsolete("2022-01-12 Moved to Identity, left for backwards compatability with older clients")]
|
||||
[HttpPost("prelogin")]
|
||||
[AllowAnonymous]
|
||||
public async Task<PreloginResponseModel> PostPrelogin([FromBody] PreloginRequestModel model)
|
||||
@ -74,12 +79,13 @@ namespace Bit.Api.Controllers
|
||||
kdfInformation = new UserKdfInformation
|
||||
{
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = 100000
|
||||
KdfIterations = 100000,
|
||||
};
|
||||
}
|
||||
return new PreloginResponseModel(kdfInformation);
|
||||
}
|
||||
|
||||
[Obsolete("2022-01-12 Moved to Identity, left for backwards compatability with older clients")]
|
||||
[HttpPost("register")]
|
||||
[AllowAnonymous]
|
||||
[CaptchaProtected]
|
||||
@ -101,6 +107,8 @@ namespace Bit.Api.Controllers
|
||||
throw new BadRequestException(ModelState);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[HttpPost("password-hint")]
|
||||
[AllowAnonymous]
|
||||
public async Task PostPasswordHint([FromBody] PasswordHintRequestModel model)
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api.Request.Accounts;
|
||||
|
||||
namespace Bit.Api.Models.Request.Accounts
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api.Request.Accounts;
|
||||
|
||||
namespace Bit.Api.Models.Request.Accounts
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Api.Models.Request.Accounts
|
||||
namespace Bit.Core.Models.Api.Request.Accounts
|
||||
{
|
||||
public class KeysRequestModel
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Api.Models.Request.Accounts
|
||||
namespace Bit.Core.Models.Api.Request.Accounts
|
||||
{
|
||||
public class PreloginRequestModel
|
||||
{
|
@ -3,11 +3,10 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Models.Request.Accounts
|
||||
namespace Bit.Core.Models.Api.Request.Accounts
|
||||
{
|
||||
public class RegisterRequestModel : IValidatableObject, ICaptchaProtectedModel
|
||||
{
|
@ -1,7 +1,7 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Api.Models.Response
|
||||
namespace Bit.Core.Models.Api.Response.Accounts
|
||||
{
|
||||
public class PreloginResponseModel
|
||||
{
|
70
src/Identity/Controllers/AccountsController.cs
Normal file
70
src/Identity/Controllers/AccountsController.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Api.Request.Accounts;
|
||||
using Bit.Core.Models.Api.Response.Accounts;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Identity.Controllers
|
||||
{
|
||||
[Route("accounts")]
|
||||
public class AccountsController : Controller
|
||||
{
|
||||
private readonly ILogger<AccountsController> _logger;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public AccountsController(
|
||||
ILogger<AccountsController> logger,
|
||||
IUserRepository userRepository,
|
||||
IUserService userService)
|
||||
{
|
||||
_logger = logger;
|
||||
_userRepository = userRepository;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
// Moved from API, If you modify this endpoint, please update Identity as well.
|
||||
[HttpPost("register")]
|
||||
[CaptchaProtected]
|
||||
public async Task PostRegister([FromBody] RegisterRequestModel model)
|
||||
{
|
||||
var result = await _userService.RegisterUserAsync(model.ToUser(), model.MasterPasswordHash,
|
||||
model.Token, model.OrganizationUserId);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName"))
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException(ModelState);
|
||||
}
|
||||
|
||||
// Moved from API, If you modify this endpoint, please update Identity as well.
|
||||
[HttpPost("prelogin")]
|
||||
public async Task<PreloginResponseModel> PostPrelogin([FromBody] PreloginRequestModel model)
|
||||
{
|
||||
var kdfInformation = await _userRepository.GetKdfInformationByEmailAsync(model.Email);
|
||||
if (kdfInformation == null)
|
||||
{
|
||||
kdfInformation = new UserKdfInformation
|
||||
{
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = 100000,
|
||||
};
|
||||
}
|
||||
return new PreloginResponseModel(kdfInformation);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ using Bit.Identity.Models;
|
||||
using IdentityModel;
|
||||
using IdentityServer4;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
@ -20,31 +19,28 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Identity.Controllers
|
||||
{
|
||||
public class AccountController : Controller
|
||||
// TODO: 2022-01-12, Remove account alias
|
||||
[Route("account/[action]")]
|
||||
[Route("sso/[action]")]
|
||||
public class SsoController : Controller
|
||||
{
|
||||
private readonly IClientStore _clientStore;
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly ILogger<AccountController> _logger;
|
||||
private readonly ILogger<SsoController> _logger;
|
||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IHttpClientFactory _clientFactory;
|
||||
|
||||
public AccountController(
|
||||
IClientStore clientStore,
|
||||
public SsoController(
|
||||
IIdentityServerInteractionService interaction,
|
||||
ILogger<AccountController> logger,
|
||||
ILogger<SsoController> logger,
|
||||
ISsoConfigRepository ssoConfigRepository,
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IHttpClientFactory clientFactory)
|
||||
{
|
||||
_clientStore = clientStore;
|
||||
_interaction = interaction;
|
||||
_logger = logger;
|
||||
_ssoConfigRepository = ssoConfigRepository;
|
||||
_userRepository = userRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
_clientFactory = clientFactory;
|
||||
}
|
||||
|
||||
@ -272,7 +268,7 @@ namespace Bit.Identity.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNativeClient(IdentityServer4.Models.AuthorizationRequest context)
|
||||
private bool IsNativeClient(IdentityServer4.Models.AuthorizationRequest context)
|
||||
{
|
||||
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
|
||||
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
|
@ -3,10 +3,10 @@ using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Api.Controllers;
|
||||
using Bit.Api.Models.Request.Accounts;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Api.Request.Accounts;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -27,7 +27,6 @@ namespace Bit.Api.Test.Controllers
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly ISsoUserRepository _ssoUserRepository;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ISendRepository _sendRepository;
|
||||
|
115
test/Identity.Test/Controllers/AccountsControllerTests.cs
Normal file
115
test/Identity.Test/Controllers/AccountsControllerTests.cs
Normal file
@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Api.Request.Accounts;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Identity.Controllers;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Identity.Test.Controllers
|
||||
{
|
||||
public class AccountsControllerTests : IDisposable
|
||||
{
|
||||
|
||||
private readonly AccountsController _sut;
|
||||
private readonly ILogger<AccountsController> _logger;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public AccountsControllerTests()
|
||||
{
|
||||
_logger = Substitute.For<ILogger<AccountsController>>();
|
||||
_userRepository = Substitute.For<IUserRepository>();
|
||||
_userService = Substitute.For<IUserService>();
|
||||
_sut = new AccountsController(
|
||||
_logger,
|
||||
_userRepository,
|
||||
_userService
|
||||
);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_sut?.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPrelogin_WhenUserExists_ShouldReturnUserKdfInfo()
|
||||
{
|
||||
var userKdfInfo = new UserKdfInformation
|
||||
{
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = 5000
|
||||
};
|
||||
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult(userKdfInfo));
|
||||
|
||||
var response = await _sut.PostPrelogin(new PreloginRequestModel { Email = "user@example.com" });
|
||||
|
||||
Assert.Equal(userKdfInfo.Kdf, response.Kdf);
|
||||
Assert.Equal(userKdfInfo.KdfIterations, response.KdfIterations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPrelogin_WhenUserDoesNotExist_ShouldDefaultToSha256And100000Iterations()
|
||||
{
|
||||
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult((UserKdfInformation)null));
|
||||
|
||||
var response = await _sut.PostPrelogin(new PreloginRequestModel { Email = "user@example.com" });
|
||||
|
||||
Assert.Equal(KdfType.PBKDF2_SHA256, response.Kdf);
|
||||
Assert.Equal(100000, response.KdfIterations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRegister_ShouldRegisterUser()
|
||||
{
|
||||
var passwordHash = "abcdef";
|
||||
var token = "123456";
|
||||
var userGuid = new Guid();
|
||||
_userService.RegisterUserAsync(Arg.Any<User>(), passwordHash, token, userGuid)
|
||||
.Returns(Task.FromResult(IdentityResult.Success));
|
||||
var request = new RegisterRequestModel
|
||||
{
|
||||
Name = "Example User",
|
||||
Email = "user@example.com",
|
||||
MasterPasswordHash = passwordHash,
|
||||
MasterPasswordHint = "example",
|
||||
Token = token,
|
||||
OrganizationUserId = userGuid
|
||||
};
|
||||
|
||||
await _sut.PostRegister(request);
|
||||
|
||||
await _userService.Received(1).RegisterUserAsync(Arg.Any<User>(), passwordHash, token, userGuid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRegister_WhenUserServiceFails_ShouldThrowBadRequestException()
|
||||
{
|
||||
var passwordHash = "abcdef";
|
||||
var token = "123456";
|
||||
var userGuid = new Guid();
|
||||
_userService.RegisterUserAsync(Arg.Any<User>(), passwordHash, token, userGuid)
|
||||
.Returns(Task.FromResult(IdentityResult.Failed()));
|
||||
var request = new RegisterRequestModel
|
||||
{
|
||||
Name = "Example User",
|
||||
Email = "user@example.com",
|
||||
MasterPasswordHash = passwordHash,
|
||||
MasterPasswordHint = "example",
|
||||
Token = token,
|
||||
OrganizationUserId = userGuid
|
||||
};
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => _sut.PostRegister(request));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
test/Identity.Test/Identity.Test.csproj
Normal file
29
test/Identity.Test/Identity.Test.csproj
Normal file
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="NSubstitute" Version="4.2.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.14.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Core\Core.csproj" />
|
||||
<ProjectReference Include="..\..\src\Identity\Identity.csproj" />
|
||||
<ProjectReference Include="..\common\Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user