mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01:00
[Provider] Setup provider (#1378)
This commit is contained in:
parent
08f508f536
commit
43f7271147
2
SETUP.md
2
SETUP.md
@ -13,6 +13,8 @@ Each service is built and run separately. The Bitwarden clients can use differen
|
||||
|
||||
This means that you don't need to run all services locally for a development environment. You can run only those services that you intend to modify, and use Bitwarden.com or a self-hosted instance for all other services required.
|
||||
|
||||
By default some of the services depends on the Bitwarden licensed `CommCore`, however it can easily be disabled by including the `/p:DefineConstants="OSS"` as an argument to `dotnet`.
|
||||
|
||||
# Local Development Environment Setup
|
||||
|
||||
This guide will show you how to set up the Api, Identity and SQL projects for development. These are the minimum projects for any development work. You may need to set up additional projects depending on the changes you want to make.
|
||||
|
@ -61,7 +61,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Portal", "bitwarden_license
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sso", "bitwarden_license\src\Sso\Sso.csproj", "{4866AF64-6640-4C65-A662-A31E02FF9064}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Icons.Test", "test\Icons.Test\Icons.Test.csproj", "{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Icons.Test", "test\Icons.Test\Icons.Test.csproj", "{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommCore", "bitwarden_license\src\CommCore\CommCore.csproj", "{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{3973D21B-A692-4B60-9B70-3631C057423A} = {3973D21B-A692-4B60-9B70-3631C057423A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommCore.Test", "bitwarden_license\test\CmmCore.Test\CommCore.Test.csproj", "{0E99A21B-684B-4C59-9831-90F775CAB6F7}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test - Bitwarden License", "test - Bitwarden License", "{287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -143,6 +152,14 @@ Global
|
||||
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0E99A21B-684B-4C59-9831-90F775CAB6F7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -166,6 +183,8 @@ Global
|
||||
{BA852F18-852F-4154-973B-77D577B8CA04} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
|
||||
{4866AF64-6640-4C65-A662-A31E02FF9064} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
|
||||
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
|
||||
{0E99A21B-684B-4C59-9831-90F775CAB6F7} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
||||
|
11
bitwarden_license/src/CommCore/CommCore.csproj
Normal file
11
bitwarden_license/src/CommCore/CommCore.csproj
Normal file
@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Enums.Provider;
|
||||
@ -10,11 +9,12 @@ using Bit.Core.Models.Business.Provider;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
namespace Bit.CommCore.Services
|
||||
{
|
||||
public class ProviderService : IProviderService
|
||||
{
|
||||
@ -24,15 +24,18 @@ namespace Bit.Core.Services
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public ProviderService(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
|
||||
IUserRepository userRepository, IUserService userService, IMailService mailService,
|
||||
IDataProtectionProvider dataProtectionProvider, IEventService eventService, GlobalSettings globalSettings)
|
||||
IProviderOrganizationRepository providerOrganizationRepository, IUserRepository userRepository,
|
||||
IUserService userService, IMailService mailService, IDataProtectionProvider dataProtectionProvider,
|
||||
IEventService eventService, GlobalSettings globalSettings)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_providerOrganizationRepository = providerOrganizationRepository;
|
||||
_userRepository = userRepository;
|
||||
_userService = userService;
|
||||
_mailService = mailService;
|
||||
@ -56,11 +59,20 @@ namespace Bit.Core.Services
|
||||
};
|
||||
await _providerRepository.CreateAsync(provider);
|
||||
|
||||
var providerUser = new ProviderUser
|
||||
{
|
||||
ProviderId = provider.Id,
|
||||
UserId = owner.Id,
|
||||
Type = ProviderUserType.ProviderAdmin,
|
||||
Status = ProviderUserStatusType.Confirmed,
|
||||
};
|
||||
await _providerUserRepository.CreateAsync(providerUser);
|
||||
|
||||
var token = _dataProtector.Protect($"ProviderSetupInvite {provider.Id} {owner.Email} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}");
|
||||
await _mailService.SendProviderSetupInviteEmailAsync(provider, token, owner.Email);
|
||||
}
|
||||
|
||||
public async Task CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key)
|
||||
public async Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key)
|
||||
{
|
||||
var owner = await _userService.GetUserByIdAsync(ownerUserId);
|
||||
if (owner == null)
|
||||
@ -68,23 +80,29 @@ namespace Bit.Core.Services
|
||||
throw new BadRequestException("Invalid owner.");
|
||||
}
|
||||
|
||||
if (provider.Status != ProviderStatusType.Pending)
|
||||
{
|
||||
throw new BadRequestException("Provider is already setup.");
|
||||
}
|
||||
|
||||
if (!CoreHelpers.TokenIsValid("ProviderSetupInvite", _dataProtector, token, owner.Email, provider.Id, _globalSettings))
|
||||
{
|
||||
throw new BadRequestException("Invalid token.");
|
||||
}
|
||||
|
||||
var providerUser = await _providerUserRepository.GetByProviderUserAsync(provider.Id, ownerUserId);
|
||||
if (!(providerUser is {Type: ProviderUserType.ProviderAdmin}))
|
||||
{
|
||||
throw new BadRequestException("Invalid owner.");
|
||||
}
|
||||
|
||||
provider.Status = ProviderStatusType.Created;
|
||||
await _providerRepository.UpsertAsync(provider);
|
||||
|
||||
var providerUser = new ProviderUser
|
||||
{
|
||||
ProviderId = provider.Id,
|
||||
UserId = owner.Id,
|
||||
Key = key,
|
||||
Status = ProviderUserStatusType.Confirmed,
|
||||
Type = ProviderUserType.ProviderAdmin,
|
||||
};
|
||||
providerUser.Key = key;
|
||||
await _providerUserRepository.ReplaceAsync(providerUser);
|
||||
|
||||
await _providerUserRepository.CreateAsync(providerUser);
|
||||
return provider;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Provider provider, bool updateBilling = false)
|
||||
@ -129,14 +147,6 @@ namespace Bit.Core.Services
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
if (invite.Permissions != null)
|
||||
{
|
||||
providerUser.Permissions = JsonSerializer.Serialize(invite.Permissions, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
}
|
||||
|
||||
await _providerUserRepository.CreateAsync(providerUser);
|
||||
|
||||
await SendInviteAsync(providerUser, provider);
|
||||
@ -322,8 +332,17 @@ namespace Bit.Core.Services
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Implement this
|
||||
public Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key) => throw new NotImplementedException();
|
||||
public async Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key)
|
||||
{
|
||||
var providerOrganization = new ProviderOrganization
|
||||
{
|
||||
ProviderId = providerId,
|
||||
OrganizationId = organizationId,
|
||||
Key = key,
|
||||
};
|
||||
|
||||
await _providerOrganizationRepository.CreateAsync(providerOrganization);
|
||||
}
|
||||
|
||||
// TODO: Implement this
|
||||
public Task RemoveOrganization(Guid providerOrganizationId, Guid removingUserId) => throw new NotImplementedException();
|
@ -0,0 +1,14 @@
|
||||
using Bit.CommCore.Services;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.CommCore.Utilities
|
||||
{
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static void AddCommCoreServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IProviderService, ProviderService>();
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ using AutoFixture;
|
||||
using AutoFixture.Xunit2;
|
||||
using Bit.Core.Enums.Provider;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.ProviderUserFixtures
|
||||
namespace Bit.CommCore.Test.AutoFixture.ProviderUserFixtures
|
||||
{
|
||||
internal class ProviderUser : ICustomization
|
||||
{
|
27
bitwarden_license/test/CmmCore.Test/CommCore.Test.csproj
Normal file
27
bitwarden_license/test/CmmCore.Test/CommCore.Test.csproj
Normal file
@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="1.3.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\CommCore\CommCore.csproj" />
|
||||
<ProjectReference Include="..\..\..\test\Core.Test\Core.Test.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -2,6 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.CommCore.Test.AutoFixture.ProviderUserFixtures;
|
||||
using Bit.CommCore.Services;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Exceptions;
|
||||
@ -12,14 +14,13 @@ using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Test.AutoFixture;
|
||||
using Bit.Core.Test.AutoFixture.Attributes;
|
||||
using Bit.Core.Test.AutoFixture.ProviderUserFixtures;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using ProviderUser = Bit.Core.Models.Table.Provider.ProviderUser;
|
||||
|
||||
namespace Bit.Core.Test.Services
|
||||
namespace Bit.CommCore.Test.Services
|
||||
{
|
||||
public class ProviderServiceTests
|
||||
{
|
||||
@ -64,12 +65,18 @@ namespace Bit.Core.Test.Services
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task CompleteSetupAsync_Success(User user, Provider provider,
|
||||
public async Task CompleteSetupAsync_Success(User user, Provider provider, string key,
|
||||
[ProviderUser(ProviderUserStatusType.Confirmed, ProviderUserType.ProviderAdmin)]ProviderUser providerUser,
|
||||
SutProvider<ProviderService> sutProvider)
|
||||
{
|
||||
providerUser.ProviderId = provider.Id;
|
||||
providerUser.UserId = user.Id;
|
||||
var userService = sutProvider.GetDependency<IUserService>();
|
||||
userService.GetUserByIdAsync(user.Id).Returns(user);
|
||||
|
||||
var providerUserRepository = sutProvider.GetDependency<IProviderUserRepository>();
|
||||
providerUserRepository.GetByProviderUserAsync(provider.Id, user.Id).Returns(providerUser);
|
||||
|
||||
var dataProtectionProvider = DataProtectionProvider.Create("ApplicationName");
|
||||
var protector = dataProtectionProvider.CreateProtector("ProviderServiceDataProtector");
|
||||
sutProvider.GetDependency<IDataProtectionProvider>().CreateProtector("ProviderServiceDataProtector")
|
||||
@ -78,11 +85,11 @@ namespace Bit.Core.Test.Services
|
||||
|
||||
var token = protector.Protect($"ProviderSetupInvite {provider.Id} {user.Email} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}");
|
||||
|
||||
await sutProvider.Sut.CompleteSetupAsync(provider, user.Id, token, default);
|
||||
await sutProvider.Sut.CompleteSetupAsync(provider, user.Id, token, key);
|
||||
|
||||
await sutProvider.GetDependency<IProviderRepository>().Received().UpsertAsync(provider);
|
||||
await sutProvider.GetDependency<IProviderUserRepository>().Received()
|
||||
.CreateAsync(Arg.Is<ProviderUser>(pu => pu.UserId == user.Id && pu.ProviderId == provider.Id));
|
||||
.ReplaceAsync(Arg.Is<ProviderUser>(pu => pu.UserId == user.Id && pu.ProviderId == provider.Id && pu.Key == key));
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
@ -9,6 +9,14 @@
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Choose>
|
||||
<When Condition="!$(DefineConstants.Contains('OSS'))">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\bitwarden_license\src\CommCore\CommCore.csproj" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.3" />
|
||||
</ItemGroup>
|
||||
|
@ -85,7 +85,7 @@ namespace Bit.Admin.Controllers
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var users = await _providerUserRepository.GetManyByProviderAsync(id);
|
||||
var users = await _providerUserRepository.GetManyDetailsByProviderAsync(id);
|
||||
return View(new ProviderViewModel(provider, users));
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ namespace Bit.Admin.Controllers
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var users = await _providerUserRepository.GetManyByProviderAsync(id);
|
||||
var users = await _providerUserRepository.GetManyDetailsByProviderAsync(id);
|
||||
return View(new ProviderEditModel(provider, users));
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
{
|
||||
public class ProviderEditModel : ProviderViewModel
|
||||
{
|
||||
public ProviderEditModel(Provider provider, IEnumerable<ProviderUser> providerUsers)
|
||||
public ProviderEditModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers)
|
||||
: base(provider, providerUsers)
|
||||
{
|
||||
Name = provider.Name;
|
||||
|
@ -1,13 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
{
|
||||
public class ProviderViewModel
|
||||
{
|
||||
public ProviderViewModel(Provider provider, IEnumerable<ProviderUser> providerUsers)
|
||||
public ProviderViewModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers)
|
||||
{
|
||||
Provider = provider;
|
||||
UserCount = providerUsers.Count();
|
||||
|
@ -13,6 +13,10 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Stripe;
|
||||
|
||||
#if !OSS
|
||||
using Bit.CommCore.Utilities;
|
||||
#endif
|
||||
|
||||
namespace Bit.Admin
|
||||
{
|
||||
public class Startup
|
||||
@ -66,6 +70,12 @@ namespace Bit.Admin
|
||||
services.AddBaseServices();
|
||||
services.AddDefaultServices(globalSettings);
|
||||
|
||||
#if OSS
|
||||
services.AddOosServices();
|
||||
#else
|
||||
services.AddCommCoreServices();
|
||||
#endif
|
||||
|
||||
// Mvc
|
||||
services.AddMvc(config =>
|
||||
{
|
||||
|
@ -19,6 +19,14 @@
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Choose>
|
||||
<When Condition="!$(DefineConstants.Contains('OSS'))">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\bitwarden_license\src\CommCore\CommCore.csproj" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.6" />
|
||||
<PackageReference Include="NewRelic.Agent" Version="8.30.0" />
|
||||
|
@ -18,6 +18,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums.Provider;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
{
|
||||
@ -30,6 +31,7 @@ namespace Bit.Api.Controllers
|
||||
private readonly IFolderRepository _folderRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
@ -40,6 +42,7 @@ namespace Bit.Api.Controllers
|
||||
IFolderRepository folderRepository,
|
||||
IOrganizationService organizationService,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IPaymentService paymentService,
|
||||
ISsoUserRepository ssoUserRepository,
|
||||
IUserRepository userRepository,
|
||||
@ -50,6 +53,7 @@ namespace Bit.Api.Controllers
|
||||
_globalSettings = globalSettings;
|
||||
_organizationService = organizationService;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_paymentService = paymentService;
|
||||
_userRepository = userRepository;
|
||||
_userService = userService;
|
||||
@ -358,7 +362,9 @@ namespace Bit.Api.Controllers
|
||||
|
||||
var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(user.Id,
|
||||
OrganizationUserStatusType.Confirmed);
|
||||
var response = new ProfileResponseModel(user, organizationUserDetails,
|
||||
var providerUserDetails = await _providerUserRepository.GetManyDetailsByUserAsync(user.Id,
|
||||
ProviderUserStatusType.Confirmed);
|
||||
var response = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
|
||||
await _userService.TwoFactorIsEnabledAsync(user));
|
||||
return response;
|
||||
}
|
||||
@ -384,7 +390,7 @@ namespace Bit.Api.Controllers
|
||||
}
|
||||
|
||||
await _userService.SaveUserAsync(model.ToUser(user));
|
||||
var response = new ProfileResponseModel(user, null, await _userService.TwoFactorIsEnabledAsync(user));
|
||||
var response = new ProfileResponseModel(user, null, null, await _userService.TwoFactorIsEnabledAsync(user));
|
||||
return response;
|
||||
}
|
||||
|
||||
@ -535,7 +541,7 @@ namespace Bit.Api.Controllers
|
||||
BillingAddressCountry = model.Country,
|
||||
BillingAddressPostalCode = model.PostalCode,
|
||||
});
|
||||
var profile = new ProfileResponseModel(user, null, await _userService.TwoFactorIsEnabledAsync(user));
|
||||
var profile = new ProfileResponseModel(user, null, null, await _userService.TwoFactorIsEnabledAsync(user));
|
||||
return new PaymentResponseModel
|
||||
{
|
||||
UserProfile = profile,
|
||||
|
62
src/Api/Controllers/ProviderOrganizationsController.cs
Normal file
62
src/Api/Controllers/ProviderOrganizationsController.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
{
|
||||
[Route("providers/{providerId:guid}/organizations")]
|
||||
[Authorize("Application")]
|
||||
public class ProviderOrganizationsController : Controller
|
||||
{
|
||||
|
||||
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public ProviderOrganizationsController(
|
||||
IProviderOrganizationRepository providerOrganizationRepository,
|
||||
IProviderService providerService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
_providerOrganizationRepository = providerOrganizationRepository;
|
||||
_providerService = providerService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>> Get(Guid providerId)
|
||||
{
|
||||
if (!_currentContext.AccessProviderOrganizations(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(providerId);
|
||||
var responses = providerOrganizations.Select(o => new ProviderOrganizationOrganizationDetailsResponseModel(o));
|
||||
return new ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPost("add")]
|
||||
public async Task Add(Guid providerId, [FromBody]ProviderOrganizationAddRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderOrganizations(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
|
||||
await _providerService.AddOrganization(providerId, model.OrganizationId, userId, model.Key);
|
||||
}
|
||||
}
|
||||
}
|
205
src/Api/Controllers/ProviderUsersController.cs
Normal file
205
src/Api/Controllers/ProviderUsersController.cs
Normal file
@ -0,0 +1,205 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Bit.Core.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Models.Business.Provider;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
{
|
||||
[Route("providers/{providerId:guid}/users")]
|
||||
[Authorize("Application")]
|
||||
public class ProviderUsersController : Controller
|
||||
{
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public ProviderUsersController(
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IProviderService providerService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_providerService = providerService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}")]
|
||||
public async Task<ProviderUserResponseModel> Get(Guid providerId, Guid id)
|
||||
{
|
||||
var providerUser = await _providerUserRepository.GetByIdAsync(id);
|
||||
if (providerUser == null || !_currentContext.ManageProviderUsers(providerUser.ProviderId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new ProviderUserResponseModel(providerUser);
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<ProviderUserUserDetailsResponseModel>> Get(Guid providerId)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var providerUsers = await _providerUserRepository.GetManyDetailsByProviderAsync(providerId);
|
||||
var responses = providerUsers.Select(o => new ProviderUserUserDetailsResponseModel(o));
|
||||
return new ListResponseModel<ProviderUserUserDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPost("invite")]
|
||||
public async Task Invite(Guid providerId, [FromBody]ProviderUserInviteRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.InviteUserAsync(providerId, userId.Value, new ProviderUserInvite(model));
|
||||
}
|
||||
|
||||
[HttpPost("reinvite")]
|
||||
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkReinvite(Guid providerId, [FromBody]ProviderUserBulkRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _providerService.ResendInvitesAsync(providerId, userId.Value, model.Ids);
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(
|
||||
result.Select(t => new ProviderUserBulkResponseModel(t.Item1.Id, t.Item2)));
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/reinvite")]
|
||||
public async Task Reinvite(Guid providerId, Guid id)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.ResendInvitesAsync(providerId, userId.Value, new [] { id });
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/accept")]
|
||||
public async Task Accept(Guid providerId, Guid id, [FromBody]ProviderUserAcceptRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
await _providerService.AcceptUserAsync(id, user, model.Token);
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/confirm")]
|
||||
public async Task Confirm(Guid providerId, Guid id, [FromBody]ProviderUserConfirmRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.ConfirmUsersAsync(providerId, new Dictionary<Guid, string> { [id] = model.Key }, userId.Value);
|
||||
}
|
||||
|
||||
[HttpPost("confirm")]
|
||||
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkConfirm(Guid providerId,
|
||||
[FromBody]ProviderUserBulkConfirmRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var results = await _providerService.ConfirmUsersAsync(providerId, model.ToDictionary(), userId.Value);
|
||||
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(results.Select(r =>
|
||||
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
}
|
||||
|
||||
[HttpPost("public-keys")]
|
||||
public async Task<ListResponseModel<ProviderUserPublicKeyResponseModel>> UserPublicKeys(Guid providerId, [FromBody]ProviderUserBulkRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var result = await _providerUserRepository.GetManyPublicKeysByProviderUserAsync(providerId, model.Ids);
|
||||
var responses = result.Select(r => new ProviderUserPublicKeyResponseModel(r.Id, r.PublicKey)).ToList();
|
||||
return new ListResponseModel<ProviderUserPublicKeyResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[HttpPost("{id:guid}")]
|
||||
public async Task Put(Guid providerId, Guid id, [FromBody]ProviderUserUpdateRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var providerUser = await _providerUserRepository.GetByIdAsync(id);
|
||||
if (providerUser == null || providerUser.ProviderId != providerId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.SaveUserAsync(model.ToProviderUser(providerUser), userId.Value);
|
||||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[HttpPost("{id:guid}/delete")]
|
||||
public async Task Delete(Guid providerId, Guid id)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.DeleteUsersAsync(providerId, new [] { id }, userId.Value);
|
||||
}
|
||||
|
||||
[HttpDelete("")]
|
||||
[HttpPost("delete")]
|
||||
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkDelete(Guid providerId, [FromBody]ProviderUserBulkRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ManageProviderUsers(providerId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _providerService.DeleteUsersAsync(providerId, model.Ids, userId.Value);
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(result.Select(r =>
|
||||
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
|
||||
}
|
||||
}
|
||||
}
|
70
src/Api/Controllers/ProvidersController.cs
Normal file
70
src/Api/Controllers/ProvidersController.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
{
|
||||
[Route("providers")]
|
||||
[Authorize("Application")]
|
||||
public class ProvidersController : Controller
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public ProvidersController(IUserService userService, IProviderRepository providerRepository,
|
||||
IProviderService providerService, ICurrentContext currentContext)
|
||||
{
|
||||
_userService = userService;
|
||||
_providerRepository = providerRepository;
|
||||
_providerService = providerService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
[HttpGet("{id:guid}")]
|
||||
public async Task<ProviderResponseModel> Get(Guid id)
|
||||
{
|
||||
if (!_currentContext.ProviderUser(id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new ProviderResponseModel(provider);
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/setup")]
|
||||
public async Task<ProviderResponseModel> Setup(Guid id, [FromBody]ProviderSetupRequestModel model)
|
||||
{
|
||||
if (!_currentContext.ProviderProviderAdmin(id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
|
||||
var response =
|
||||
await _providerService.CompleteSetupAsync(model.ToProvider(provider), userId, model.Token, model.Key);
|
||||
|
||||
return new ProviderResponseModel(response);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ using Bit.Core.Exceptions;
|
||||
using System.Linq;
|
||||
using Bit.Core.Models.Table;
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
@ -25,6 +26,7 @@ namespace Bit.Api.Controllers
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly ICollectionCipherRepository _collectionCipherRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
@ -36,6 +38,7 @@ namespace Bit.Api.Controllers
|
||||
ICollectionRepository collectionRepository,
|
||||
ICollectionCipherRepository collectionCipherRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IPolicyRepository policyRepository,
|
||||
ISendRepository sendRepository,
|
||||
GlobalSettings globalSettings)
|
||||
@ -46,6 +49,7 @@ namespace Bit.Api.Controllers
|
||||
_collectionRepository = collectionRepository;
|
||||
_collectionCipherRepository = collectionCipherRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_policyRepository = policyRepository;
|
||||
_sendRepository = sendRepository;
|
||||
_globalSettings = globalSettings;
|
||||
@ -62,6 +66,8 @@ namespace Bit.Api.Controllers
|
||||
|
||||
var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(user.Id,
|
||||
OrganizationUserStatusType.Confirmed);
|
||||
var providerUserDetails = await _providerUserRepository.GetManyDetailsByUserAsync(user.Id,
|
||||
ProviderUserStatusType.Confirmed);
|
||||
var hasEnabledOrgs = organizationUserDetails.Any(o => o.Enabled);
|
||||
var folders = await _folderRepository.GetManyByUserIdAsync(user.Id);
|
||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(user.Id, hasEnabledOrgs);
|
||||
@ -80,7 +86,8 @@ namespace Bit.Api.Controllers
|
||||
|
||||
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
||||
var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, organizationUserDetails,
|
||||
folders, collections, ciphers, collectionCiphersGroupDict, excludeDomains, policies, sends);
|
||||
providerUserDetails, folders, collections, ciphers, collectionCiphersGroupDict, excludeDomains,
|
||||
policies, sends);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,10 @@ using Microsoft.OpenApi.Models;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
#if !OSS
|
||||
using Bit.CommCore.Utilities;
|
||||
#endif
|
||||
|
||||
namespace Bit.Api
|
||||
{
|
||||
public class Startup
|
||||
@ -119,6 +123,12 @@ namespace Bit.Api
|
||||
services.AddDefaultServices(globalSettings);
|
||||
services.AddCoreLocalizationServices();
|
||||
|
||||
#if OSS
|
||||
services.AddOosServices();
|
||||
#else
|
||||
services.AddCommCoreServices();
|
||||
#endif
|
||||
|
||||
// MVC
|
||||
services.AddMvc(config =>
|
||||
{
|
||||
|
26
src/Core/Context/CurrentContentProvider.cs
Normal file
26
src/Core/Context/CurrentContentProvider.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Context
|
||||
{
|
||||
public class CurrentContentProvider
|
||||
{
|
||||
public CurrentContentProvider() { }
|
||||
|
||||
public CurrentContentProvider(ProviderUser providerUser)
|
||||
{
|
||||
Id = providerUser.ProviderId;
|
||||
Type = providerUser.Type;
|
||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(providerUser.Permissions);
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public Permissions Permissions { get; set; }
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Bit.Core.Repositories;
|
||||
using System.Threading.Tasks;
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
@ -25,6 +26,7 @@ namespace Bit.Core.Context
|
||||
public virtual DeviceType? DeviceType { get; set; }
|
||||
public virtual string IpAddress { get; set; }
|
||||
public virtual List<CurrentContentOrganization> Organizations { get; set; }
|
||||
public virtual List<CurrentContentProvider> Providers { get; set; }
|
||||
public virtual Guid? InstallationId { get; set; }
|
||||
public virtual Guid? OrganizationId { get; set; }
|
||||
public virtual bool CloudflareWorkerProxied { get; set; }
|
||||
@ -127,10 +129,19 @@ namespace Bit.Core.Context
|
||||
|
||||
DeviceIdentifier = GetClaimValue(claimsDict, "device");
|
||||
|
||||
Organizations = new List<CurrentContentOrganization>();
|
||||
Organizations = GetOrganizations(claimsDict, orgApi);
|
||||
|
||||
Providers = GetProviders(claimsDict);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
private List<CurrentContentOrganization> GetOrganizations(Dictionary<string, IEnumerable<Claim>> claimsDict, bool orgApi)
|
||||
{
|
||||
var organizations = new List<CurrentContentOrganization>();
|
||||
if (claimsDict.ContainsKey("orgowner"))
|
||||
{
|
||||
Organizations.AddRange(claimsDict["orgowner"].Select(c =>
|
||||
organizations.AddRange(claimsDict["orgowner"].Select(c =>
|
||||
new CurrentContentOrganization
|
||||
{
|
||||
Id = new Guid(c.Value),
|
||||
@ -139,7 +150,7 @@ namespace Bit.Core.Context
|
||||
}
|
||||
else if (orgApi && OrganizationId.HasValue)
|
||||
{
|
||||
Organizations.Add(new CurrentContentOrganization
|
||||
organizations.Add(new CurrentContentOrganization
|
||||
{
|
||||
Id = OrganizationId.Value,
|
||||
Type = OrganizationUserType.Owner
|
||||
@ -148,7 +159,7 @@ namespace Bit.Core.Context
|
||||
|
||||
if (claimsDict.ContainsKey("orgadmin"))
|
||||
{
|
||||
Organizations.AddRange(claimsDict["orgadmin"].Select(c =>
|
||||
organizations.AddRange(claimsDict["orgadmin"].Select(c =>
|
||||
new CurrentContentOrganization
|
||||
{
|
||||
Id = new Guid(c.Value),
|
||||
@ -158,7 +169,7 @@ namespace Bit.Core.Context
|
||||
|
||||
if (claimsDict.ContainsKey("orguser"))
|
||||
{
|
||||
Organizations.AddRange(claimsDict["orguser"].Select(c =>
|
||||
organizations.AddRange(claimsDict["orguser"].Select(c =>
|
||||
new CurrentContentOrganization
|
||||
{
|
||||
Id = new Guid(c.Value),
|
||||
@ -168,7 +179,7 @@ namespace Bit.Core.Context
|
||||
|
||||
if (claimsDict.ContainsKey("orgmanager"))
|
||||
{
|
||||
Organizations.AddRange(claimsDict["orgmanager"].Select(c =>
|
||||
organizations.AddRange(claimsDict["orgmanager"].Select(c =>
|
||||
new CurrentContentOrganization
|
||||
{
|
||||
Id = new Guid(c.Value),
|
||||
@ -178,7 +189,7 @@ namespace Bit.Core.Context
|
||||
|
||||
if (claimsDict.ContainsKey("orgcustom"))
|
||||
{
|
||||
Organizations.AddRange(claimsDict["orgcustom"].Select(c =>
|
||||
organizations.AddRange(claimsDict["orgcustom"].Select(c =>
|
||||
new CurrentContentOrganization
|
||||
{
|
||||
Id = new Guid(c.Value),
|
||||
@ -187,7 +198,33 @@ namespace Bit.Core.Context
|
||||
}));
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
return organizations;
|
||||
}
|
||||
|
||||
private List<CurrentContentProvider> GetProviders(Dictionary<string, IEnumerable<Claim>> claimsDict)
|
||||
{
|
||||
var providers = new List<CurrentContentProvider>();
|
||||
if (claimsDict.ContainsKey("providerprovideradmin"))
|
||||
{
|
||||
providers.AddRange(claimsDict["providerprovideradmin"].Select(c =>
|
||||
new CurrentContentProvider
|
||||
{
|
||||
Id = new Guid(c.Value),
|
||||
Type = ProviderUserType.ProviderAdmin
|
||||
}));
|
||||
}
|
||||
|
||||
if (claimsDict.ContainsKey("providerserviceuser"))
|
||||
{
|
||||
providers.AddRange(claimsDict["providerserviceuser"].Select(c =>
|
||||
new CurrentContentProvider
|
||||
{
|
||||
Id = new Guid(c.Value),
|
||||
Type = ProviderUserType.ServiceUser
|
||||
}));
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
public bool OrganizationUser(Guid orgId)
|
||||
@ -284,6 +321,31 @@ namespace Bit.Core.Context
|
||||
&& (o.Permissions?.ManageResetPassword ?? false)) ?? false);
|
||||
}
|
||||
|
||||
public bool ProviderProviderAdmin(Guid providerId)
|
||||
{
|
||||
return Providers?.Any(o => o.Id == providerId && o.Type == ProviderUserType.ProviderAdmin) ?? false;
|
||||
}
|
||||
|
||||
public bool ManageProviderUsers(Guid providerId)
|
||||
{
|
||||
return ProviderProviderAdmin(providerId);
|
||||
}
|
||||
|
||||
public bool AccessProviderOrganizations(Guid providerId)
|
||||
{
|
||||
return ProviderUser(providerId);
|
||||
}
|
||||
|
||||
public bool ManageProviderOrganizations(Guid providerId)
|
||||
{
|
||||
return ProviderProviderAdmin(providerId);
|
||||
}
|
||||
|
||||
public bool ProviderUser(Guid providerId)
|
||||
{
|
||||
return Providers?.Any(o => o.Id == providerId) ?? false;
|
||||
}
|
||||
|
||||
public async Task<ICollection<CurrentContentOrganization>> OrganizationMembershipAsync(
|
||||
IOrganizationUserRepository organizationUserRepository, Guid userId)
|
||||
{
|
||||
@ -296,6 +358,18 @@ namespace Bit.Core.Context
|
||||
return Organizations;
|
||||
}
|
||||
|
||||
public async Task<ICollection<CurrentContentProvider>> ProviderMembershipAsync(
|
||||
IProviderUserRepository providerUserRepository, Guid userId)
|
||||
{
|
||||
if (Providers == null)
|
||||
{
|
||||
var userProviders = await providerUserRepository.GetManyByUserAsync(userId);
|
||||
Providers = userProviders.Where(ou => ou.Status == ProviderUserStatusType.Confirmed)
|
||||
.Select(ou => new CurrentContentProvider(ou)).ToList();
|
||||
}
|
||||
return Providers;
|
||||
}
|
||||
|
||||
private string GetClaimValue(Dictionary<string, IEnumerable<Claim>> claims, string type)
|
||||
{
|
||||
if (!claims.ContainsKey(type))
|
||||
|
@ -47,8 +47,16 @@ namespace Bit.Core.Context
|
||||
bool ManageSso(Guid orgId);
|
||||
bool ManageUsers(Guid orgId);
|
||||
bool ManageResetPassword(Guid orgId);
|
||||
bool ProviderProviderAdmin(Guid providerId);
|
||||
bool ProviderUser(Guid providerId);
|
||||
bool ManageProviderUsers(Guid providerId);
|
||||
bool AccessProviderOrganizations(Guid providerId);
|
||||
bool ManageProviderOrganizations(Guid providerId);
|
||||
|
||||
Task<ICollection<CurrentContentOrganization>> OrganizationMembershipAsync(
|
||||
IOrganizationUserRepository organizationUserRepository, Guid userId);
|
||||
|
||||
Task<ICollection<CurrentContentProvider>> ProviderMembershipAsync(
|
||||
IProviderUserRepository providerUserRepository, Guid userId);
|
||||
}
|
||||
}
|
||||
|
@ -22,11 +22,14 @@ namespace Bit.Core.IdentityServer
|
||||
"orgmanager",
|
||||
"orguser",
|
||||
"orgcustom",
|
||||
"providerprovideradmin",
|
||||
"providerserviceuser",
|
||||
}),
|
||||
new ApiResource("internal", new string[] { JwtClaimTypes.Subject }),
|
||||
new ApiResource("api.push", new string[] { JwtClaimTypes.Subject }),
|
||||
new ApiResource("api.licensing", new string[] { JwtClaimTypes.Subject }),
|
||||
new ApiResource("api.organization", new string[] { JwtClaimTypes.Subject })
|
||||
new ApiResource("api.organization", new string[] { JwtClaimTypes.Subject }),
|
||||
new ApiResource("api.provider", new string[] { JwtClaimTypes.Subject }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace Bit.Core.IdentityServer
|
||||
private readonly ILicensingService _licensingService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
|
||||
public ClientStore(
|
||||
IInstallationRepository installationRepository,
|
||||
@ -34,7 +35,8 @@ namespace Bit.Core.IdentityServer
|
||||
StaticClientStore staticClientStore,
|
||||
ILicensingService licensingService,
|
||||
ICurrentContext currentContext,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProviderUserRepository providerUserRepository)
|
||||
{
|
||||
_installationRepository = installationRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
@ -44,6 +46,7 @@ namespace Bit.Core.IdentityServer
|
||||
_licensingService = licensingService;
|
||||
_currentContext = currentContext;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
}
|
||||
|
||||
public async Task<Client> FindClientByIdAsync(string clientId)
|
||||
@ -138,8 +141,9 @@ namespace Bit.Core.IdentityServer
|
||||
new ClientClaim(JwtClaimTypes.AuthenticationMethod, "Application", "external")
|
||||
};
|
||||
var orgs = await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id);
|
||||
var providers = await _currentContext.ProviderMembershipAsync(_providerUserRepository, user.Id);
|
||||
var isPremium = await _licensingService.ValidateUserPremiumAsync(user);
|
||||
foreach (var claim in CoreHelpers.BuildIdentityClaims(user, orgs, isPremium))
|
||||
foreach (var claim in CoreHelpers.BuildIdentityClaims(user, orgs, providers, isPremium))
|
||||
{
|
||||
var upperValue = claim.Value.ToUpperInvariant();
|
||||
var isBool = upperValue == "TRUE" || upperValue == "FALSE";
|
||||
|
@ -18,17 +18,20 @@ namespace Bit.Core.IdentityServer
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly ILicensingService _licensingService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public ProfileService(
|
||||
IUserService userService,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
ILicensingService licensingService,
|
||||
ICurrentContext currentContext)
|
||||
{
|
||||
_userService = userService;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_licensingService = licensingService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
@ -43,7 +46,8 @@ namespace Bit.Core.IdentityServer
|
||||
{
|
||||
var isPremium = await _licensingService.ValidateUserPremiumAsync(user);
|
||||
var orgs = await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id);
|
||||
foreach (var claim in CoreHelpers.BuildIdentityClaims(user, orgs, isPremium))
|
||||
var providers = await _currentContext.ProviderMembershipAsync(_providerUserRepository, user.Id);
|
||||
foreach (var claim in CoreHelpers.BuildIdentityClaims(user, orgs, providers, isPremium))
|
||||
{
|
||||
var upperValue = claim.Value.ToUpperInvariant();
|
||||
var isBool = upperValue == "TRUE" || upperValue == "FALSE";
|
||||
|
@ -5,47 +5,20 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class OrganizationUserInviteRequestModel : IValidatableObject
|
||||
public class OrganizationUserInviteRequestModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddressList]
|
||||
public IEnumerable<string> Emails { get; set; }
|
||||
[Required]
|
||||
public Enums.OrganizationUserType? Type { get; set; }
|
||||
public bool AccessAll { get; set; }
|
||||
public Permissions Permissions { get; set; }
|
||||
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (!Emails.Any())
|
||||
{
|
||||
yield return new ValidationResult("An email is required.");
|
||||
}
|
||||
|
||||
if (Emails.Count() > 20)
|
||||
{
|
||||
yield return new ValidationResult("You can only invite up to 20 users at a time.");
|
||||
}
|
||||
|
||||
var attr = new EmailAddressAttribute();
|
||||
for (var i = 0; i < Emails.Count(); i++)
|
||||
{
|
||||
var email = Emails.ElementAt(i);
|
||||
if (!attr.IsValid(email) || email.Contains(" ") || email.Contains("<"))
|
||||
{
|
||||
yield return new ValidationResult($"Email #{i + 1} is not valid.",
|
||||
new string[] { nameof(Emails) });
|
||||
}
|
||||
else if (email.Length > 256)
|
||||
{
|
||||
yield return new ValidationResult($"Email #{i + 1} is longer than 256 characters.",
|
||||
new string[] { nameof(Emails) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OrganizationUserAcceptRequestModel
|
||||
|
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class ProviderOrganizationAddRequestModel
|
||||
{
|
||||
[Required]
|
||||
public Guid OrganizationId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Key { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class ProviderSetupRequestModel
|
||||
{
|
||||
[Required]
|
||||
[StringLength(50)]
|
||||
public string Name { get; set; }
|
||||
[StringLength(50)]
|
||||
public string BusinessName { get; set; }
|
||||
[Required]
|
||||
[StringLength(256)]
|
||||
[EmailAddress]
|
||||
public string BillingEmail { get; set; }
|
||||
[Required]
|
||||
public string Token { get; set; }
|
||||
[Required]
|
||||
public string Key { get; set; }
|
||||
|
||||
public virtual Provider ToProvider(Provider provider)
|
||||
{
|
||||
provider.Name = Name;
|
||||
provider.BusinessName = BusinessName;
|
||||
provider.BillingEmail = BillingEmail;
|
||||
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class ProviderUserInviteRequestModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddressList]
|
||||
public IEnumerable<string> Emails { get; set; }
|
||||
[Required]
|
||||
public ProviderUserType? Type { get; set; }
|
||||
}
|
||||
|
||||
public class ProviderUserAcceptRequestModel
|
||||
{
|
||||
[Required]
|
||||
public string Token { get; set; }
|
||||
}
|
||||
|
||||
public class ProviderUserConfirmRequestModel
|
||||
{
|
||||
[Required]
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
public class ProviderUserBulkConfirmRequestModelEntry
|
||||
{
|
||||
[Required]
|
||||
public Guid Id { get; set; }
|
||||
[Required]
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
public class ProviderUserBulkConfirmRequestModel
|
||||
{
|
||||
[Required]
|
||||
public IEnumerable<ProviderUserBulkConfirmRequestModelEntry> Keys { get; set; }
|
||||
|
||||
public Dictionary<Guid, string> ToDictionary()
|
||||
{
|
||||
return Keys.ToDictionary(e => e.Id, e => e.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public class ProviderUserUpdateRequestModel
|
||||
{
|
||||
[Required]
|
||||
public ProviderUserType? Type { get; set; }
|
||||
|
||||
public ProviderUser ToProviderUser(ProviderUser existingUser)
|
||||
{
|
||||
existingUser.Type = Type.Value;
|
||||
return existingUser;
|
||||
}
|
||||
}
|
||||
|
||||
public class ProviderUserBulkRequestModel
|
||||
{
|
||||
[Required]
|
||||
public IEnumerable<Guid> Ids { get; set; }
|
||||
}
|
||||
}
|
@ -34,6 +34,8 @@ namespace Bit.Core.Models.Api
|
||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organization.Permissions);
|
||||
ResetPasswordEnrolled = organization.ResetPasswordKey != null;
|
||||
UserId = organization.UserId?.ToString();
|
||||
ProviderId = organization.ProviderId?.ToString();
|
||||
ProviderName = organization.ProviderName;
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
@ -63,5 +65,7 @@ namespace Bit.Core.Models.Api
|
||||
public bool ResetPasswordEnrolled { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public bool HasPublicAndPrivateKeys { get; set; }
|
||||
public string ProviderId { get; set; }
|
||||
public string ProviderName { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ namespace Bit.Core.Models.Api
|
||||
public class ProfileResponseModel : ResponseModel
|
||||
{
|
||||
public ProfileResponseModel(User user,
|
||||
IEnumerable<OrganizationUserOrganizationDetails> organizationsUserDetails, bool twoFactorEnabled)
|
||||
: base("profile")
|
||||
IEnumerable<OrganizationUserOrganizationDetails> organizationsUserDetails,
|
||||
IEnumerable<ProviderUserProviderDetails> providerUserDetails, bool twoFactorEnabled) : base("profile")
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
@ -30,6 +30,7 @@ namespace Bit.Core.Models.Api
|
||||
PrivateKey = user.PrivateKey;
|
||||
SecurityStamp = user.SecurityStamp;
|
||||
Organizations = organizationsUserDetails?.Select(o => new ProfileOrganizationResponseModel(o));
|
||||
Providers = providerUserDetails?.Select(p => new ProfileProviderResponseModel(p));
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
@ -44,5 +45,6 @@ namespace Bit.Core.Models.Api
|
||||
public string PrivateKey { get; set; }
|
||||
public string SecurityStamp { get; set; }
|
||||
public IEnumerable<ProfileOrganizationResponseModel> Organizations { get; set; }
|
||||
public IEnumerable<ProfileProviderResponseModel> Providers { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class ProfileProviderResponseModel : ResponseModel
|
||||
{
|
||||
public ProfileProviderResponseModel(ProviderUserProviderDetails provider)
|
||||
: base("profileProvider")
|
||||
{
|
||||
Id = provider.ProviderId.ToString();
|
||||
Name = provider.Name;
|
||||
Key = provider.Key;
|
||||
Status = provider.Status;
|
||||
Type = provider.Type;
|
||||
Enabled = provider.Enabled;
|
||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(provider.Permissions);
|
||||
UserId = provider.UserId?.ToString();
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Key { get; set; }
|
||||
public ProviderUserStatusType Status { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
public Permissions Permissions { get; set; }
|
||||
public string UserId { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class ProviderOrganizationOrganizationDetailsResponseModel : ResponseModel
|
||||
{
|
||||
public ProviderOrganizationOrganizationDetailsResponseModel(ProviderOrganizationOrganizationDetails providerOrganization,
|
||||
string obj = "providerOrganization") : base(obj)
|
||||
{
|
||||
if (providerOrganization == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(providerOrganization));
|
||||
}
|
||||
|
||||
Id = providerOrganization.Id;
|
||||
ProviderId = providerOrganization.ProviderId;
|
||||
OrganizationId = providerOrganization.OrganizationId;
|
||||
OrganizationName = providerOrganization.OrganizationName;
|
||||
Key = providerOrganization.Key;
|
||||
Settings = providerOrganization.Settings;
|
||||
CreationDate = providerOrganization.CreationDate;
|
||||
RevisionDate = providerOrganization.RevisionDate;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public Guid ProviderId { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string OrganizationName { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Settings { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime RevisionDate { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class ProviderResponseModel : ResponseModel
|
||||
{
|
||||
public ProviderResponseModel(Provider provider, string obj = "provider") : base(obj)
|
||||
{
|
||||
if (provider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(provider));
|
||||
}
|
||||
|
||||
Id = provider.Id;
|
||||
Name = provider.Name;
|
||||
BusinessName = provider.BusinessName;
|
||||
BusinessAddress1 = provider.BusinessAddress1;
|
||||
BusinessAddress2 = provider.BusinessAddress2;
|
||||
BusinessAddress3 = provider.BusinessAddress3;
|
||||
BusinessCountry = provider.BusinessCountry;
|
||||
BusinessTaxNumber = provider.BusinessTaxNumber;
|
||||
BillingEmail = provider.BillingEmail;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string BusinessName { get; set; }
|
||||
public string BusinessAddress1 { get; set; }
|
||||
public string BusinessAddress2 { get; set; }
|
||||
public string BusinessAddress3 { get; set; }
|
||||
public string BusinessCountry { get; set; }
|
||||
public string BusinessTaxNumber { get; set; }
|
||||
public string BillingEmail { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class ProviderUserResponseModel : ResponseModel
|
||||
{
|
||||
public ProviderUserResponseModel(ProviderUser providerUser, string obj = "providerUser")
|
||||
: base(obj)
|
||||
{
|
||||
if (providerUser == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(providerUser));
|
||||
}
|
||||
|
||||
Id = providerUser.Id.ToString();
|
||||
UserId = providerUser.UserId?.ToString();
|
||||
Type = providerUser.Type;
|
||||
Status = providerUser.Status;
|
||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(providerUser.Permissions);
|
||||
}
|
||||
|
||||
public ProviderUserResponseModel(ProviderUserUserDetails providerUser, string obj = "providerUser")
|
||||
: base(obj)
|
||||
{
|
||||
if (providerUser == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(providerUser));
|
||||
}
|
||||
|
||||
Id = providerUser.Id.ToString();
|
||||
UserId = providerUser.UserId?.ToString();
|
||||
Type = providerUser.Type;
|
||||
Status = providerUser.Status;
|
||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(providerUser.Permissions);
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public ProviderUserStatusType Status { get; set; }
|
||||
public Permissions Permissions { get; set; }
|
||||
}
|
||||
|
||||
public class ProviderUserUserDetailsResponseModel : ProviderUserResponseModel
|
||||
{
|
||||
public ProviderUserUserDetailsResponseModel(ProviderUserUserDetails providerUser,
|
||||
string obj = "providerUserUserDetails") : base(providerUser, obj)
|
||||
{
|
||||
if (providerUser == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(providerUser));
|
||||
}
|
||||
|
||||
Name = providerUser.Name;
|
||||
Email = providerUser.Email;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
public class ProviderUserPublicKeyResponseModel : ResponseModel
|
||||
{
|
||||
public ProviderUserPublicKeyResponseModel(Guid id, string key,
|
||||
string obj = "providerUserPublicKeyResponseModel") : base(obj)
|
||||
{
|
||||
Id = id;
|
||||
Key = key;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
public class ProviderUserBulkResponseModel : ResponseModel
|
||||
{
|
||||
public ProviderUserBulkResponseModel(Guid id, string error,
|
||||
string obj = "providerBulkConfirmResponseModel") : base(obj)
|
||||
{
|
||||
Id = id;
|
||||
Error = error;
|
||||
}
|
||||
public Guid Id { get; set; }
|
||||
public string Error { get; set; }
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ namespace Bit.Core.Models.Api
|
||||
User user,
|
||||
bool userTwoFactorEnabled,
|
||||
IEnumerable<OrganizationUserOrganizationDetails> organizationUserDetails,
|
||||
IEnumerable<ProviderUserProviderDetails> providerUserDetails,
|
||||
IEnumerable<Folder> folders,
|
||||
IEnumerable<CollectionDetails> collections,
|
||||
IEnumerable<CipherDetails> ciphers,
|
||||
@ -24,7 +25,7 @@ namespace Bit.Core.Models.Api
|
||||
IEnumerable<Send> sends)
|
||||
: base("sync")
|
||||
{
|
||||
Profile = new ProfileResponseModel(user, organizationUserDetails, userTwoFactorEnabled);
|
||||
Profile = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails, userTwoFactorEnabled);
|
||||
Folders = folders.Select(f => new FolderResponseModel(f));
|
||||
Ciphers = ciphers.Select(c => new CipherDetailsResponseModel(c, globalSettings, collectionCiphersDict));
|
||||
Collections = collections?.Select(
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Models.Business.Provider
|
||||
@ -8,8 +9,11 @@ namespace Bit.Core.Models.Business.Provider
|
||||
{
|
||||
public IEnumerable<string> Emails { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public Permissions Permissions { get; set; }
|
||||
|
||||
public ProviderUserInvite() {}
|
||||
public ProviderUserInvite(ProviderUserInviteRequestModel requestModel)
|
||||
{
|
||||
Emails = requestModel.Emails;
|
||||
Type = requestModel.Type.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,13 @@ namespace Bit.Core.Models.Data
|
||||
public EventType Type { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public Guid? OrganizationId { get; set; }
|
||||
public Guid? ProviderId { get; set; }
|
||||
public Guid? CipherId { get; set; }
|
||||
public Guid? CollectionId { get; set; }
|
||||
public Guid? GroupId { get; set; }
|
||||
public Guid? PolicyId { get; set; }
|
||||
public Guid? OrganizationUserId { get; set; }
|
||||
public Guid? ProviderUserId { get; set; }
|
||||
public Guid? ActingUserId { get; set; }
|
||||
public DeviceType? DeviceType { get; set; }
|
||||
public string IpAddress { get; set; }
|
||||
|
@ -32,5 +32,7 @@ namespace Bit.Core.Models.Data
|
||||
public string ResetPasswordKey { get; set; }
|
||||
public string PublicKey { get; set; }
|
||||
public string PrivateKey { get; set; }
|
||||
public Guid? ProviderId { get; set; }
|
||||
public string ProviderName { get; set; }
|
||||
}
|
||||
}
|
||||
|
22
src/Core/Models/Data/Provider/ProviderAbility.cs
Normal file
22
src/Core/Models/Data/Provider/ProviderAbility.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class ProviderAbility
|
||||
{
|
||||
public ProviderAbility() { }
|
||||
|
||||
public ProviderAbility(Provider provider)
|
||||
{
|
||||
Id = provider.Id;
|
||||
UseEvents = provider.UseEvents;
|
||||
Enabled = provider.Enabled;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public bool UseEvents { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using Bit.Core.Enums.Provider;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class ProviderOrganizationOrganizationDetails
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ProviderId { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string OrganizationName { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Settings { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime RevisionDate { get; set; }
|
||||
}
|
||||
}
|
17
src/Core/Models/Data/Provider/ProviderUserProviderDetails.cs
Normal file
17
src/Core/Models/Data/Provider/ProviderUserProviderDetails.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using Bit.Core.Enums.Provider;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class ProviderUserProviderDetails
|
||||
{
|
||||
public Guid ProviderId { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Key { get; set; }
|
||||
public ProviderUserStatusType Status { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
public string Permissions { get; set; }
|
||||
}
|
||||
}
|
10
src/Core/Models/Data/Provider/ProviderUserPublicKey.cs
Normal file
10
src/Core/Models/Data/Provider/ProviderUserPublicKey.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class ProviderUserPublicKey
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string PublicKey { get; set; }
|
||||
}
|
||||
}
|
17
src/Core/Models/Data/Provider/ProviderUserUserDetails.cs
Normal file
17
src/Core/Models/Data/Provider/ProviderUserUserDetails.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using Bit.Core.Enums.Provider;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class ProviderUserUserDetails
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ProviderId { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public ProviderUserStatusType Status { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public string Permissions { get; set; }
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
public string ProviderId { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string Url => string.Format("{0}/setup-provider?providerId={1}&email={2}&token={3}",
|
||||
public string Url => string.Format("{0}/providers/setup-provider?providerId={1}&email={2}&token={3}",
|
||||
WebVaultUrl,
|
||||
ProviderId,
|
||||
Email,
|
||||
|
@ -8,7 +8,7 @@
|
||||
public string Email { get; set; }
|
||||
public string ProviderNameUrlEncoded { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string Url => string.Format("{0}/accept-provider?providerId={1}&" +
|
||||
public string Url => string.Format("{0}/providers/accept-provider?providerId={1}&" +
|
||||
"providerUserId={2}&email={3}&providerName={4}&token={5}",
|
||||
WebVaultUrl,
|
||||
ProviderId,
|
||||
|
@ -16,6 +16,7 @@ namespace Bit.Core.Models.Table.Provider
|
||||
public string BusinessTaxNumber { get; set; }
|
||||
public string BillingEmail { get; set; }
|
||||
public ProviderStatusType Status { get; set; }
|
||||
public bool UseEvents { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
@ -14,8 +14,8 @@ namespace Bit.Core.Models.Table.Provider
|
||||
public ProviderUserStatusType Status { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public string Permissions { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
|
@ -1,9 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
{
|
||||
public interface IProviderOrganizationRepository : IRepository<Provider, Guid>
|
||||
public interface IProviderOrganizationRepository : IRepository<ProviderOrganization, Guid>
|
||||
{
|
||||
Task<ICollection<ProviderOrganizationOrganizationDetails>> GetManyDetailsByProviderAsync(Guid providerId);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
@ -8,5 +9,6 @@ namespace Bit.Core.Repositories
|
||||
public interface IProviderRepository : IRepository<Provider, Guid>
|
||||
{
|
||||
Task<ICollection<Provider>> SearchAsync(string name, string userEmail, int skip, int take);
|
||||
Task<ICollection<ProviderAbility>> GetManyAbilitiesAsync();
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
@ -10,7 +11,13 @@ namespace Bit.Core.Repositories
|
||||
{
|
||||
Task<int> GetCountByProviderAsync(Guid providerId, string email, bool onlyRegisteredUsers);
|
||||
Task<ICollection<ProviderUser>> GetManyAsync(IEnumerable<Guid> ids);
|
||||
Task<ICollection<ProviderUser>> GetManyByUserAsync(Guid userId);
|
||||
Task<ProviderUser> GetByProviderUserAsync(Guid providerId, Guid userId);
|
||||
Task<ICollection<ProviderUser>> GetManyByProviderAsync(Guid providerId, ProviderUserType? type = null);
|
||||
Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId);
|
||||
Task<ICollection<ProviderUserProviderDetails>> GetManyDetailsByUserAsync(Guid userId,
|
||||
ProviderUserStatusType? status = null);
|
||||
Task DeleteManyAsync(IEnumerable<Guid> userIds);
|
||||
Task<IEnumerable<ProviderUserPublicKey>> GetManyPublicKeysByProviderUserAsync(Guid providerId, IEnumerable<Guid> Ids);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Settings;
|
||||
using Dapper;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer
|
||||
{
|
||||
public class ProviderOrganizationRepository : Repository<Provider, Guid>, IProviderOrganizationRepository
|
||||
public class ProviderOrganizationRepository : Repository<ProviderOrganization, Guid>, IProviderOrganizationRepository
|
||||
{
|
||||
public ProviderOrganizationRepository(GlobalSettings globalSettings)
|
||||
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
|
||||
@ -13,5 +20,18 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
public ProviderOrganizationRepository(string connectionString, string readOnlyConnectionString)
|
||||
: base(connectionString, readOnlyConnectionString)
|
||||
{ }
|
||||
|
||||
public async Task<ICollection<ProviderOrganizationOrganizationDetails>> GetManyDetailsByProviderAsync(Guid providerId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<ProviderOrganizationOrganizationDetails>(
|
||||
"[dbo].[ProviderOrganizationOrganizationDetails_ReadByProviderId]",
|
||||
new { ProviderId = providerId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using System.Data;
|
||||
using Dapper;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
@ -34,5 +35,17 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<ProviderAbility>> GetManyAbilitiesAsync()
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<ProviderAbility>(
|
||||
"[dbo].[Provider_ReadAbilities]",
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
@ -48,6 +49,32 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<ProviderUser>> GetManyByUserAsync(Guid userId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<ProviderUser>(
|
||||
"[dbo].[ProviderUser_ReadByUserId]",
|
||||
new { UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ProviderUser> GetByProviderUserAsync(Guid providerId, Guid userId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<ProviderUser>(
|
||||
"[dbo].[ProviderUser_ReadByProviderIdUserId]",
|
||||
new { ProviderId = providerId, UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.SingleOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<ProviderUser>> GetManyByProviderAsync(Guid providerId, ProviderUserType? type)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
@ -61,6 +88,33 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<ProviderUserUserDetails>(
|
||||
"[dbo].[ProviderUserUserDetails_ReadByProviderId]",
|
||||
new { ProviderId = providerId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<ProviderUserProviderDetails>> GetManyDetailsByUserAsync(Guid userId,
|
||||
ProviderUserStatusType? status = null)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<ProviderUserProviderDetails>(
|
||||
"[dbo].[ProviderUserProviderDetails_ReadByUserIdStatus]",
|
||||
new { UserId = userId, Status = status },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteManyAsync(IEnumerable<Guid> providerUserIds)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
@ -69,5 +123,19 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
new { Ids = providerUserIds.ToGuidIdArrayTVP() }, commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ProviderUserPublicKey>> GetManyPublicKeysByProviderUserAsync(
|
||||
Guid providerId, IEnumerable<Guid> Ids)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<ProviderUserPublicKey>(
|
||||
"[dbo].[User_ReadPublicKeysByProviderUserIds]",
|
||||
new { ProviderId = providerId, ProviderUserIds = Ids.ToGuidIdArrayTVP() },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace Bit.Core.Services
|
||||
public interface IApplicationCacheService
|
||||
{
|
||||
Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync();
|
||||
Task<IDictionary<Guid, ProviderAbility>> GetProviderAbilitiesAsync();
|
||||
Task UpsertOrganizationAbilityAsync(Organization organization);
|
||||
Task DeleteOrganizationAbilityAsync(Guid organizationId);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ namespace Bit.Core.Services
|
||||
public interface IProviderService
|
||||
{
|
||||
Task CreateAsync(string ownerEmail);
|
||||
Task CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key);
|
||||
Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key);
|
||||
Task UpdateAsync(Provider provider, bool updateBilling = false);
|
||||
|
||||
Task<List<ProviderUser>> InviteUserAsync(Guid providerId, Guid invitingUserId, ProviderUserInvite providerUserInvite);
|
||||
|
@ -223,16 +223,45 @@ namespace Bit.Core.Services
|
||||
await _eventWriteService.CreateAsync(e);
|
||||
}
|
||||
|
||||
// TODO: Implement this
|
||||
public Task LogProviderUserEventAsync(ProviderUser providerUser, EventType type, DateTime? date = null) => throw new NotImplementedException();
|
||||
public async Task LogProviderUserEventAsync(ProviderUser providerUser, EventType type, DateTime? date = null)
|
||||
{
|
||||
await LogProviderUsersEventAsync(new[] { (providerUser, type, date) });
|
||||
}
|
||||
|
||||
// TODO: Implement this
|
||||
public Task LogProviderUsersEventAsync(IEnumerable<(ProviderUser, EventType, DateTime?)> events) => throw new NotImplementedException();
|
||||
public async Task LogProviderUsersEventAsync(IEnumerable<(ProviderUser, EventType, DateTime?)> events)
|
||||
{
|
||||
var providerAbilities = await _applicationCacheService.GetProviderAbilitiesAsync();
|
||||
var eventMessages = new List<IEvent>();
|
||||
foreach (var (providerUser, type, date) in events)
|
||||
{
|
||||
if (!CanUseProviderEvents(providerAbilities, providerUser.ProviderId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
eventMessages.Add(new EventMessage
|
||||
{
|
||||
ProviderId = providerUser.ProviderId,
|
||||
UserId = providerUser.UserId,
|
||||
ProviderUserId = providerUser.Id,
|
||||
Type = type,
|
||||
ActingUserId = _currentContext?.UserId,
|
||||
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
||||
});
|
||||
}
|
||||
|
||||
await _eventWriteService.CreateManyAsync(eventMessages);
|
||||
}
|
||||
|
||||
private bool CanUseEvents(IDictionary<Guid, OrganizationAbility> orgAbilities, Guid orgId)
|
||||
{
|
||||
return orgAbilities != null && orgAbilities.ContainsKey(orgId) &&
|
||||
orgAbilities[orgId].Enabled && orgAbilities[orgId].UseEvents;
|
||||
}
|
||||
|
||||
private bool CanUseProviderEvents(IDictionary<Guid, ProviderAbility> providerAbilities, Guid providerId)
|
||||
{
|
||||
return providerAbilities != null && providerAbilities.ContainsKey(providerId) &&
|
||||
providerAbilities[providerId].Enabled && providerAbilities[providerId].UseEvents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -675,6 +675,7 @@ namespace Bit.Core.Services
|
||||
ProviderId = providerUser.ProviderId.ToString(),
|
||||
ProviderUserId = providerUser.Id.ToString(),
|
||||
ProviderNameUrlEncoded = WebUtility.UrlEncode(providerName),
|
||||
Token = token,
|
||||
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
||||
SiteName = _globalSettings.SiteName,
|
||||
};
|
||||
|
@ -11,14 +11,18 @@ namespace Bit.Core.Services
|
||||
public class InMemoryApplicationCacheService : IApplicationCacheService
|
||||
{
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private DateTime _lastOrgAbilityRefresh = DateTime.MinValue;
|
||||
private IDictionary<Guid, OrganizationAbility> _orgAbilities;
|
||||
private TimeSpan _orgAbilitiesRefreshInterval = TimeSpan.FromMinutes(10);
|
||||
|
||||
private IDictionary<Guid, ProviderAbility> _providerAbilities;
|
||||
|
||||
public InMemoryApplicationCacheService(
|
||||
IOrganizationRepository organizationRepository)
|
||||
IOrganizationRepository organizationRepository, IProviderRepository providerRepository)
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
_providerRepository = providerRepository;
|
||||
}
|
||||
|
||||
public virtual async Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync()
|
||||
@ -27,6 +31,12 @@ namespace Bit.Core.Services
|
||||
return _orgAbilities;
|
||||
}
|
||||
|
||||
public virtual async Task<IDictionary<Guid, ProviderAbility>> GetProviderAbilitiesAsync()
|
||||
{
|
||||
await InitProviderAbilitiesAsync();
|
||||
return _providerAbilities;
|
||||
}
|
||||
|
||||
public virtual async Task UpsertOrganizationAbilityAsync(Organization organization)
|
||||
{
|
||||
await InitOrganizationAbilitiesAsync();
|
||||
@ -62,5 +72,16 @@ namespace Bit.Core.Services
|
||||
_lastOrgAbilityRefresh = now;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitProviderAbilitiesAsync()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
if (_providerAbilities == null || (now - _lastOrgAbilityRefresh) > _orgAbilitiesRefreshInterval)
|
||||
{
|
||||
var abilities = await _providerRepository.GetManyAbilitiesAsync();
|
||||
_providerAbilities = abilities.ToDictionary(a => a.Id);
|
||||
_lastOrgAbilityRefresh = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,9 @@ namespace Bit.Core.Services
|
||||
|
||||
public InMemoryServiceBusApplicationCacheService(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IProviderRepository providerRepository,
|
||||
GlobalSettings globalSettings)
|
||||
: base(organizationRepository)
|
||||
: base(organizationRepository, providerRepository)
|
||||
{
|
||||
_subName = CoreHelpers.GetApplicationCacheServiceBusSubcriptionName(globalSettings);
|
||||
_topicClient = new TopicClient(globalSettings.ServiceBus.ConnectionString,
|
||||
|
34
src/Core/Services/NoopImplementations/NoopProviderService.cs
Normal file
34
src/Core/Services/NoopImplementations/NoopProviderService.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Business.Provider;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
public class NoopProviderService : IProviderService
|
||||
{
|
||||
public Task CreateAsync(string ownerEmail) => throw new NotImplementedException();
|
||||
|
||||
public Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key) => throw new NotImplementedException();
|
||||
|
||||
public Task UpdateAsync(Provider provider, bool updateBilling = false) => throw new NotImplementedException();
|
||||
|
||||
public Task<List<ProviderUser>> InviteUserAsync(Guid providerId, Guid invitingUserId, ProviderUserInvite providerUserInvite) => throw new NotImplementedException();
|
||||
|
||||
public Task<List<Tuple<ProviderUser, string>>> ResendInvitesAsync(Guid providerId, Guid invitingUserId, IEnumerable<Guid> providerUsersId) => throw new NotImplementedException();
|
||||
|
||||
public Task<ProviderUser> AcceptUserAsync(Guid providerUserId, User user, string token) => throw new NotImplementedException();
|
||||
|
||||
public Task<List<Tuple<ProviderUser, string>>> ConfirmUsersAsync(Guid providerId, Dictionary<Guid, string> keys, Guid confirmingUserId) => throw new NotImplementedException();
|
||||
|
||||
public Task SaveUserAsync(ProviderUser user, Guid savingUserId) => throw new NotImplementedException();
|
||||
|
||||
public Task<List<Tuple<ProviderUser, string>>> DeleteUsersAsync(Guid providerId, IEnumerable<Guid> providerUserIds, Guid deletingUserId) => throw new NotImplementedException();
|
||||
|
||||
public Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key) => throw new NotImplementedException();
|
||||
|
||||
public Task RemoveOrganization(Guid providerOrganizationId, Guid removingUserId) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ using Microsoft.Azure.Storage.Blob;
|
||||
using Bit.Core.Models.Table;
|
||||
using IdentityModel;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums.Provider;
|
||||
|
||||
namespace Bit.Core.Utilities
|
||||
{
|
||||
@ -737,7 +738,8 @@ namespace Bit.Core.Utilities
|
||||
return configDict;
|
||||
}
|
||||
|
||||
public static List<KeyValuePair<string, string>> BuildIdentityClaims(User user, ICollection<CurrentContentOrganization> orgs, bool isPremium)
|
||||
public static List<KeyValuePair<string, string>> BuildIdentityClaims(User user, ICollection<CurrentContentOrganization> orgs,
|
||||
ICollection<CurrentContentProvider> providers, bool isPremium)
|
||||
{
|
||||
var claims = new List<KeyValuePair<string, string>>()
|
||||
{
|
||||
@ -849,6 +851,29 @@ namespace Bit.Core.Utilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (providers.Any())
|
||||
{
|
||||
foreach (var group in providers.GroupBy(o => o.Type))
|
||||
{
|
||||
switch (group.Key)
|
||||
{
|
||||
case ProviderUserType.ProviderAdmin:
|
||||
foreach (var provider in group)
|
||||
{
|
||||
claims.Add(new KeyValuePair<string, string>("providerprovideradmin", provider.Id.ToString()));
|
||||
}
|
||||
break;
|
||||
case ProviderUserType.ServiceUser:
|
||||
foreach (var provider in group)
|
||||
{
|
||||
claims.Add(new KeyValuePair<string, string>("providerserviceuser", provider.Id.ToString()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return claims;
|
||||
}
|
||||
|
||||
|
41
src/Core/Utilities/EmailAddressListAttribute.cs
Normal file
41
src/Core/Utilities/EmailAddressListAttribute.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Bit.Core.Utilities
|
||||
{
|
||||
public class EmailAddressListAttribute : ValidationAttribute
|
||||
{
|
||||
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
|
||||
{
|
||||
var emailAttribute = new EmailAddressAttribute();
|
||||
var emails = value as IList<string>;
|
||||
|
||||
if (!emails?.Any() ?? true)
|
||||
{
|
||||
return new ValidationResult("An email is required.");
|
||||
}
|
||||
|
||||
if (emails.Count() > 20)
|
||||
{
|
||||
return new ValidationResult("You can only submit up to 20 emails at a time.");
|
||||
}
|
||||
|
||||
for (var i = 0; i < emails.Count(); i++)
|
||||
{
|
||||
var email = emails.ElementAt(i);
|
||||
if (!emailAttribute.IsValid(email) || email.Contains(" ") || email.Contains("<"))
|
||||
{
|
||||
return new ValidationResult($"Email #{i + 1} is not valid.");
|
||||
}
|
||||
|
||||
if (email.Length > 256)
|
||||
{
|
||||
return new ValidationResult($"Email #{i + 1} is longer than 256 characters.");
|
||||
}
|
||||
}
|
||||
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -126,7 +126,6 @@ namespace Bit.Core.Utilities
|
||||
services.AddSingleton<IAppleIapService, AppleIapService>();
|
||||
services.AddSingleton<ISsoConfigService, SsoConfigService>();
|
||||
services.AddScoped<ISendService, SendService>();
|
||||
services.AddScoped<IProviderService, ProviderService>();
|
||||
}
|
||||
|
||||
public static void AddDefaultServices(this IServiceCollection services, GlobalSettings globalSettings)
|
||||
@ -265,6 +264,11 @@ namespace Bit.Core.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddOosServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IProviderService, NoopProviderService>();
|
||||
}
|
||||
|
||||
public static void AddNoopServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IMailService, NoopMailService>();
|
||||
|
@ -74,6 +74,7 @@
|
||||
<Build Include="dbo\Stored Procedures\SsoConfig_ReadByIdentifier.sql" />
|
||||
<Build Include="dbo\Stored Procedures\SsoConfig_ReadByOrganizationId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\SsoConfig_Update.sql" />
|
||||
<Build Include="dbo\Stored Procedures\User_ReadPublicKeysByProviderUserIds.sql" />
|
||||
<Build Include="dbo\Tables\Grant.sql" />
|
||||
<Build Include="dbo\Tables\SsoConfig.sql" />
|
||||
<Build Include="dbo\Tables\User.sql" />
|
||||
@ -90,6 +91,8 @@
|
||||
<Build Include="dbo\Tables\OrganizationUser.sql" />
|
||||
<Build Include="dbo\Tables\Organization.sql" />
|
||||
<Build Include="dbo\Views\GrantView.sql" />
|
||||
<Build Include="dbo\Views\ProviderOrganizationOrganizationDetailsView.sql" />
|
||||
<Build Include="dbo\Views\ProviderUserProviderDetailsView.sql" />
|
||||
<Build Include="dbo\Views\SsoConfigView.sql" />
|
||||
<Build Include="dbo\Views\UserView.sql" />
|
||||
<Build Include="dbo\Views\U2fView.sql" />
|
||||
@ -332,6 +335,7 @@
|
||||
<Build Include="dbo\Stored Procedures\Provider_DeleteById.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Provider_ReadById.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Provider_Search.sql" />
|
||||
<Build Include="dbo\Stored Procedures\Provider_ReadAbilities.sql" />
|
||||
<Build Include="dbo\Tables\ProviderUser.sql" />
|
||||
<Build Include="dbo\Views\ProviderUserView.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderUser_Create.sql" />
|
||||
@ -343,12 +347,17 @@
|
||||
<Build Include="dbo\Stored Procedures\ProviderUser_ReadByProviderId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderUser_ReadByUserId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderUser_ReadCountByProviderIdEmail.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderUser_ReadByProviderIdUserId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderUserProviderDetails_ReadByUserIdStatus.sql" />
|
||||
<Build Include="dbo\Views\ProviderUserUserDetailsView.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderUserUserDetails_ReadByProviderId.sql" />
|
||||
<Build Include="dbo\Tables\ProviderOrganization.sql" />
|
||||
<Build Include="dbo\Views\ProviderOrganizationView.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderOrganization_Create.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderOrganization_Update.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderOrganization_DeleteById.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderOrganization_ReadById.sql" />
|
||||
<Build Include="dbo\Stored Procedures\ProviderOrganizationOrganizationDetails_ReadByProviderId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\User_BumpAccountRevisionDateByProviderId.sql" />
|
||||
<Build Include="dbo\Stored Procedures\User_BumpAccountRevisionDateByProviderUserId.sql" />
|
||||
<Build Include="dbo\Tables\ProviderOrganizationProviderUser.sql" />
|
||||
|
@ -0,0 +1,13 @@
|
||||
CREATE PROCEDURE [dbo].[ProviderOrganizationOrganizationDetails_ReadByProviderId]
|
||||
@ProviderId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[ProviderOrganizationOrganizationDetailsView]
|
||||
WHERE
|
||||
[ProviderId] = @ProviderId
|
||||
END
|
@ -0,0 +1,15 @@
|
||||
CREATE PROCEDURE [dbo].[ProviderUserProviderDetails_ReadByUserIdStatus]
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@Status TINYINT
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[ProviderUserProviderDetailsView]
|
||||
WHERE
|
||||
[UserId] = @UserId
|
||||
AND (@Status IS NULL OR [Status] = @Status)
|
||||
END
|
@ -0,0 +1,13 @@
|
||||
CREATE PROCEDURE [dbo].[ProviderUserUserDetails_ReadByProviderId]
|
||||
@ProviderId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[ProviderUserUserDetailsView]
|
||||
WHERE
|
||||
[ProviderId] = @ProviderId
|
||||
END
|
@ -28,7 +28,7 @@ BEGIN
|
||||
BEGIN
|
||||
BEGIN TRANSACTION ProviderUser_DeleteMany_PUs
|
||||
|
||||
DELETE TOP(@BatchSize) OU
|
||||
DELETE TOP(@BatchSize) PU
|
||||
FROM
|
||||
[dbo].[ProviderUser] PU
|
||||
INNER JOIN
|
||||
|
@ -0,0 +1,15 @@
|
||||
CREATE PROCEDURE [dbo].[ProviderUser_ReadByProviderIdUserId]
|
||||
@ProviderId UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[ProviderUserView]
|
||||
WHERE
|
||||
[ProviderId] = @ProviderId
|
||||
AND [UserId] = @UserId
|
||||
END
|
@ -9,6 +9,7 @@
|
||||
@BusinessTaxNumber NVARCHAR(30),
|
||||
@BillingEmail NVARCHAR(256),
|
||||
@Status TINYINT,
|
||||
@UseEvents BIT,
|
||||
@Enabled BIT,
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7)
|
||||
@ -28,6 +29,7 @@ BEGIN
|
||||
[BusinessTaxNumber],
|
||||
[BillingEmail],
|
||||
[Status],
|
||||
[UseEvents],
|
||||
[Enabled],
|
||||
[CreationDate],
|
||||
[RevisionDate]
|
||||
@ -44,6 +46,7 @@ BEGIN
|
||||
@BusinessTaxNumber,
|
||||
@BillingEmail,
|
||||
@Status,
|
||||
@UseEvents,
|
||||
@Enabled,
|
||||
@CreationDate,
|
||||
@RevisionDate
|
||||
|
12
src/Sql/dbo/Stored Procedures/Provider_ReadAbilities.sql
Normal file
12
src/Sql/dbo/Stored Procedures/Provider_ReadAbilities.sql
Normal file
@ -0,0 +1,12 @@
|
||||
CREATE PROCEDURE [dbo].[Provider_ReadAbilities]
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[Id],
|
||||
[UseEvents],
|
||||
[Enabled]
|
||||
FROM
|
||||
[dbo].[Provider]
|
||||
END
|
@ -9,6 +9,7 @@
|
||||
@BusinessTaxNumber NVARCHAR(30),
|
||||
@BillingEmail NVARCHAR(256),
|
||||
@Status TINYINT,
|
||||
@UseEvents BIT,
|
||||
@Enabled BIT,
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7)
|
||||
@ -28,6 +29,7 @@ BEGIN
|
||||
[BusinessTaxNumber] = @BusinessTaxNumber,
|
||||
[BillingEmail] = @BillingEmail,
|
||||
[Status] = @Status,
|
||||
[UseEvents] = @UseEvents,
|
||||
[Enabled] = @Enabled,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate
|
||||
|
@ -0,0 +1,19 @@
|
||||
CREATE PROCEDURE [dbo].[User_ReadPublicKeysByProviderUserIds]
|
||||
@ProviderId UNIQUEIDENTIFIER,
|
||||
@ProviderUserIds [dbo].[GuidIdArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
PU.[Id],
|
||||
U.[PublicKey]
|
||||
FROM
|
||||
@ProviderUserIds PUIDs
|
||||
INNER JOIN
|
||||
[dbo].[ProviderUser] PU ON PUIDs.Id = PU.Id AND PU.[Status] = 1 -- Accepted
|
||||
INNER JOIN
|
||||
[dbo].[User] U ON PU.UserId = U.Id
|
||||
WHERE
|
||||
PU.ProviderId = @ProviderId
|
||||
END
|
@ -9,6 +9,7 @@
|
||||
[BusinessTaxNumber] NVARCHAR (30) NULL,
|
||||
[BillingEmail] NVARCHAR (256) NULL,
|
||||
[Status] TINYINT NOT NULL,
|
||||
[UseEvents] BIT NOT NULL,
|
||||
[Enabled] BIT NOT NULL,
|
||||
[CreationDate] DATETIME2 (7) NOT NULL,
|
||||
[RevisionDate] DATETIME2 (7) NOT NULL,
|
||||
|
@ -27,10 +27,16 @@ SELECT
|
||||
OU.[Status],
|
||||
OU.[Type],
|
||||
SU.[ExternalId] SsoExternalId,
|
||||
OU.[Permissions]
|
||||
OU.[Permissions],
|
||||
PO.[ProviderId],
|
||||
P.[Name] ProviderName
|
||||
FROM
|
||||
[dbo].[OrganizationUser] OU
|
||||
INNER JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = OU.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[ProviderOrganization] PO ON PO.[OrganizationId] = O.[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[Provider] P ON P.[Id] = PO.[ProviderId]
|
||||
|
@ -0,0 +1,15 @@
|
||||
CREATE VIEW [dbo].[ProviderOrganizationOrganizationDetailsView]
|
||||
AS
|
||||
SELECT
|
||||
PO.[Id],
|
||||
PO.[ProviderId],
|
||||
PO.[OrganizationId],
|
||||
O.[Name] OrganizationName,
|
||||
PO.[Key],
|
||||
PO.[Settings],
|
||||
PO.[CreationDate],
|
||||
PO.[RevisionDate]
|
||||
FROM
|
||||
[dbo].[ProviderOrganization] PO
|
||||
LEFT JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = PO.[OrganizationId]
|
15
src/Sql/dbo/Views/ProviderUserProviderDetailsView.sql
Normal file
15
src/Sql/dbo/Views/ProviderUserProviderDetailsView.sql
Normal file
@ -0,0 +1,15 @@
|
||||
CREATE VIEW [dbo].[ProviderUserProviderDetailsView]
|
||||
AS
|
||||
SELECT
|
||||
PU.[UserId],
|
||||
PU.[ProviderId],
|
||||
P.[Name],
|
||||
PU.[Key],
|
||||
PU.[Status],
|
||||
PU.[Type],
|
||||
P.[Enabled],
|
||||
PU.[Permissions]
|
||||
FROM
|
||||
[dbo].[ProviderUser] PU
|
||||
LEFT JOIN
|
||||
[dbo].[Provider] P ON P.[Id] = PU.[ProviderId]
|
15
src/Sql/dbo/Views/ProviderUserUserDetailsView.sql
Normal file
15
src/Sql/dbo/Views/ProviderUserUserDetailsView.sql
Normal file
@ -0,0 +1,15 @@
|
||||
CREATE VIEW [dbo].[ProviderUserUserDetailsView]
|
||||
AS
|
||||
SELECT
|
||||
PU.[Id],
|
||||
PU.[UserId],
|
||||
PU.[ProviderId],
|
||||
U.[Name],
|
||||
ISNULL(U.[Email], PU.[Email]) Email,
|
||||
PU.[Status],
|
||||
PU.[Type],
|
||||
PU.[Permissions]
|
||||
FROM
|
||||
[dbo].[ProviderUser] PU
|
||||
LEFT JOIN
|
||||
[dbo].[User] U ON U.[Id] = PU.[UserId]
|
@ -30,6 +30,7 @@ namespace Bit.Api.Test.Controllers
|
||||
private readonly ISsoUserRepository _ssoUserRepository;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
|
||||
public AccountsControllerTests()
|
||||
{
|
||||
@ -39,6 +40,7 @@ namespace Bit.Api.Test.Controllers
|
||||
_folderRepository = Substitute.For<IFolderRepository>();
|
||||
_organizationService = Substitute.For<IOrganizationService>();
|
||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
_providerUserRepository = Substitute.For<IProviderUserRepository>();
|
||||
_paymentService = Substitute.For<IPaymentService>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_sut = new AccountsController(
|
||||
@ -47,6 +49,7 @@ namespace Bit.Api.Test.Controllers
|
||||
_folderRepository,
|
||||
_organizationService,
|
||||
_organizationUserRepository,
|
||||
_providerUserRepository,
|
||||
_paymentService,
|
||||
_ssoUserRepository,
|
||||
_userRepository,
|
||||
|
@ -5,7 +5,7 @@ using AutoFixture.Xunit2;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.Attributes
|
||||
{
|
||||
internal class CustomAutoDataAttribute : AutoDataAttribute
|
||||
public class CustomAutoDataAttribute : AutoDataAttribute
|
||||
{
|
||||
public CustomAutoDataAttribute(params Type[] iCustomizationTypes) : this(iCustomizationTypes
|
||||
.Select(t => (ICustomization)Activator.CreateInstance(t)).ToArray())
|
||||
|
@ -11,12 +11,14 @@ namespace Bit.Core.Test.Services
|
||||
private readonly InMemoryApplicationCacheService _sut;
|
||||
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
|
||||
public InMemoryApplicationCacheServiceTests()
|
||||
{
|
||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
||||
_providerRepository = Substitute.For<IProviderRepository>();
|
||||
|
||||
_sut = new InMemoryApplicationCacheService(_organizationRepository);
|
||||
_sut = new InMemoryApplicationCacheService(_organizationRepository, _providerRepository);
|
||||
}
|
||||
|
||||
// Remove this test when we add actual tests. It only proves that
|
||||
|
@ -12,15 +12,18 @@ namespace Bit.Core.Test.Services
|
||||
private readonly InMemoryServiceBusApplicationCacheService _sut;
|
||||
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public InMemoryServiceBusApplicationCacheServiceTests()
|
||||
{
|
||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
||||
_providerRepository = Substitute.For<IProviderRepository>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
|
||||
_sut = new InMemoryServiceBusApplicationCacheService(
|
||||
_organizationRepository,
|
||||
_providerRepository,
|
||||
_globalSettings
|
||||
);
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ BEGIN
|
||||
[BusinessTaxNumber] NVARCHAR (30) NULL,
|
||||
[BillingEmail] NVARCHAR (256) NOT NULL,
|
||||
[Status] TINYINT NOT NULL,
|
||||
[UseEvents] BIT NOT NULL,
|
||||
[Enabled] BIT NOT NULL,
|
||||
[CreationDate] DATETIME2 (7) NOT NULL,
|
||||
[RevisionDate] DATETIME2 (7) NOT NULL,
|
||||
@ -101,6 +102,29 @@ GO
|
||||
ALTER TABLE [dbo].[Provider] ALTER COLUMN [BillingEmail] NVARCHAR (256) NULL;
|
||||
GO
|
||||
|
||||
IF COL_LENGTH('[dbo].[Provider]', 'UseEvents') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE
|
||||
[dbo].[Provider]
|
||||
ADD
|
||||
[UseEvents] BIT NULL
|
||||
END
|
||||
GO
|
||||
|
||||
UPDATE
|
||||
[dbo].[Provider]
|
||||
SET
|
||||
[UseEvents] = 0
|
||||
WHERE
|
||||
[UseEvents] IS NULL
|
||||
GO
|
||||
|
||||
ALTER TABLE
|
||||
[dbo].[Provider]
|
||||
ALTER COLUMN
|
||||
[UseEvents] BIT NOT NULL
|
||||
GO
|
||||
|
||||
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'ProviderView')
|
||||
BEGIN
|
||||
DROP VIEW [dbo].[ProviderView];
|
||||
@ -132,6 +156,7 @@ CREATE PROCEDURE [dbo].[Provider_Create]
|
||||
@BusinessTaxNumber NVARCHAR(30),
|
||||
@BillingEmail NVARCHAR(256),
|
||||
@Status TINYINT,
|
||||
@UseEvents BIT,
|
||||
@Enabled BIT,
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7)
|
||||
@ -151,6 +176,7 @@ BEGIN
|
||||
[BusinessTaxNumber],
|
||||
[BillingEmail],
|
||||
[Status],
|
||||
[UseEvents],
|
||||
[Enabled],
|
||||
[CreationDate],
|
||||
[RevisionDate]
|
||||
@ -167,6 +193,7 @@ BEGIN
|
||||
@BusinessTaxNumber,
|
||||
@BillingEmail,
|
||||
@Status,
|
||||
@UseEvents,
|
||||
@Enabled,
|
||||
@CreationDate,
|
||||
@RevisionDate
|
||||
@ -191,6 +218,7 @@ CREATE PROCEDURE [dbo].[Provider_Update]
|
||||
@BusinessTaxNumber NVARCHAR(30),
|
||||
@BillingEmail NVARCHAR(256),
|
||||
@Status TINYINT,
|
||||
@UseEvents BIT,
|
||||
@Enabled BIT,
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7)
|
||||
@ -210,6 +238,7 @@ BEGIN
|
||||
[BusinessTaxNumber] = @BusinessTaxNumber,
|
||||
[BillingEmail] = @BillingEmail,
|
||||
[Status] = @Status,
|
||||
[UseEvents] = @UseEvents,
|
||||
[Enabled] = @Enabled,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate
|
||||
@ -923,7 +952,7 @@ BEGIN
|
||||
BEGIN
|
||||
BEGIN TRANSACTION ProviderUser_DeleteMany_PUs
|
||||
|
||||
DELETE TOP(@BatchSize) OU
|
||||
DELETE TOP(@BatchSize) PU
|
||||
FROM
|
||||
[dbo].[ProviderUser] PU
|
||||
INNER JOIN
|
||||
@ -984,3 +1013,256 @@ BEGIN
|
||||
END
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[ProviderUser_ReadByProviderIdUserId]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[ProviderUser_ReadByProviderIdUserId]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[ProviderUser_ReadByProviderIdUserId]
|
||||
@ProviderId UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[ProviderUserView]
|
||||
WHERE
|
||||
[ProviderId] = @ProviderId
|
||||
AND [UserId] = @UserId
|
||||
END
|
||||
GO
|
||||
|
||||
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'ProviderUserUserDetailsView')
|
||||
BEGIN
|
||||
DROP VIEW [dbo].[ProviderUserUserDetailsView];
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE VIEW [dbo].[ProviderUserUserDetailsView]
|
||||
AS
|
||||
SELECT
|
||||
PU.[Id],
|
||||
PU.[UserId],
|
||||
PU.[ProviderId],
|
||||
U.[Name],
|
||||
ISNULL(U.[Email], PU.[Email]) Email,
|
||||
PU.[Status],
|
||||
PU.[Type],
|
||||
PU.[Permissions]
|
||||
FROM
|
||||
[dbo].[ProviderUser] PU
|
||||
LEFT JOIN
|
||||
[dbo].[User] U ON U.[Id] = PU.[UserId]
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[ProviderUserUserDetails_ReadByProviderId]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[ProviderUserUserDetails_ReadByProviderId]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[ProviderUserUserDetails_ReadByProviderId]
|
||||
@ProviderId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[ProviderUserUserDetailsView]
|
||||
WHERE
|
||||
[ProviderId] = @ProviderId
|
||||
END
|
||||
GO
|
||||
|
||||
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'ProviderUserProviderDetailsView')
|
||||
BEGIN
|
||||
DROP VIEW [dbo].[ProviderUserProviderDetailsView];
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE VIEW [dbo].[ProviderUserProviderDetailsView]
|
||||
AS
|
||||
SELECT
|
||||
PU.[UserId],
|
||||
PU.[ProviderId],
|
||||
P.[Name],
|
||||
PU.[Key],
|
||||
PU.[Status],
|
||||
PU.[Type],
|
||||
P.[Enabled],
|
||||
PU.[Permissions]
|
||||
FROM
|
||||
[dbo].[ProviderUser] PU
|
||||
LEFT JOIN
|
||||
[dbo].[Provider] P ON P.[Id] = PU.[ProviderId]
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[ProviderUserProviderDetails_ReadByUserIdStatus]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[ProviderUserProviderDetails_ReadByUserIdStatus]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[ProviderUserProviderDetails_ReadByUserIdStatus]
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@Status TINYINT
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[ProviderUserProviderDetailsView]
|
||||
WHERE
|
||||
[UserId] = @UserId
|
||||
AND (@Status IS NULL OR [Status] = @Status)
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[Provider_ReadAbilities]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[Provider_ReadAbilities]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[Provider_ReadAbilities]
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
[Id],
|
||||
[UseEvents],
|
||||
[Enabled]
|
||||
FROM
|
||||
[dbo].[Provider]
|
||||
END
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[User_ReadPublicKeysByProviderUserIds]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[User_ReadPublicKeysByProviderUserIds]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[User_ReadPublicKeysByProviderUserIds]
|
||||
@ProviderId UNIQUEIDENTIFIER,
|
||||
@ProviderUserIds [dbo].[GuidIdArray] READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
PU.[Id],
|
||||
U.[PublicKey]
|
||||
FROM
|
||||
@ProviderUserIds PUIDs
|
||||
INNER JOIN
|
||||
[dbo].[ProviderUser] PU ON PUIDs.Id = PU.Id AND PU.[Status] = 1 -- Accepted
|
||||
INNER JOIN
|
||||
[dbo].[User] U ON PU.UserId = U.Id
|
||||
WHERE
|
||||
PU.ProviderId = @ProviderId
|
||||
END
|
||||
GO
|
||||
|
||||
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'ProviderOrganizationOrganizationDetailsView')
|
||||
BEGIN
|
||||
DROP VIEW [dbo].[ProviderOrganizationOrganizationDetailsView];
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE VIEW [dbo].[ProviderOrganizationOrganizationDetailsView]
|
||||
AS
|
||||
SELECT
|
||||
PO.[Id],
|
||||
PO.[ProviderId],
|
||||
PO.[OrganizationId],
|
||||
O.[Name] OrganizationName,
|
||||
PO.[Key],
|
||||
PO.[Settings],
|
||||
PO.[CreationDate],
|
||||
PO.[RevisionDate]
|
||||
FROM
|
||||
[dbo].[ProviderOrganization] PO
|
||||
LEFT JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = PO.[OrganizationId]
|
||||
GO
|
||||
|
||||
IF OBJECT_ID('[dbo].[ProviderOrganizationOrganizationDetails_ReadByProviderId]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP PROCEDURE [dbo].[ProviderOrganizationOrganizationDetails_ReadByProviderId]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE PROCEDURE [dbo].[ProviderOrganizationOrganizationDetails_ReadByProviderId]
|
||||
@ProviderId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[ProviderOrganizationOrganizationDetailsView]
|
||||
WHERE
|
||||
[ProviderId] = @ProviderId
|
||||
END
|
||||
GO
|
||||
|
||||
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'OrganizationUserOrganizationDetailsView')
|
||||
BEGIN
|
||||
DROP VIEW [dbo].[OrganizationUserOrganizationDetailsView];
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE VIEW [dbo].[OrganizationUserOrganizationDetailsView]
|
||||
AS
|
||||
SELECT
|
||||
OU.[UserId],
|
||||
OU.[OrganizationId],
|
||||
O.[Name],
|
||||
O.[Enabled],
|
||||
O.[UsePolicies],
|
||||
O.[UseSso],
|
||||
O.[UseGroups],
|
||||
O.[UseDirectory],
|
||||
O.[UseEvents],
|
||||
O.[UseTotp],
|
||||
O.[Use2fa],
|
||||
O.[UseApi],
|
||||
O.[UseResetPassword],
|
||||
O.[SelfHost],
|
||||
O.[UsersGetPremium],
|
||||
O.[Seats],
|
||||
O.[MaxCollections],
|
||||
O.[MaxStorageGb],
|
||||
O.[Identifier],
|
||||
OU.[Key],
|
||||
OU.[ResetPasswordKey],
|
||||
O.[PublicKey],
|
||||
O.[PrivateKey],
|
||||
OU.[Status],
|
||||
OU.[Type],
|
||||
SU.[ExternalId] SsoExternalId,
|
||||
OU.[Permissions],
|
||||
PO.[ProviderId],
|
||||
P.[Name] ProviderName
|
||||
FROM
|
||||
[dbo].[OrganizationUser] OU
|
||||
INNER JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = OU.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[ProviderOrganization] PO ON PO.[OrganizationId] = O.[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[Provider] P ON P.[Id] = PO.[ProviderId]
|
Loading…
Reference in New Issue
Block a user