1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

Remove Business Portal (#1614)

This commit is contained in:
Oscar Hinton 2021-10-06 10:39:13 +02:00 committed by GitHub
parent fccfce1048
commit 79447b6671
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
107 changed files with 188 additions and 7196 deletions

View File

@ -102,9 +102,6 @@ jobs:
base_path: ./src
- service_name: Notifications
base_path: ./src
- service_name: Portal
base_path: ./bitwarden_license/src
gulp: true
- service_name: Server
base_path: ./util
- service_name: Setup
@ -225,10 +222,6 @@ jobs:
base_path: ./src
docker_repo: bitwarden
dotnet: true
- service_name: Portal
base_path: ./bitwarden_license/src
docker_repo: bitwarden
dotnet: true
- service_name: Server
base_path: ./util
docker_repo: bitwarden

View File

@ -75,7 +75,6 @@ jobs:
- name: Billing
- name: Events
- name: Sso
- name: Portal
- name: Identity
steps:
- name: Setup

View File

@ -56,7 +56,6 @@ jobs:
- name: Billing
- name: Events
- name: Sso
- name: Portal
- name: Identity
steps:
- name: Setup

2
.gitignore vendored
View File

@ -209,8 +209,6 @@ src/Admin/wwwroot/lib
src/Admin/wwwroot/css
.vscode/*
**/.vscode/*
bitwarden_license/src/Portal/wwwroot/lib
bitwarden_license/src/Portal/wwwroot/css
bitwarden_license/src/Sso/wwwroot/lib
bitwarden_license/src/Sso/wwwroot/css
.github/test/build.secrets

View File

@ -16,7 +16,12 @@ Our current software products have the following licenses:
*Bitwarden server:* The main Bitwarden server code is licensed under the AGPL 3.0 license.
*Business Portal and the SSO integration:* Code for certain new modules that are designed and developed for use by larger organizations and enterprise environments is released under the Bitwarden License, a "source available" license. The Bitwarden License provides users access to product source code for non-production purposes such as development and testing, but requires a paid subscription for production use of the product, and environments supporting production. At this time, the new Business Portal and the SSO integration are the only Bitwarden modules to which this license applies.
*CommCore and SSO integration:* Code for certain new modules that are designed and developed for use by larger
organizations and enterprise environments is released under the Bitwarden License, a "source available" license. The
Bitwarden License provides users access to product source code for non-production purposes such as development and
testing, but requires a paid subscription for production use of the product, and environments supporting production.
Additionally the Api module by default includes CommCore which is under the Bitwarden License, however this can be
disabled by using `/p:DefineConstants="OSS"` as an argument to `dotnet` while building the module.
# Frequently Asked Questions

View File

@ -57,8 +57,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api.Test", "test\Api.Test\A
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src - Bitwarden License", "src - Bitwarden License", "{4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Portal", "bitwarden_license\src\Portal\Portal.csproj", "{BA852F18-852F-4154-973B-77D577B8CA04}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sso", "bitwarden_license\src\Sso\Sso.csproj", "{4866AF64-6640-4C65-A662-A31E02FF9064}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Icons.Test", "test\Icons.Test\Icons.Test.csproj", "{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}"
@ -144,10 +142,6 @@ Global
{860DE301-0B3E-4717-9C21-A9B4C3C2B121}.Debug|Any CPU.Build.0 = Debug|Any CPU
{860DE301-0B3E-4717-9C21-A9B4C3C2B121}.Release|Any CPU.ActiveCfg = Release|Any CPU
{860DE301-0B3E-4717-9C21-A9B4C3C2B121}.Release|Any CPU.Build.0 = Release|Any CPU
{BA852F18-852F-4154-973B-77D577B8CA04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA852F18-852F-4154-973B-77D577B8CA04}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA852F18-852F-4154-973B-77D577B8CA04}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA852F18-852F-4154-973B-77D577B8CA04}.Release|Any CPU.Build.0 = Release|Any CPU
{4866AF64-6640-4C65-A662-A31E02FF9064}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4866AF64-6640-4C65-A662-A31E02FF9064}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4866AF64-6640-4C65-A662-A31E02FF9064}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -193,7 +187,6 @@ Global
{79BB453F-D0D8-4DDF-9809-A405C56692BD} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
{54DED792-A022-417E-9804-21FCC9C7C610} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{860DE301-0B3E-4717-9C21-A9B4C3C2B121} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{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}
{BDC1D592-5947-47ED-9903-7CDBB12A50C8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}

View File

@ -1,33 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Portal.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Bit.Portal.Components
{
public class OrganizationPickerViewComponent : ViewComponent
{
private readonly EnterprisePortalCurrentContext _enterprisePortalCurrentContext;
public OrganizationPickerViewComponent(EnterprisePortalCurrentContext enterprisePortalCurrentContext)
{
_enterprisePortalCurrentContext = enterprisePortalCurrentContext;
}
public Task<IViewComponentResult> InvokeAsync()
{
return Task.FromResult(View(new OrganizationPickerViewModel
{
SelectedOrganization = _enterprisePortalCurrentContext?.SelectedOrganizationId?.ToString(),
Organizations = _enterprisePortalCurrentContext?.OrganizationsDetails?.Where(x => x.UseBusinessPortal)
.Select(o => new SelectListItem
{
Value = o.OrganizationId.ToString(),
Text = o.Name
}).ToList() ?? new List<SelectListItem>()
}) as IViewComponentResult);
}
}
}

View File

@ -1,63 +0,0 @@
using System.Threading.Tasks;
using Bit.Portal.Utilities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Portal.Controllers
{
public class AuthController : Controller
{
private readonly EnterprisePortalTokenSignInManager _signInManager;
public AuthController(
EnterprisePortalTokenSignInManager signInManager)
{
_signInManager = signInManager;
}
[HttpGet("~/login")]
public async Task<IActionResult> Index(string userId, string token, string organizationId, string returnUrl)
{
var result = await _signInManager.TokenSignInAsync(userId, token, false);
if (!result.Succeeded)
{
return RedirectToAction("Index", "Home", new
{
error = 2
});
}
if (!string.IsNullOrWhiteSpace(organizationId))
{
Response.Cookies.Append("SelectedOrganization", organizationId, new CookieOptions { HttpOnly = true });
}
if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
[HttpPost("~/logout")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
return RedirectToAction("LoggedOut");
}
[HttpGet("~/logged-out")]
public IActionResult LoggedOut()
{
return View();
}
[HttpGet("~/access-denied")]
public IActionResult AccessDenied()
{
return View();
}
}
}

View File

@ -1,70 +0,0 @@
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Bit.Portal.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Bit.Core.Models.Table;
namespace Bit.Portal.Controllers
{
public class HomeController : Controller
{
private readonly SignInManager<User> _signInManager;
private readonly ILogger<HomeController> _logger;
private readonly EnterprisePortalCurrentContext _enterprisePortalCurrentContext;
public HomeController(
SignInManager<User> signInManager,
ILogger<HomeController> logger,
EnterprisePortalCurrentContext enterprisePortalCurrentContext)
{
_signInManager = signInManager;
_logger = logger;
_enterprisePortalCurrentContext = enterprisePortalCurrentContext;
}
public IActionResult Index()
{
if(_signInManager.IsSignedIn(User))
{
return View();
}
else
{
return NotFound();
}
}
[HttpGet("~/alive")]
[HttpGet("~/now")]
[AllowAnonymous]
public DateTime GetAlive()
{
return DateTime.UtcNow;
}
[Authorize]
public IActionResult SetSelectedOrganization(Guid id, string returnUrl)
{
if (_enterprisePortalCurrentContext.Organizations.Any(o => o.Id == id))
{
Response.Cookies.Append("SelectedOrganization", id.ToString(), new CookieOptions { HttpOnly = true });
}
if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}

View File

@ -1,91 +0,0 @@
using System.Threading.Tasks;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Portal.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Portal.Controllers
{
[Authorize]
public class SsoController : Controller
{
private readonly ISsoConfigRepository _ssoConfigRepository;
private readonly ISsoConfigService _ssoConfigService;
private readonly EnterprisePortalCurrentContext _enterprisePortalCurrentContext;
private readonly II18nService _i18nService;
private readonly GlobalSettings _globalSettings;
public SsoController(
ISsoConfigRepository ssoConfigRepository,
ISsoConfigService ssoConfigService,
EnterprisePortalCurrentContext enterprisePortalCurrentContext,
II18nService i18nService,
GlobalSettings globalSettings)
{
_ssoConfigRepository = ssoConfigRepository;
_ssoConfigService = ssoConfigService;
_enterprisePortalCurrentContext = enterprisePortalCurrentContext;
_i18nService = i18nService;
_globalSettings = globalSettings;
}
[HttpGet]
public async Task<IActionResult> Index()
{
var orgId = _enterprisePortalCurrentContext.SelectedOrganizationId;
if (orgId == null)
{
return Redirect("~/");
}
if (!_enterprisePortalCurrentContext.SelectedOrganizationDetails.UseSso ||
!_enterprisePortalCurrentContext.CanManageSsoForSelectedOrganization)
{
return Redirect("~/");
}
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(orgId.Value);
var model = new SsoConfigEditViewModel(ssoConfig, orgId.Value, _i18nService, _globalSettings);
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(SsoConfigEditViewModel model)
{
var orgId = _enterprisePortalCurrentContext.SelectedOrganizationId;
if (orgId == null)
{
return Redirect("~/");
}
if (!_enterprisePortalCurrentContext.SelectedOrganizationDetails.UseSso ||
!_enterprisePortalCurrentContext.CanManageSsoForSelectedOrganization)
{
return Redirect("~/");
}
model.BuildLists(_i18nService);
if (!ModelState.IsValid)
{
return View(model);
}
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(orgId.Value);
if (ssoConfig == null)
{
ssoConfig = model.ToSsoConfig(orgId.GetValueOrDefault());
}
else
{
ssoConfig = model.ToSsoConfig(ssoConfig);
}
await _ssoConfigService.SaveAsync(ssoConfig);
return View(model);
}
}
}

View File

@ -1,20 +0,0 @@
FROM mcr.microsoft.com/dotnet/aspnet:5.0
LABEL com.bitwarden.product="bitwarden"
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
gosu \
curl \
&& rm -rf /var/lib/apt/lists/*
ENV ASPNETCORE_URLS http://+:5000
WORKDIR /app
EXPOSE 5000
COPY obj/build-output/publish .
COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -1,160 +0,0 @@
using System;
using Bit.Core.Context;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Bit.Core.Repositories;
using System.Linq;
using System.Collections.Generic;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Portal
{
public class EnterprisePortalCurrentContext : CurrentContext
{
private readonly IServiceProvider _serviceProvider;
public EnterprisePortalCurrentContext(IProviderUserRepository providerUserRepository,
IServiceProvider serviceProvider) : base(providerUserRepository)
{
_serviceProvider = serviceProvider;
}
public Guid? SelectedOrganizationId { get; set; }
public OrganizationUserOrganizationDetails SelectedOrganizationDetails { get; set; }
public List<OrganizationUserOrganizationDetails> OrganizationsDetails { get; set; }
public bool ManagerForSelectedOrganization =>
SelectedOrganizationDetails?.Type == Core.Enums.OrganizationUserType.Manager ||
SelectedOrganizationDetails?.Type == Core.Enums.OrganizationUserType.Admin ||
SelectedOrganizationDetails?.Type == Core.Enums.OrganizationUserType.Owner;
public bool AdminForSelectedOrganization =>
SelectedOrganizationDetails?.Type == Core.Enums.OrganizationUserType.Admin ||
SelectedOrganizationDetails?.Type == Core.Enums.OrganizationUserType.Owner;
public bool OwnerForSelectedOrganization =>
SelectedOrganizationDetails?.Type == Core.Enums.OrganizationUserType.Owner;
public bool CanManageSsoForSelectedOrganization =>
AdminForSelectedOrganization || SelectedOrganizationDetailsPermissions.ManageSso == true;
public Permissions SelectedOrganizationDetailsPermissions => CoreHelpers.LoadClassFromJsonData<Permissions>(SelectedOrganizationDetails?.Permissions);
public async override Task SetContextAsync(ClaimsPrincipal user)
{
var nameId = user.FindFirstValue(ClaimTypes.NameIdentifier);
if (Guid.TryParse(nameId, out var nameIdGuid))
{
UserId = nameIdGuid;
}
if (!UserId.HasValue)
{
return;
}
// TODO: maybe make loading orgs Lazy<T> somehow?
var orgUserRepo = _serviceProvider.GetRequiredService<IOrganizationUserRepository>();
var userOrgs = await orgUserRepo.GetManyDetailsByUserAsync(UserId.Value,
Core.Enums.OrganizationUserStatusType.Confirmed);
OrganizationsDetails = userOrgs.ToList();
Organizations = userOrgs.Select(ou => new CurrentContentOrganization
{
Id = ou.OrganizationId,
Type = ou.Type
}).ToList();
// Add all provider orgs.
var providerOrgs = await GetProviderOrganizations();
Organizations.AddRange(providerOrgs.Select(po => new CurrentContentOrganization
{
Id = po.OrganizationId,
Type = OrganizationUserType.Owner,
}));
// Yes this is ugly, but the business portal is deprecated.
OrganizationsDetails.AddRange(providerOrgs.Select(pu => new OrganizationUserOrganizationDetails
{
OrganizationId = pu.OrganizationId,
UserId = pu.UserId,
Name = pu.Name,
UsePolicies = pu.UsePolicies,
UseSso = pu.UseSso,
UseGroups = pu.UseGroups,
UseDirectory = pu.UseDirectory,
UseEvents = pu.UseEvents,
UseTotp = pu.UseTotp,
Use2fa = pu.Use2fa,
UseApi = pu.UseApi,
UseResetPassword = pu.UseResetPassword,
SelfHost = pu.SelfHost,
UsersGetPremium = pu.UsersGetPremium,
Seats = pu.Seats,
MaxCollections = pu.MaxCollections,
MaxStorageGb = pu.MaxStorageGb,
Key = pu.Key,
Status = OrganizationUserStatusType.Confirmed,
Type = OrganizationUserType.Owner,
Enabled = pu.Enabled,
SsoExternalId = null,
Identifier = pu.Identifier,
Permissions = null,
ResetPasswordKey = null,
PublicKey = pu.PublicKey,
PrivateKey = pu.PrivateKey,
ProviderId = pu.ProviderId,
ProviderName = pu.ProviderName,
}));
if (SelectedOrganizationId == null && HttpContext.Request.Cookies.ContainsKey("SelectedOrganization") &&
Guid.TryParse(HttpContext.Request.Cookies["SelectedOrganization"], out var selectedOrgId))
{
SelectedOrganizationId = Organizations.FirstOrDefault(o => o.Id == selectedOrgId)?.Id;
SelectedOrganizationDetails = OrganizationsDetails.FirstOrDefault(
o => o.OrganizationId == SelectedOrganizationId);
}
if (DeviceIdentifier == null && HttpContext.Request.Cookies.ContainsKey("DeviceIdentifier"))
{
DeviceIdentifier = HttpContext.Request.Cookies["DeviceIdentifier"];
}
DeviceType = Core.Enums.DeviceType.UnknownBrowser;
if (HttpContext.Request.Headers.ContainsKey("User-Agent"))
{
var userAgent = HttpContext.Request.Headers["User-Agent"].ToString();
if (userAgent.Contains(" Firefox/") || userAgent.Contains(" Gecko/"))
{
DeviceType = Core.Enums.DeviceType.FirefoxBrowser;
}
else if (userAgent.IndexOf(" OPR/") >= 0)
{
DeviceType = Core.Enums.DeviceType.OperaBrowser;
}
else if (userAgent.Contains(" Edge/"))
{
DeviceType = Core.Enums.DeviceType.EdgeBrowser;
}
else if (userAgent.Contains(" Vivaldi/"))
{
DeviceType = Core.Enums.DeviceType.VivaldiBrowser;
}
else if (userAgent.Contains(" Safari/") && !userAgent.Contains("Chrome"))
{
DeviceType = Core.Enums.DeviceType.SafariBrowser;
}
else if (userAgent.Contains(" Chrome/"))
{
DeviceType = Core.Enums.DeviceType.ChromeBrowser;
}
else if (userAgent.Contains(" Trident/"))
{
DeviceType = Core.Enums.DeviceType.IEBrowser;
}
}
}
}
}

View File

@ -1,11 +0,0 @@
using System;
namespace Bit.Portal.Models
{
public class ErrorViewModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}

View File

@ -1,11 +0,0 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Bit.Portal.Models
{
public class OrganizationPickerViewModel
{
public string SelectedOrganization { get; set; }
public List<SelectListItem> Organizations { get; set; }
}
}

View File

@ -1,117 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Json;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Sso;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Bit.Portal.Models
{
public class SsoConfigEditViewModel
{
public SsoConfigEditViewModel() { }
public SsoConfigEditViewModel(SsoConfig ssoConfig, Guid organizationId,
II18nService i18nService, GlobalSettings globalSettings)
{
if (ssoConfig != null)
{
Id = ssoConfig.Id;
Enabled = ssoConfig.Enabled;
}
SsoConfigurationData configurationData;
if (!string.IsNullOrWhiteSpace(ssoConfig?.Data))
{
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
configurationData = JsonSerializer.Deserialize<SsoConfigurationData>(ssoConfig.Data, options);
}
else
{
configurationData = new SsoConfigurationData();
}
Data = new SsoConfigDataViewModel(configurationData, globalSettings, organizationId);
BuildLists(i18nService);
}
public long Id { get; set; }
[Display(Name = "Enabled")]
public bool Enabled { get; set; }
public SsoConfigDataViewModel Data { get; set; }
public List<SelectListItem> ConfigTypes { get; set; }
public List<SelectListItem> SpNameIdFormats { get; set; }
public List<SelectListItem> BindingTypes { get; set; }
public List<SelectListItem> SigningBehaviors { get; set; }
public List<SelectListItem> SigningAlgorithms { get; set; }
public List<SelectListItem> RedirectBehaviors { get; set; }
public SsoConfig ToSsoConfig(Guid organizationId)
{
return ToSsoConfig(new SsoConfig { OrganizationId = organizationId });
}
public SsoConfig ToSsoConfig(SsoConfig existingConfig)
{
existingConfig.Enabled = Enabled;
var configurationData = Data.ToConfigurationData();
existingConfig.Data = JsonSerializer.Serialize(configurationData, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
return existingConfig;
}
public void BuildLists(II18nService i18nService)
{
ConfigTypes = Enum.GetNames(typeof(SsoType))
.Select(configType => new SelectListItem
{
Value = configType,
Text = i18nService.T(configType),
}).ToList();
SpNameIdFormats = Enum.GetNames(typeof(Saml2NameIdFormat))
.Select(nameIdFormat => new SelectListItem
{
Value = nameIdFormat,
Text = i18nService.T(nameIdFormat),
}).ToList();
BindingTypes = Enum.GetNames(typeof(Saml2BindingType))
.Select(bindingType => new SelectListItem
{
Value = bindingType,
Text = i18nService.T(bindingType),
}).ToList();
SigningBehaviors = Enum.GetNames(typeof(Saml2SigningBehavior))
.Select(behavior => new SelectListItem
{
Value = behavior,
Text = i18nService.T(behavior),
}).ToList();
SigningAlgorithms = SamlSigningAlgorithms.GetEnumerable().Select(a =>
new SelectListItem(a, a)).ToList();
RedirectBehaviors = Enum.GetNames(typeof(OpenIdConnectRedirectBehavior))
.Select(behavior => new SelectListItem
{
Value = behavior,
Text = i18nService.T(behavior),
}).ToList();
}
}
}

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<UserSecretsId>bitwarden-Portal</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Core\Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NewRelic.Agent" Version="8.41.0" />
</ItemGroup>
</Project>

View File

@ -1,8 +0,0 @@
using System;
namespace Bit.Portal
{
public class PortalSettings
{
}
}

View File

@ -1,36 +0,0 @@
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Bit.Core.Utilities;
using Serilog.Events;
namespace Bit.Portal
{
public class Program
{
public static void Main(string[] args)
{
Host
.CreateDefaultBuilder(args)
.ConfigureCustomAppConfiguration(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.ConfigureLogging((hostingContext, logging) =>
logging.AddSerilog(hostingContext, e =>
{
var context = e.Properties["SourceContext"].ToString();
if (e.Properties.ContainsKey("RequestPath") &&
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
{
return false;
}
return e.Level >= LogEventLevel.Error;
}));
})
.Build()
.Run();
}
}
}

View File

@ -1,4 +0,0 @@
using System.Reflection;
using Microsoft.Extensions.Localization;
[assembly: ResourceLocation("Resources")]

View File

@ -1,27 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:52313",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Portal": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "http://localhost:52313",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -1,233 +0,0 @@
@import "webfonts.css";
$primary: #175DDC;
$primary-accent: #1252A3;
$success: #00a65a;
$info: #555555;
$warning: #bf7e16;
$danger: #dd4b39;
$theme-colors: ( "primary-accent": $primary-accent );
$body-bg: #ffffff;
$body-color: #333333;
$font-family-sans-serif: 'Open Sans','Helvetica Neue',Helvetica, Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';
$h1-font-size: 2rem;
$h2-font-size: 1.3rem;
$h3-font-size: 1rem;
$h4-font-size: 1rem;
$h5-font-size: 1rem;
$h6-font-size: 1rem;
$small-font-size: 90%;
$font-size-lg: 1.15rem;
$code-font-size: 100%;
$navbar-padding-y: .75rem;
$grid-gutter-width: 20px;
$card-spacer-y: .6rem;
$list-group-item-padding-y: .6rem;
$list-group-active-color: $body-color;
$list-group-active-bg: #ffffff;
$list-group-active-border-color: rgba(#000000, .125);
$dropdown-link-color: $body-color;
$dropdown-link-hover-bg: rgba(#000000, .06);
$dropdown-link-active-color: $dropdown-link-color;
$dropdown-link-active-bg: rgba(#000000, .1);
$dropdown-item-padding-x: 1rem;
$navbar-brand-font-size: 35px;
$navbar-brand-height: 35px;
$navbar-brand-padding-y: 0;
$navbar-dark-color: rgba(#ffffff, .7);
$navbar-dark-hover-color: rgba(#ffffff, .9);
$navbar-nav-link-padding-x: 0.8rem;
$input-bg: #fbfbfb;
$input-focus-bg: #ffffff;
$input-disabled-bg: #e0e0e0;
$input-placeholder-color: #b4b4b4;
$table-accent-bg: rgba(#000000, .02);
$table-hover-bg: rgba(#000000, .03);
$modal-backdrop-opacity: 0.3;
$btn-font-weight: 600;
$lead-font-weight: normal;
$grid-breakpoints: (
xs: 0,
sm: 1px,
md: 2px,
lg: 3px,
xl: 4px
);
@import "../node_modules/bootstrap/scss/bootstrap.scss";
html {
font-size: 14px;
}
body {
min-width: 1010px;
}
.page-header, .secondary-header {
border-bottom: 1px solid $border-color;
padding-bottom: 0.6rem;
margin-bottom: 0.5rem;
h1, h2, h3, h4 {
margin: 0;
}
}
h1, h2, h3, h4, h5 {
small {
font-size: 80%;
}
}
input, select, textarea {
&:required {
box-shadow: none;
}
}
.navbar {
padding-left: 0;
padding-right: 0;
padding-top: 0.4rem;
padding-bottom: 0.4rem;
.dropdown-menu {
min-width: 200px;
max-width: 300px;
.dropdown-item-text {
line-height: 1.3;
span, small {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.nav-link {
font-weight: 600;
}
}
.dropdown-menu {
button {
cursor: pointer;
}
}
.container {
width: 980px;
max-width: none !important;
margin: 0 auto;
padding: 0;
}
.page-content {
margin-top: 20px;
}
.footer {
margin-top: 40px;
padding: 40px 0 40px 0;
border-top: 1px solid $border-color;
}
.callout {
padding: $alert-padding-y $alert-padding-x;
margin-bottom: $alert-margin-bottom;
border: 1px solid $card-border-color;
border-left-width: 5px;
border-radius: $card-inner-border-radius;
background-color: #fafafa;
.callout-heading {
margin-top: 0;
}
h3.callout-heading {
font-weight: bold;
text-transform: uppercase;
}
&.callout-primary {
border-left-color: $primary;
.callout-heading {
color: $primary;
}
}
&.callout-info {
border-left-color: $gray-800;
.callout-heading {
color: $gray-800;
}
}
&.callout-danger {
border-left-color: $danger;
.callout-heading {
color: $danger;
}
}
&.callout-success {
border-left-color: $success;
.callout-heading {
color: $success;
}
}
&.callout-warning {
border-left-color: $warning;
.callout-heading {
color: $warning;
}
}
}
.config-section {
padding-top: 20px;
padding-bottom: 20px;
h2 {
border-bottom: 1px solid #ccc;
}
}
//////////////////////////
.validation-summary-valid {
display: none;
}
.alert.validation-summary-errors > ul {
margin-bottom: 0;
}
.input-validation-error {
border: solid 1px $danger;
border-color: $danger;
}

View File

@ -1,90 +0,0 @@
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-display: auto;
src: url(webfonts/Open_Sans-italic-300.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-display: auto;
src: url(webfonts/Open_Sans-italic-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-display: auto;
src: url(webfonts/Open_Sans-italic-600.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 700;
font-display: auto;
src: url(webfonts/Open_Sans-italic-700.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 800;
font-display: auto;
src: url(webfonts/Open_Sans-italic-800.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-display: auto;
src: url(webfonts/Open_Sans-normal-300.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-display: auto;
src: url(webfonts/Open_Sans-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-display: auto;
src: url(webfonts/Open_Sans-normal-600.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-display: auto;
src: url(webfonts/Open_Sans-normal-700.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 800;
font-display: auto;
src: url(webfonts/Open_Sans-normal-800.woff) format('woff');
unicode-range: U+0-10FFFF;
}

View File

@ -1,113 +0,0 @@
using Bit.Core;
using Bit.Core.Context;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Bit.Portal.Utilities;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
namespace Bit.Portal
{
public class Startup
{
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
Configuration = configuration;
Environment = env;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Options
services.AddOptions();
// Settings
var globalSettings = services.AddGlobalSettingsServices(Configuration);
services.Configure<PortalSettings>(Configuration.GetSection("PortalSettings"));
// Data Protection
services.AddCustomDataProtectionServices(Environment, globalSettings);
// Repositories
services.AddSqlServerRepositories(globalSettings);
// Context
services.AddScoped<EnterprisePortalCurrentContext>();
services.AddScoped<ICurrentContext, CurrentContext>((serviceProvider) =>
serviceProvider.GetService<EnterprisePortalCurrentContext>());
// Identity
services.AddEnterprisePortalTokenIdentityServices();
if (globalSettings.SelfHosted)
{
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Path = "/portal";
});
}
// Services
services.AddBaseServices();
services.AddDefaultServices(globalSettings);
services.AddCoreLocalizationServices();
// Mvc
services.AddControllersWithViews()
.AddViewAndDataAnnotationLocalization();
services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env,
IHostApplicationLifetime appLifetime,
GlobalSettings globalSettings,
ILogger<Startup> logger)
{
app.UseSerilog(env, appLifetime, globalSettings);
if (globalSettings.SelfHosted)
{
app.UsePathBase("/portal");
app.UseForwardedHeaders(globalSettings);
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCoreLocalization();
// Add static files to the request pipeline.
app.UseStaticFiles();
// Add routing
app.UseRouting();
// Add authentication and authorization to the request pipeline.
app.UseAuthentication();
app.UseAuthorization();
// Add current context
app.UseMiddleware<EnterprisePortalCurrentContextMiddleware>();
// Add endpoints to the request pipeline.
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
// Log startup
logger.LogInformation(Constants.BypassFiltersEventId, globalSettings.ProjectName + " started.");
}
}
}

View File

@ -1,24 +0,0 @@
using Bit.Core;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace Bit.Portal.Utilities
{
public class EnterprisePortalCurrentContextMiddleware
{
private readonly RequestDelegate _next;
public EnterprisePortalCurrentContextMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext, EnterprisePortalCurrentContext currentContext,
GlobalSettings globalSettings)
{
await currentContext.BuildAsync(httpContext, globalSettings);
await _next.Invoke(httpContext);
}
}
}

View File

@ -1,41 +0,0 @@
using System;
using Bit.Core.Identity;
using Bit.Core.Models.Table;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Bit.Portal.Utilities
{
public static class EnterprisePortalServiceCollectionExtensions
{
public static (IdentityBuilder, IdentityBuilder) AddEnterprisePortalTokenIdentityServices(
this IServiceCollection services)
{
services.TryAddTransient<ILookupNormalizer, LowerInvariantLookupNormalizer>();
var passwordlessIdentityBuilder = services.AddIdentity<User, Role>()
.AddUserStore<UserStore>()
.AddRoleStore<RoleStore>()
.AddDefaultTokenProviders();
var regularIdentityBuilder = services.AddIdentityCore<User>()
.AddUserStore<UserStore>();
services.TryAddScoped<EnterprisePortalTokenSignInManager, EnterprisePortalTokenSignInManager>();
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/login";
options.LogoutPath = "/logout";
options.AccessDeniedPath = "/access-denied";
options.Cookie.Name = $"Bitwarden_BusinessPortal";
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromDays(2);
options.ReturnUrlParameter = "returnUrl";
options.SlidingExpiration = true;
});
return (passwordlessIdentityBuilder, regularIdentityBuilder);
}
}
}

View File

@ -1,74 +0,0 @@
using System;
using System.Threading.Tasks;
using Bit.Core.Models.Table;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Bit.Portal.Utilities
{
public class EnterprisePortalTokenSignInManager : SignInManager<User>
{
public const string TokenSignInPurpose = "EnterprisePortalTokenSignIn";
public EnterprisePortalTokenSignInManager(
UserManager<User> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<User> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<User>> logger,
IAuthenticationSchemeProvider schemes,
IUserConfirmation<User> confirmation)
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
{ }
public async Task<SignInResult> TokenSignInAsync(User user, string token, bool isPersistent)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var attempt = await CheckTokenSignInAsync(user, token);
return attempt.Succeeded ?
await SignInOrTwoFactorAsync(user, isPersistent, bypassTwoFactor: true) : attempt;
}
public async Task<SignInResult> TokenSignInAsync(string userId, string token, bool isPersistent)
{
var user = await UserManager.FindByIdAsync(userId);
if (user == null)
{
return SignInResult.Failed;
}
return await TokenSignInAsync(user, token, isPersistent);
}
public virtual async Task<SignInResult> CheckTokenSignInAsync(User user, string token)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var error = await PreSignInCheck(user);
if (error != null)
{
return error;
}
if (await UserManager.VerifyUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
TokenSignInPurpose, token))
{
return SignInResult.Success;
}
Logger.LogWarning(2, "User {userId} failed to provide the correct enterprise portal token.",
await UserManager.GetUserIdAsync(user));
return SignInResult.Failed;
}
}
}

View File

@ -1,8 +0,0 @@
@inject Bit.Core.Services.II18nService i18nService
@{
ViewData["Title"] = i18nService.T("AccessDenied");
}
<p>
@i18nService.T("AccessDeniedError")
</p>

View File

@ -1,8 +0,0 @@
@inject Bit.Core.Services.II18nService i18nService
@{
ViewData["Title"] = i18nService.T("LoggedOut");
}
<p>
@i18nService.T("LoggedOutMessage")
</p>

View File

@ -1,21 +0,0 @@
@{
ViewData["Title"] = "Home Page";
}
@inject SignInManager<Bit.Core.Models.Table.User> SignInManager
@inject Bit.Core.Services.II18nService i18nService
@inject Bit.Portal.EnterprisePortalCurrentContext EnterprisePortalCurrentContext
<div class="page-header">
<h1>@i18nService.T("WelcomeToBusinessPortal")</h1>
</div>
<div class="row">
@if (EnterprisePortalCurrentContext.SelectedOrganizationDetails.UseSso &&
EnterprisePortalCurrentContext.CanManageSsoForSelectedOrganization)
{
<div class="col">
<a class="card p-5 border border-primary" asp-area="" asp-controller="Sso" asp-action="Index">
<h2 class="card-title text-center">@i18nService.T("SingleSignOn")</h2>
</a>
</div>
}
</div>

View File

@ -1,9 +0,0 @@
@model OrganizationPickerViewModel
<form method="get" asp-action="SetSelectedOrganization" asp-controller="Home" class="form-inline my-2 my-lg-0">
<label asp-for="SelectedOrganization" class="sr-only">Organization</label>
<select asp-for="SelectedOrganization" asp-items="Model.Organizations" name="id" class="form-control mr-sm-2">
<option value="">--Select Organization--</option>
</select>
<button type="submit" class="btn btn-outline-light my-2 my-sm-0">Go</button>
</form>

View File

@ -1,25 +0,0 @@
@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>

View File

@ -1,106 +0,0 @@
@using static Bit.Core.Utilities.CoreHelpers;
@inject SignInManager<Bit.Core.Models.Table.User> SignInManager
@inject Bit.Core.Services.II18nService i18nService
@inject Bit.Portal.EnterprisePortalCurrentContext EnterprisePortalCurrentContext
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Business Portal</title>
<link rel="stylesheet" href="~/css/webfonts.css" />
<environment include="Development">
<link rel="stylesheet" href="~/lib/font-awesome/css/font-awesome.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="~/lib/font-awesome/css/font-awesome.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</environment>
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-primary mb-4">
<div class="container">
<a class="navbar-brand" asp-controller="Home" asp-action="Index">
<i class="fa fa-shield" aria-hidden="true"></i>
</a>
@if (SignInManager.IsSignedIn(User))
{
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
@if (EnterprisePortalCurrentContext.SelectedOrganizationDetails.UseSso)
{
<li class="nav-item">
<a class="nav-link" asp-area="" asp-controller="Sso" asp-action="Index">
@i18nService.T("SingleSignOn")
</a>
</li>
}
</ul>
</div>
}
@if (SignInManager.IsSignedIn(User))
{
@await Component.InvokeAsync("OrganizationPicker")
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item dropdown">
<a class="nav-item nav-link dropdown-toggle" href="#" id="nav-profile" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fa fa-user-circle fa-lg" aria-hidden="true"></i>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nav-profile">
<div class="dropdown-item-text d-flex align-items-center">
<i aria-hidden="true" class="fa fa-user-circle fa-lg"></i>
<div class="ml-2 overflow-hidden">
<span>Logged in as</span>
<small class="text-muted">@User.Identity.Name</small>
</div>
</div>
<div class="dropdown-divider"></div>
<form asp-controller="Auth" asp-action="Logout" method="post">
<button type="submit" class="dropdown-item">
<i class="fa fa-fw fa-sign-out" aria-hidden="true"></i>
Log Out
</button>
</form>
</div>
</li>
</ul>
}
</div>
</nav>
<div class="container page-content">
@RenderBody()
</div>
<div class="container footer text-muted">
<div class="row">
<div class="col">
&copy; @DateTime.Now.Year, Bitwarden Inc.
</div>
<div class="col text-center"></div>
<div class="col text-right">
Version @GetVersion()
</div>
</div>
</div>
<environment include="Development">
<script src="~/lib/jquery/jquery.slim.js"></script>
<script src="~/lib/popper/popper.js"></script>
<script src="~/lib/bootstrap/js/bootstrap.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/jquery/jquery.slim.min.js" asp-append-version="true"></script>
<script src="~/lib/popper/popper.min.js" asp-append-version="true"></script>
<script src="~/lib/bootstrap/js/bootstrap.min.js" asp-append-version="true"></script>
</environment>
<script src="~/js/site.js" asp-append-version="true"></script>
@RenderSection("Scripts", required: false)
</body>
</html>

View File

@ -1,354 +0,0 @@
@inject Bit.Core.Services.II18nService i18nService
@model SsoConfigEditViewModel
@{
ViewData["Title"] = i18nService.T("EditSsoConfig");
}
@section Scripts {
<script type="text/javascript">
function toggleVisibility() {
var value = $('#Data_ConfigType').val();
if (value == 'OpenIdConnect') {
$('.oidc-config').show();
$('.saml-config').hide();
} else {
$('.oidc-config').hide();
$('.saml-config').show();
}
}
$(function () {
// Set initial visibility
toggleVisibility();
// Toggle visibiity on change
$('#Data_ConfigType').change(function () {
toggleVisibility();
});
$('.copy-button').on('click', function () {
const $control = $(this).closest('.form-group').find('input[type="text"], textarea');
if ($control.length > 0) {
const elem = $control[0];
elem.select();
elem.setSelectionRange(0, $control.val().length);
document.execCommand('copy');
}
});
});
</script>
}
<div class="page-header">
<h1>@i18nService.T("SingleSignOn")</h1>
</div>
<form method="post" id="edit-form">
<div class="form-group">
<div class="form-check">
<input asp-for="Enabled" type="checkbox" class="form-check-input">
<label asp-for="Enabled" class="form-check-label"></label>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.ConfigType"></label>
<select asp-for="Data.ConfigType" asp-items="Model.ConfigTypes" class="form-control"></select>
</div>
</div>
<!-- OIDC -->
<div class="oidc-config">
<div class="config-section">
<h2>@i18nService.T("OpenIdConnectConfig")</h2>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.CallbackPath"></label>
<div class="input-group">
<input asp-for="Data.CallbackPath" class="form-control" readonly>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary copy-button"
aria-label="@i18nService.T("CopyCallbackPath")" title="@i18nService.T("CopyCallbackPath")"
tabindex="-1">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.SignedOutCallbackPath"></label>
<div class="input-group">
<input asp-for="Data.SignedOutCallbackPath" class="form-control" readonly>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary copy-button"
aria-label="@i18nService.T("CopySignedOutCallbackPath")" title="@i18nService.T("CopySignedOutCallbackPath")"
tabindex="-1">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.Authority"></label>
<input asp-for="Data.Authority" class="form-control">
<span asp-validation-for="Data.Authority" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.ClientId"></label>
<input asp-for="Data.ClientId" class="form-control">
<span asp-validation-for="Data.ClientId" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.ClientSecret"></label>
<input asp-for="Data.ClientSecret" class="form-control">
<span asp-validation-for="Data.ClientSecret" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.MetadataAddress"></label>
<input asp-for="Data.MetadataAddress" class="form-control">
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.RedirectBehavior"></label>
<select asp-for="Data.RedirectBehavior" asp-items="Model.RedirectBehaviors"
class="form-control"></select>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<div class="form-check">
<input asp-for="Data.GetClaimsFromUserInfoEndpoint" type="checkbox" class="form-check-input">
<label asp-for="Data.GetClaimsFromUserInfoEndpoint" class="form-check-label"></label>
</div>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.AdditionalScopes"></label>
<input asp-for="Data.AdditionalScopes" class="form-control">
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.AdditionalUserIdClaimTypes"></label>
<input asp-for="Data.AdditionalUserIdClaimTypes" class="form-control">
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.AdditionalEmailClaimTypes"></label>
<input asp-for="Data.AdditionalEmailClaimTypes" class="form-control">
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.AdditionalNameClaimTypes"></label>
<input asp-for="Data.AdditionalNameClaimTypes" class="form-control">
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.AcrValues"></label>
<input asp-for="Data.AcrValues" class="form-control">
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.ExpectedReturnAcrValue"></label>
<input asp-for="Data.ExpectedReturnAcrValue" class="form-control">
</div>
</div>
</div>
</div>
<div class="saml-config">
<!-- SAML2 SP -->
<div class="config-section">
<h2>@i18nService.T("SamlSpConfig")</h2>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.SpEntityId"></label>
<div class="input-group">
<input asp-for="Data.SpEntityId" class="form-control" readonly>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary copy-button"
aria-label="@i18nService.T("CopySpEntityId")" title="@i18nService.T("CopySpEntityId")"
tabindex="-1">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.SpMetadataUrl"></label>
<div class="input-group">
<input asp-for="Data.SpMetadataUrl" class="form-control" readonly>
<div class="input-group-append">
<a class="btn btn-outline-secondary launch-button"
href="@Model.Data.SpMetadataUrl"
target="_blank"
aria-label="@i18nService.T("LaunchSpMetadataUrl")" title="@i18nService.T("LaunchSpMetadataUrl")"
tabindex="-1">
<i class="fa fa-lg fa-external-link" aria-hidden="true"></i>
</a>
<button type="button" class="btn btn-outline-secondary copy-button"
aria-label="@i18nService.T("CopySpMetadataUrl")" title="@i18nService.T("CopySpMetadataUrl")"
tabindex="-1">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="col-1">
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.SpAcsUrl"></label>
<div class="input-group">
<input asp-for="Data.SpAcsUrl" class="form-control" readonly>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary copy-button"
aria-label="@i18nService.T("CopySpAcsUrl")" title="@i18nService.T("CopySpAcsUrl")"
tabindex="-1">
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="col-1">
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.SpNameIdFormat"></label>
<select asp-for="Data.SpNameIdFormat" asp-items="Model.SpNameIdFormats"
class="form-control"></select>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.SpOutboundSigningAlgorithm"></label>
<select asp-for="Data.SpOutboundSigningAlgorithm" asp-items="Model.SigningAlgorithms"
class="form-control"></select>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.SpSigningBehavior"></label>
<select asp-for="Data.SpSigningBehavior" asp-items="Model.SigningBehaviors"
class="form-control"></select>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.SpMinIncomingSigningAlgorithm"></label>
<select asp-for="Data.SpMinIncomingSigningAlgorithm" asp-items="Model.SigningAlgorithms"
class="form-control"></select>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input asp-for="Data.SpWantAssertionsSigned" type="checkbox" class="form-check-input">
<label asp-for="Data.SpWantAssertionsSigned" class="form-check-label"></label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input asp-for="Data.SpValidateCertificates" type="checkbox" class="form-check-input">
<label asp-for="Data.SpValidateCertificates" class="form-check-label"></label>
</div>
</div>
</div>
<!-- SAML2 IDP -->
<div class="config-section">
<h2>@i18nService.T("SamlIdpConfig")</h2>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.IdpEntityId"></label>
<input asp-for="Data.IdpEntityId" class="form-control">
<span asp-validation-for="Data.IdpEntityId" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.IdpBindingType"></label>
<select asp-for="Data.IdpBindingType" asp-items="Model.BindingTypes" class="form-control"></select>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.IdpSingleSignOnServiceUrl"></label>
<input asp-for="Data.IdpSingleSignOnServiceUrl" class="form-control">
<span asp-validation-for="Data.IdpSingleSignOnServiceUrl" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.IdpSingleLogoutServiceUrl"></label>
<input asp-for="Data.IdpSingleLogoutServiceUrl" class="form-control">
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.IdpArtifactResolutionServiceUrl"></label>
<input asp-for="Data.IdpArtifactResolutionServiceUrl" class="form-control">
<span asp-validation-for="Data.IdpArtifactResolutionServiceUrl" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.IdpX509PublicCert"></label>
<textarea asp-for="Data.IdpX509PublicCert" class="form-control form-control-sm text-monospace" rows="6"></textarea>
<span asp-validation-for="Data.IdpX509PublicCert" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-7 form-group">
<label asp-for="Data.IdpOutboundSigningAlgorithm"></label>
<select asp-for="Data.IdpOutboundSigningAlgorithm" asp-items="Model.SigningAlgorithms"
class="form-control"></select>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input asp-for="Data.IdpAllowUnsolicitedAuthnResponse" type="checkbox" class="form-check-input">
<label asp-for="Data.IdpAllowUnsolicitedAuthnResponse" class="form-check-label"></label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input asp-for="Data.IdpDisableOutboundLogoutRequests" type="checkbox" class="form-check-input">
<label asp-for="Data.IdpDisableOutboundLogoutRequests" class="form-check-label"></label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input asp-for="Data.IdpWantAuthnRequestsSigned" type="checkbox" class="form-check-input">
<label asp-for="Data.IdpWantAuthnRequestsSigned" class="form-check-label"></label>
</div>
</div>
</div>
</div>
</form>
<div class="d-flex">
<button type="submit" class="btn btn-primary" form="edit-form">@i18nService.T("Save")</button>
<a class="btn btn-outline-secondary ml-1" asp-action="index">@i18nService.T("Cancel")</a>
</div>

View File

@ -1,4 +0,0 @@
@using Microsoft.AspNetCore.Identity
@using Bit.Portal
@using Bit.Portal.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@ -1,3 +0,0 @@
@{
Layout = "_Layout";
}

View File

@ -1,20 +0,0 @@
{
"globalSettings": {
"baseServiceUri": {
"vault": "https://localhost:8080",
"api": "http://localhost:4000",
"identity": "http://localhost:33656",
"admin": "http://localhost:62911",
"notifications": "http://localhost:61840",
"sso": "http://localhost:51822",
"portal": "http://localhost:52313",
"internalNotifications": "http://localhost:61840",
"internalAdmin": "http://localhost:62911",
"internalIdentity": "http://localhost:33656",
"internalApi": "http://localhost:4000",
"internalVault": "http://localhost:4001",
"internalSso": "http://localhost:51822",
"internalPortal": "http://localhost:52313"
}
}
}

View File

@ -1,23 +0,0 @@
{
"globalSettings": {
"baseServiceUri": {
"vault": "https://vault.bitwarden.com",
"api": "https://api.bitwarden.com",
"identity": "https://identity.bitwarden.com",
"admin": "https://admin.bitwarden.com",
"notifications": "https://notifications.bitwarden.com",
"sso": "https://sso.bitwarden.com",
"portal": "http://portal.bitwarden.com",
"internalNotifications": "https://notifications.bitwarden.com",
"internalAdmin": "https://admin.bitwarden.com",
"internalIdentity": "https://identity.bitwarden.com",
"internalApi": "https://api.bitwarden.com",
"internalVault": "https://vault.bitwarden.com",
"internalSso": "https://sso.bitwarden.com",
"internalPortal": "https://portal.bitwarden.com"
},
"braintree": {
"production": true
}
}
}

View File

@ -1,23 +0,0 @@
{
"globalSettings": {
"baseServiceUri": {
"vault": "https://vault.qa.bitwarden.pw",
"api": "https://api.qa.bitwarden.pw",
"identity": "https://identity.qa.bitwarden.pw",
"admin": "https://admin.qa.bitwarden.pw",
"notifications": "https://notifications.qa.bitwarden.pw",
"sso": "https://sso.qa.bitwarden.pw",
"portal": "http://portal.qa.bitwarden.pw",
"internalNotifications": "https://notifications.qa.bitwarden.pw",
"internalAdmin": "https://admin.qa.bitwarden.pw",
"internalIdentity": "https://identity.qa.bitwarden.pw",
"internalApi": "https://api.qa.bitwarden.pw",
"internalVault": "https://vault.qa.bitwarden.pw",
"internalSso": "https://sso.qa.bitwarden.pw",
"internalPortal": "https://portal.qa.bitwarden.pw"
},
"braintree": {
"production": false
}
}
}

View File

@ -1,20 +0,0 @@
{
"globalSettings": {
"baseServiceUri": {
"vault": null,
"api": null,
"identity": null,
"admin": null,
"notifications": null,
"sso": null,
"portal": null,
"internalNotifications": null,
"internalAdmin": null,
"internalIdentity": null,
"internalApi": null,
"internalVault": null,
"internalSso": null,
"internalPortal": null
}
}
}

View File

@ -1,48 +0,0 @@
{
"globalSettings": {
"selfHosted": false,
"siteName": "Bitwarden",
"projectName": "Business Portal",
"stripeApiKey": "SECRET",
"sqlServer": {
"connectionString": "SECRET"
},
"mail": {
"sendGridApiKey": "SECRET",
"amazonConfigSetName": "Email",
"replyToEmail": "no-reply@bitwarden.com"
},
"identityServer": {
"certificateThumbprint": "SECRET"
},
"dataProtection": {
"certificateThumbprint": "SECRET"
},
"storage": {
"connectionString": "SECRET"
},
"events": {
"connectionString": "SECRET"
},
"serviceBus": {
"connectionString": "SECRET",
"applicationCacheTopicName": "SECRET"
},
"documentDb": {
"uri": "SECRET",
"key": "SECRET"
},
"notificationHub": {
"connectionString": "SECRET",
"hubName": "SECRET"
},
"amazon": {
"accessKeyId": "SECRET",
"accessKeySecret": "SECRET",
"region": "SECRET"
}
},
"portalSettings": {
}
}

View File

@ -1,18 +0,0 @@
$curDir = pwd
$dir = Split-Path -Parent $MyInvocation.MyCommand.Path
echo "`n## Building Portal"
echo "`nBuilding app"
echo ".NET Core version $(dotnet --version)"
echo "Restore"
dotnet restore $dir\Portal.csproj
echo "Clean"
dotnet clean $dir\Portal.csproj -c "Release" -o $dir\obj\Azure\publish
echo "Node Build"
cd $dir
npm install
cd $curDir
gulp --gulpfile $dir\gulpfile.js build
echo "Publish"
dotnet publish $dir\Portal.csproj -c "Release" -o $dir\obj\Azure\publish

View File

@ -1,25 +0,0 @@
#!/usr/bin/env bash
set -e
CUR_DIR="$(pwd)"
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo -e "\n## Building Portal"
echo -e "\nBuilding app"
echo ".NET Core version $(dotnet --version)"
echo "Restore"
dotnet restore "$DIR/Portal.csproj"
echo "Clean"
dotnet clean "$DIR/Portal.csproj" -c "Release" -o "$DIR/obj/build-output/publish"
echo "Node Build"
cd "$DIR"
npm install
cd "$CUR_DIR"
gulp --gulpfile "$DIR/gulpfile.js" build
echo "Publish"
dotnet publish "$DIR/Portal.csproj" -c "Release" -o "$DIR/obj/build-output/publish"
echo -e "\nBuilding docker image"
docker --version
docker build -t bitwarden/portal "$DIR/."

View File

@ -1,41 +0,0 @@
#!/bin/bash
# Setup
GROUPNAME="bitwarden"
USERNAME="bitwarden"
LUID=${LOCAL_UID:-0}
LGID=${LOCAL_GID:-0}
# Step down from host root to well-known nobody/nogroup user
if [ $LUID -eq 0 ]
then
LUID=65534
fi
if [ $LGID -eq 0 ]
then
LGID=65534
fi
# Create user and group
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
mkhomedir_helper $USERNAME
# The rest...
chown -R $USERNAME:$GROUPNAME /app
mkdir -p /etc/bitwarden/core
mkdir -p /etc/bitwarden/logs
mkdir -p /etc/bitwarden/ca-certificates
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
&& update-ca-certificates
exec gosu $USERNAME:$GROUPNAME dotnet /app/Portal.dll

View File

@ -1,71 +0,0 @@
/// <binding BeforeBuild='build' Clean='clean' ProjectOpened='build' />
const gulp = require('gulp');
const merge = require('merge-stream');
const sass = require('gulp-sass');
const del = require('del');
const paths = {};
paths.webroot = './wwwroot/';
paths.npmDir = './node_modules/';
paths.sassDir = './Sass/';
paths.libDir = paths.webroot + 'lib/';
paths.cssDir = paths.webroot + 'css/';
paths.jsDir = paths.webroot + 'js/';
paths.sass = paths.sassDir + '**/*.scss';
paths.minCss = paths.cssDir + '**/*.min.css';
paths.js = paths.jsDir + '**/*.js';
paths.minJs = paths.jsDir + '**/*.min.js';
paths.libJs = paths.libDir + '**/*.js';
paths.libMinJs = paths.libDir + '**/*.min.js';
function clean() {
return del([paths.minJs, paths.cssDir, paths.libDir]);
}
function lib() {
const libs = [
{
src: paths.npmDir + 'bootstrap/dist/js/*',
dest: paths.libDir + 'bootstrap/js'
},
{
src: paths.npmDir + 'popper.js/dist/umd/*',
dest: paths.libDir + 'popper'
},
{
src: paths.npmDir + 'font-awesome/css/*',
dest: paths.libDir + 'font-awesome/css'
},
{
src: paths.npmDir + 'font-awesome/fonts/*',
dest: paths.libDir + 'font-awesome/fonts'
},
{
src: paths.npmDir + 'jquery/dist/jquery.slim*',
dest: paths.libDir + 'jquery'
},
];
const tasks = libs.map((lib) => {
return gulp.src(lib.src).pipe(gulp.dest(lib.dest));
});
return merge(tasks);
}
function runSass() {
return gulp.src(paths.sass)
.pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
.pipe(gulp.dest(paths.cssDir));
}
function sassWatch() {
gulp.watch(paths.sass, runSass);
}
exports.build = gulp.series(clean, gulp.parallel([lib, runSass]));
exports['sass:watch'] = sassWatch;
exports.sass = runSass;
exports.lib = lib;
exports.clean = clean;

View File

@ -1,6 +0,0 @@
<?xml version="1.0"?>
<configuration xmlns="urn:newrelic-config">
<application></application>
<service licenseKey="SECRET"></service>
<log directory="/home/LogFiles/NewRelic" level="info"></log>
</configuration>

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
{
"name": "bitwarden-enterprise-portal",
"version": "0.0.0",
"description": "Bitwarden Enterprise Portal",
"repository": "https://github.com/bitwarden/enterprise",
"license": "-",
"devDependencies": {
"bootstrap": "4.5.0",
"del": "5.1.0",
"font-awesome": "4.7.0",
"gulp": "4.0.2",
"gulp-sass": "4.0.1",
"jquery": "3.5.1",
"merge-stream": "1.0.1",
"popper.js": "1.16.1"
}
}

View File

@ -1 +0,0 @@
Open+Sans:300,300i,400,400i,600,600i,700,700i,800,800i&subset=cyrillic,cyrillic-ext,greek,greek-ext,latin-ext

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,4 +0,0 @@
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
// for details on configuring this project to bundle and minify static web assets.
// Write your JavaScript code.

View File

@ -7,14 +7,12 @@
"admin": "http://localhost:62911",
"notifications": "http://localhost:61840",
"sso": "http://localhost:51822",
"portal": "http://localhost:52313",
"internalNotifications": "http://localhost:61840",
"internalAdmin": "http://localhost:62911",
"internalIdentity": "http://localhost:33656",
"internalApi": "http://localhost:4000",
"internalVault": "http://localhost:4001",
"internalSso": "http://localhost:51822",
"internalPortal": "http://localhost:52313"
"internalSso": "http://localhost:51822"
}
}
}

View File

@ -7,14 +7,12 @@
"admin": "https://admin.bitwarden.com",
"notifications": "https://notifications.bitwarden.com",
"sso": "https://sso.bitwarden.com",
"portal": "http://portal.bitwarden.com",
"internalNotifications": "https://notifications.bitwarden.com",
"internalAdmin": "https://admin.bitwarden.com",
"internalIdentity": "https://identity.bitwarden.com",
"internalApi": "https://api.bitwarden.com",
"internalVault": "https://vault.bitwarden.com",
"internalSso": "https://sso.bitwarden.com",
"internalPortal": "https://portal.bitwarden.com"
"internalSso": "https://sso.bitwarden.com"
},
"braintree": {
"production": true

View File

@ -7,14 +7,12 @@
"admin": "https://admin.qa.bitwarden.pw",
"notifications": "https://notifications.qa.bitwarden.pw",
"sso": "https://sso.qa.bitwarden.pw",
"portal": "http://portal.qa.bitwarden.pw",
"internalNotifications": "https://notifications.qa.bitwarden.pw",
"internalAdmin": "https://admin.qa.bitwarden.pw",
"internalIdentity": "https://identity.qa.bitwarden.pw",
"internalApi": "https://api.qa.bitwarden.pw",
"internalVault": "https://vault.qa.bitwarden.pw",
"internalSso": "https://sso.qa.bitwarden.pw",
"internalPortal": "https://portal.qa.bitwarden.pw"
"internalSso": "https://sso.qa.bitwarden.pw"
},
"braintree": {
"production": false

View File

@ -7,14 +7,12 @@
"admin": null,
"notifications": null,
"sso": null,
"portal": null,
"internalNotifications": null,
"internalAdmin": null,
"internalIdentity": null,
"internalApi": null,
"internalVault": null,
"internalSso": null,
"internalPortal": null
"internalSso": null
}
}
}

View File

@ -124,7 +124,6 @@ function Docker-Compose-Volumes {
Create-Dir "logs/nginx"
Create-Dir "logs/notifications"
Create-Dir "logs/sso"
Create-Dir "logs/portal"
Create-Dir "mssql/backups"
Create-Dir "mssql/data"
}

View File

@ -133,7 +133,6 @@ function dockerComposeVolumes() {
createDir "logs/nginx"
createDir "logs/notifications"
createDir "logs/sso"
createDir "logs/portal"
createDir "mssql/backups"
createDir "mssql/data"
}

View File

@ -7,14 +7,12 @@
"admin": "http://localhost:62911",
"notifications": "http://localhost:61840",
"sso": "http://localhost:51822",
"portal": "http://localhost:52313",
"internalNotifications": "http://localhost:61840",
"internalAdmin": "http://localhost:62911",
"internalIdentity": "http://localhost:33656",
"internalApi": "http://localhost:4000",
"internalVault": "http://localhost:4001",
"internalSso": "http://localhost:51822",
"internalPortal": "http://localhost:52313"
"internalSso": "http://localhost:51822"
},
"send": {
"connectionString": "SECRET",

View File

@ -7,14 +7,12 @@
"admin": "https://admin.bitwarden.com",
"notifications": "https://notifications.bitwarden.com",
"sso": "https://sso.bitwarden.com",
"portal": "http://portal.bitwarden.com",
"internalNotifications": "https://notifications.bitwarden.com",
"internalAdmin": "https://admin.bitwarden.com",
"internalIdentity": "https://identity.bitwarden.com",
"internalApi": "https://api.bitwarden.com",
"internalVault": "https://vault.bitwarden.com",
"internalSso": "https://sso.bitwarden.com",
"internalPortal": "https://portal.bitwarden.com"
"internalSso": "https://sso.bitwarden.com"
},
"braintree": {
"production": true

View File

@ -7,14 +7,12 @@
"admin": "https://admin.qa.bitwarden.pw",
"notifications": "https://notifications.qa.bitwarden.pw",
"sso": "https://sso.qa.bitwarden.pw",
"portal": "http://portal.qa.bitwarden.pw",
"internalNotifications": "https://notifications.qa.bitwarden.pw",
"internalAdmin": "https://admin.qa.bitwarden.pw",
"internalIdentity": "https://identity.qa.bitwarden.pw",
"internalApi": "https://api.qa.bitwarden.pw",
"internalVault": "https://vault.qa.bitwarden.pw",
"internalSso": "https://sso.qa.bitwarden.pw",
"internalPortal": "https://portal.qa.bitwarden.pw"
"internalSso": "https://sso.qa.bitwarden.pw"
},
"braintree": {
"production": false

View File

@ -7,14 +7,12 @@
"admin": null,
"notifications": null,
"sso": null,
"portal": null,
"internalNotifications": null,
"internalAdmin": null,
"internalIdentity": null,
"internalApi": null,
"internalVault": null,
"internalSso": null,
"internalPortal": null
"internalSso": null
}
}
}

View File

@ -684,25 +684,6 @@ namespace Bit.Api.Controllers
await _userService.ReinstatePremiumAsync(user);
}
[HttpGet("enterprise-portal-signin-token")]
[Authorize("Web")]
public async Task<string> GetEnterprisePortalSignInToken()
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
var token = await _userService.GenerateEnterprisePortalSignInTokenAsync(user);
if (token == null)
{
throw new BadRequestException("Cannot generate sign in token.");
}
return token;
}
[HttpGet("tax")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<TaxInfoResponseModel> GetTaxInfo()

View File

@ -30,6 +30,8 @@ namespace Bit.Api.Controllers
private readonly IUserService _userService;
private readonly IPaymentService _paymentService;
private readonly ICurrentContext _currentContext;
private readonly ISsoConfigRepository _ssoConfigRepository;
private readonly ISsoConfigService _ssoConfigService;
private readonly GlobalSettings _globalSettings;
public OrganizationsController(
@ -40,6 +42,8 @@ namespace Bit.Api.Controllers
IUserService userService,
IPaymentService paymentService,
ICurrentContext currentContext,
ISsoConfigRepository ssoConfigRepository,
ISsoConfigService ssoConfigService,
GlobalSettings globalSettings)
{
_organizationRepository = organizationRepository;
@ -49,6 +53,8 @@ namespace Bit.Api.Controllers
_userService = userService;
_paymentService = paymentService;
_currentContext = currentContext;
_ssoConfigRepository = ssoConfigRepository;
_ssoConfigService = ssoConfigService;
_globalSettings = globalSettings;
}
@ -599,5 +605,53 @@ namespace Bit.Api.Controllers
var org = await _organizationService.UpdateOrganizationKeysAsync(new Guid(id), model.PublicKey, model.EncryptedPrivateKey);
return new OrganizationKeysResponseModel(org);
}
[HttpGet("{id:guid}/sso")]
public async Task<OrganizationSsoResponseModel> GetSso(Guid id)
{
if (!await _currentContext.ManageSso(id))
{
throw new NotFoundException();
}
var organization = await _organizationRepository.GetByIdAsync(id);
if (organization == null)
{
throw new NotFoundException();
}
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(id);
return new OrganizationSsoResponseModel(organization, _globalSettings, ssoConfig);
}
[HttpPost("{id:guid}/sso")]
public async Task<OrganizationSsoResponseModel> PostSso(Guid id, [FromBody]OrganizationSsoRequestModel model)
{
if (!await _currentContext.ManageSso(id))
{
throw new NotFoundException();
}
var organization = await _organizationRepository.GetByIdAsync(id);
if (organization == null)
{
throw new NotFoundException();
}
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(id);
if (ssoConfig == null)
{
ssoConfig = model.ToSsoConfig(id);
}
else
{
ssoConfig = model.ToSsoConfig(ssoConfig);
}
await _ssoConfigService.SaveAsync(ssoConfig);
return new OrganizationSsoResponseModel(organization, _globalSettings, ssoConfig);
}
}
}

View File

@ -7,14 +7,12 @@
"admin": "http://localhost:62911",
"notifications": "http://localhost:61840",
"sso": "http://localhost:51822",
"portal": "http://localhost:52313",
"internalNotifications": "http://localhost:61840",
"internalAdmin": "http://localhost:62911",
"internalIdentity": "http://localhost:33656",
"internalApi": "http://localhost:4000",
"internalVault": "http://localhost:4001",
"internalSso": "http://localhost:51822",
"internalPortal": "http://localhost:52313"
"internalSso": "http://localhost:51822"
},
"attachment": {
"connectionString": "SECRET",

View File

@ -7,14 +7,12 @@
"admin": "https://admin.bitwarden.com",
"notifications": "https://notifications.bitwarden.com",
"sso": "https://sso.bitwarden.com",
"portal": "http://portal.bitwarden.com",
"internalNotifications": "https://notifications.bitwarden.com",
"internalAdmin": "https://admin.bitwarden.com",
"internalIdentity": "https://identity.bitwarden.com",
"internalApi": "https://api.bitwarden.com",
"internalVault": "https://vault.bitwarden.com",
"internalSso": "https://sso.bitwarden.com",
"internalPortal": "https://portal.bitwarden.com"
"internalSso": "https://sso.bitwarden.com"
},
"braintree": {
"production": true

View File

@ -7,14 +7,12 @@
"admin": "https://admin.qa.bitwarden.pw",
"notifications": "https://notifications.qa.bitwarden.pw",
"sso": "https://sso.qa.bitwarden.pw",
"portal": "http://portal.qa.bitwarden.pw",
"internalNotifications": "https://notifications.qa.bitwarden.pw",
"internalAdmin": "https://admin.qa.bitwarden.pw",
"internalIdentity": "https://identity.qa.bitwarden.pw",
"internalApi": "https://api.qa.bitwarden.pw",
"internalVault": "https://vault.qa.bitwarden.pw",
"internalSso": "https://sso.qa.bitwarden.pw",
"internalPortal": "https://portal.qa.bitwarden.pw"
"internalSso": "https://sso.qa.bitwarden.pw"
},
"braintree": {
"production": false

View File

@ -7,14 +7,12 @@
"admin": null,
"notifications": null,
"sso": null,
"portal": null,
"internalNotifications": null,
"internalAdmin": null,
"internalIdentity": null,
"internalApi": null,
"internalVault": null,
"internalSso": null,
"internalPortal": null
"internalSso": null
}
}
}

View File

@ -7,14 +7,12 @@
"admin": "http://localhost:62911",
"notifications": "http://localhost:61840",
"sso": "http://localhost:51822",
"portal": "http://localhost:52313",
"internalNotifications": "http://localhost:61840",
"internalAdmin": "http://localhost:62911",
"internalIdentity": "http://localhost:33656",
"internalApi": "http://localhost:4000",
"internalVault": "http://localhost:4001",
"internalSso": "http://localhost:51822",
"internalPortal": "http://localhost:52313"
"internalSso": "http://localhost:51822"
}
}
}

View File

@ -7,14 +7,12 @@
"admin": "https://admin.bitwarden.com",
"notifications": "https://notifications.bitwarden.com",
"sso": "https://sso.bitwarden.com",
"portal": "http://portal.bitwarden.com",
"internalNotifications": "https://notifications.bitwarden.com",
"internalAdmin": "https://admin.bitwarden.com",
"internalIdentity": "https://identity.bitwarden.com",
"internalApi": "https://api.bitwarden.com",
"internalVault": "https://vault.bitwarden.com",
"internalSso": "https://sso.bitwarden.com",
"internalPortal": "https://portal.bitwarden.com"
"internalSso": "https://sso.bitwarden.com"
},
"braintree": {
"production": true

View File

@ -7,14 +7,12 @@
"admin": "https://admin.qa.bitwarden.pw",
"notifications": "https://notifications.qa.bitwarden.pw",
"sso": "https://sso.qa.bitwarden.pw",
"portal": "http://portal.qa.bitwarden.pw",
"internalNotifications": "https://notifications.qa.bitwarden.pw",
"internalAdmin": "https://admin.qa.bitwarden.pw",
"internalIdentity": "https://identity.qa.bitwarden.pw",
"internalApi": "https://api.qa.bitwarden.pw",
"internalVault": "https://vault.qa.bitwarden.pw",
"internalSso": "https://sso.qa.bitwarden.pw",
"internalPortal": "https://portal.qa.bitwarden.pw"
"internalSso": "https://sso.qa.bitwarden.pw"
},
"braintree": {
"production": false

View File

@ -10,9 +10,7 @@ using System.Security.Claims;
using Bit.Core.Enums.Provider;
using Bit.Core.Utilities;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Settings;
using Microsoft.EntityFrameworkCore.Internal;
namespace Bit.Core.Context
{
@ -274,13 +272,6 @@ namespace Bit.Core.Context
return Task.FromResult(Organizations?.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Custom) ?? false);
}
public async Task<bool> AccessBusinessPortal(Guid orgId)
{
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.AccessBusinessPortal ?? false)) ?? false);
}
public async Task<bool> AccessEventLogs(Guid orgId)
{
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
@ -456,7 +447,6 @@ namespace Bit.Core.Context
return new Permissions
{
AccessBusinessPortal = hasClaim("accessbusinessportal"),
AccessEventLogs = hasClaim("accesseventlogs"),
AccessImportExport = hasClaim("accessimportexport"),
AccessReports = hasClaim("accessreports"),

View File

@ -36,7 +36,6 @@ namespace Bit.Core.Context
Task<bool> OrganizationAdmin(Guid orgId);
Task<bool> OrganizationOwner(Guid orgId);
Task<bool> OrganizationCustom(Guid orgId);
Task<bool> AccessBusinessPortal(Guid orgId);
Task<bool> AccessEventLogs(Guid orgId);
Task<bool> AccessImportExport(Guid orgId);
Task<bool> AccessReports(Guid orgId);

View File

@ -5,34 +5,53 @@ using Bit.Core.Services;
using Bit.Core.Models.Data;
using Bit.Core.Enums;
using Bit.Core.Sso;
using Bit.Core.Settings;
using U2F.Core.Utils;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using System.Text.RegularExpressions;
using Bit.Core.Models.Table;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
namespace Bit.Portal.Models
namespace Bit.Core.Models.Api
{
public class SsoConfigDataViewModel : IValidatableObject
public class OrganizationSsoRequestModel
{
public SsoConfigDataViewModel() { }
[Required]
public bool Enabled { get; set; }
[Required]
public SsoConfigurationDataRequest Data { get; set; }
public SsoConfigDataViewModel(SsoConfigurationData configurationData, GlobalSettings globalSettings,
Guid organizationId)
public SsoConfig ToSsoConfig(Guid organizationId)
{
return ToSsoConfig(new SsoConfig { OrganizationId = organizationId });
}
public SsoConfig ToSsoConfig(SsoConfig existingConfig)
{
existingConfig.Enabled = Enabled;
var configurationData = Data.ToConfigurationData();
existingConfig.Data = JsonSerializer.Serialize(configurationData, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
return existingConfig;
}
}
public class SsoConfigurationDataRequest : IValidatableObject
{
public SsoConfigurationDataRequest() {}
public SsoConfigurationDataRequest(SsoConfigurationData configurationData)
{
ConfigType = configurationData.ConfigType;
Authority = configurationData.Authority;
ClientId = configurationData.ClientId;
ClientSecret = configurationData.ClientSecret;
CallbackPath = configurationData.BuildCallbackPath(globalSettings.BaseServiceUri.Sso);
SignedOutCallbackPath = configurationData.BuildSignedOutCallbackPath(globalSettings.BaseServiceUri.Sso);
MetadataAddress = configurationData.MetadataAddress;
RedirectBehavior = configurationData.RedirectBehavior;
GetClaimsFromUserInfoEndpoint = configurationData.GetClaimsFromUserInfoEndpoint;
SpEntityId = configurationData.BuildSaml2ModulePath(globalSettings.BaseServiceUri.Sso);
SpMetadataUrl = configurationData.BuildSaml2MetadataUrl(globalSettings.BaseServiceUri.Sso, organizationId.ToString());
SpAcsUrl = configurationData.BuildSaml2AcsUrl(globalSettings.BaseServiceUri.Sso, organizationId.ToString());
IdpEntityId = configurationData.IdpEntityId;
IdpBindingType = configurationData.IdpBindingType;
IdpSingleSignOnServiceUrl = configurationData.IdpSingleSignOnServiceUrl;
@ -58,148 +77,108 @@ namespace Bit.Portal.Models
}
[Required]
[Display(Name = "ConfigType")]
public SsoType ConfigType { get; set; }
// OIDC
[Display(Name = "Authority")]
public string Authority { get; set; }
[Display(Name = "ClientId")]
public string ClientId { get; set; }
[Display(Name = "ClientSecret")]
public string ClientSecret { get; set; }
[Display(Name = "CallbackPath")]
public string CallbackPath { get; set; }
[Display(Name = "SignedOutCallbackPath")]
public string SignedOutCallbackPath { get; set; }
[Display(Name = "MetadataAddress")]
public string MetadataAddress { get; set; }
[Display(Name = "RedirectBehavior")]
public OpenIdConnectRedirectBehavior RedirectBehavior { get; set; }
[Display(Name = "GetClaimsFromUserInfoEndpoint")]
public bool GetClaimsFromUserInfoEndpoint { get; set; }
[Display(Name = "AdditionalScopes")]
public string AdditionalScopes { get; set; }
[Display(Name = "AdditionalUserIdClaimTypes")]
public string AdditionalUserIdClaimTypes { get; set; }
[Display(Name = "AdditionalEmailClaimTypes")]
public string AdditionalEmailClaimTypes { get; set; }
[Display(Name = "AdditionalNameClaimTypes")]
public string AdditionalNameClaimTypes { get; set; }
[Display(Name = "AcrValues")]
public string AcrValues { get; set; }
[Display(Name = "ExpectedReturnAcrValue")]
public string ExpectedReturnAcrValue { get; set; }
// SAML2 SP
[Display(Name = "SpEntityId")]
public string SpEntityId { get; set; }
[Display(Name = "SpMetadataUrl")]
public string SpMetadataUrl { get; set; }
[Display(Name = "SpAcsUrl")]
public string SpAcsUrl { get; set; }
[Display(Name = "NameIdFormat")]
public Saml2NameIdFormat SpNameIdFormat { get; set; }
[Display(Name = "OutboundSigningAlgorithm")]
public string SpOutboundSigningAlgorithm { get; set; }
[Display(Name = "SigningBehavior")]
public Saml2SigningBehavior SpSigningBehavior { get; set; }
[Display(Name = "SpWantAssertionsSigned")]
public bool SpWantAssertionsSigned { get; set; }
[Display(Name = "SpValidateCertificates")]
public bool SpValidateCertificates { get; set; }
[Display(Name = "MinIncomingSigningAlgorithm")]
public string SpMinIncomingSigningAlgorithm { get; set; }
// SAML2 IDP
[Display(Name = "EntityId")]
public string IdpEntityId { get; set; }
[Display(Name = "BindingType")]
public Saml2BindingType IdpBindingType { get; set; }
[Display(Name = "SingleSignOnServiceUrl")]
public string IdpSingleSignOnServiceUrl { get; set; }
[Display(Name = "SingleLogoutServiceUrl")]
public string IdpSingleLogoutServiceUrl { get; set; }
[Display(Name = "ArtifactResolutionServiceUrl")]
public string IdpArtifactResolutionServiceUrl { get; set; }
[Display(Name = "X509PublicCert")]
public string IdpX509PublicCert { get; set; }
[Display(Name = "OutboundSigningAlgorithm")]
public string IdpOutboundSigningAlgorithm { get; set; }
[Display(Name = "AllowUnsolicitedAuthnResponse")]
public bool IdpAllowUnsolicitedAuthnResponse { get; set; }
[Display(Name = "DisableOutboundLogoutRequests")]
public bool IdpDisableOutboundLogoutRequests { get; set; }
[Display(Name = "WantAuthnRequestsSigned")]
public bool IdpWantAuthnRequestsSigned { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
var i18nService = context.GetService(typeof(II18nService)) as I18nService;
var model = context.ObjectInstance as SsoConfigDataViewModel;
if (model.ConfigType == SsoType.OpenIdConnect)
if (ConfigType == SsoType.OpenIdConnect)
{
if (string.IsNullOrWhiteSpace(model.Authority))
if (string.IsNullOrWhiteSpace(Authority))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("AuthorityValidationError"),
new[] { nameof(model.Authority) });
new[] { nameof(Authority) });
}
if (string.IsNullOrWhiteSpace(model.ClientId))
if (string.IsNullOrWhiteSpace(ClientId))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("ClientIdValidationError"),
new[] { nameof(model.ClientId) });
new[] { nameof(ClientId) });
}
if (string.IsNullOrWhiteSpace(model.ClientSecret))
if (string.IsNullOrWhiteSpace(ClientSecret))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("ClientSecretValidationError"),
new[] { nameof(model.ClientSecret) });
new[] { nameof(ClientSecret) });
}
}
else if (model.ConfigType == SsoType.Saml2)
else if (ConfigType == SsoType.Saml2)
{
if (string.IsNullOrWhiteSpace(model.IdpEntityId))
if (string.IsNullOrWhiteSpace(IdpEntityId))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpEntityIdValidationError"),
new[] { nameof(model.IdpEntityId) });
new[] { nameof(IdpEntityId) });
}
if (model.IdpBindingType == Saml2BindingType.Artifact && string.IsNullOrWhiteSpace(model.IdpArtifactResolutionServiceUrl))
if (IdpBindingType == Saml2BindingType.Artifact && string.IsNullOrWhiteSpace(IdpArtifactResolutionServiceUrl))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("Saml2BindingTypeValidationError"),
new[] { nameof(model.IdpArtifactResolutionServiceUrl) });
new[] { nameof(IdpArtifactResolutionServiceUrl) });
}
if (!Uri.IsWellFormedUriString(model.IdpEntityId, UriKind.Absolute) && string.IsNullOrWhiteSpace(model.IdpSingleSignOnServiceUrl))
if (!Uri.IsWellFormedUriString(IdpEntityId, UriKind.Absolute) && string.IsNullOrWhiteSpace(IdpSingleSignOnServiceUrl))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpSingleSignOnServiceUrlValidationError"),
new[] { nameof(model.IdpSingleSignOnServiceUrl) });
new[] { nameof(IdpSingleSignOnServiceUrl) });
}
if (!string.IsNullOrWhiteSpace(model.IdpX509PublicCert))
if (!string.IsNullOrWhiteSpace(IdpX509PublicCert))
{
// Validate the certificate is in a valid format
ValidationResult failedResult = null;
try
{
var certData = StripPemCertificateElements(model.IdpX509PublicCert).Base64StringToByteArray();
var certData = StripPemCertificateElements(IdpX509PublicCert).Base64StringToByteArray();
new X509Certificate2(certData);
}
catch (FormatException)
{
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertInvalidFormatValidationError"),
new[] { nameof(model.IdpX509PublicCert) });
new[] { nameof(IdpX509PublicCert) });
}
catch (CryptographicException cryptoEx)
{
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertCryptographicExceptionValidationError", cryptoEx.Message),
new[] { nameof(model.IdpX509PublicCert) });
new[] { nameof(IdpX509PublicCert) });
}
catch (Exception ex)
{
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertValidationError", ex.Message),
new[] { nameof(model.IdpX509PublicCert) });
new[] { nameof(IdpX509PublicCert) });
}
if (failedResult != null)
{

View File

@ -0,0 +1,51 @@
using System.Text.Json;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
using Bit.Core.Settings;
namespace Bit.Core.Models.Api
{
public class OrganizationSsoResponseModel : ResponseModel
{
public OrganizationSsoResponseModel(Organization organization, GlobalSettings globalSettings,
SsoConfig config = null) : base("organizationSso")
{
if (config != null)
{
Enabled = config.Enabled;
Data = JsonSerializer.Deserialize<SsoConfigurationData>(config.Data, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
}
else
{
Data = new SsoConfigurationData();
}
Urls = new SsoUrls(organization.Id.ToString(), Data, globalSettings);
}
public bool Enabled { get; set; }
public SsoConfigurationData Data { get; set; }
public SsoUrls Urls { get; set; }
}
public class SsoUrls
{
public SsoUrls(string organizationId, SsoConfigurationData configurationData, GlobalSettings globalSettings)
{
CallbackPath = configurationData.BuildCallbackPath(globalSettings.BaseServiceUri.Sso);
SignedOutCallbackPath = configurationData.BuildSignedOutCallbackPath(globalSettings.BaseServiceUri.Sso);
SpEntityId = configurationData.BuildSaml2ModulePath(globalSettings.BaseServiceUri.Sso);
SpMetadataUrl = configurationData.BuildSaml2MetadataUrl(globalSettings.BaseServiceUri.Sso, organizationId);
SpAcsUrl = configurationData.BuildSaml2AcsUrl(globalSettings.BaseServiceUri.Sso, organizationId);
}
public string CallbackPath { get; set; }
public string SignedOutCallbackPath { get; set; }
public string SpEntityId { get; set; }
public string SpMetadataUrl { get; set; }
public string SpAcsUrl { get; set; }
}
}

View File

@ -51,7 +51,6 @@ namespace Bit.Core.Models.Api
public bool Use2fa { get; set; }
public bool UseApi { get; set; }
public bool UseResetPassword { get; set; }
public bool UseBusinessPortal => UsePolicies || UseSso; // TODO add events if needed
public bool UsersGetPremium { get; set; }
public bool SelfHost { get; set; }
public int? Seats { get; set; }

View File

@ -16,7 +16,6 @@ namespace Bit.Core.Models.Data
public bool Use2fa { get; set; }
public bool UseApi{ get; set; }
public bool UseResetPassword { get; set; }
public bool UseBusinessPortal => UsePolicies || UseSso;
public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; }
public int? Seats { get; set; }

View File

@ -6,7 +6,6 @@ namespace Bit.Core.Models.Data
{
public class Permissions
{
public bool AccessBusinessPortal { get; set; }
public bool AccessEventLogs { get; set; }
public bool AccessImportExport { get; set; }
public bool AccessReports { get; set; }
@ -29,7 +28,6 @@ namespace Bit.Core.Models.Data
[System.Text.Json.Serialization.JsonIgnore]
public List<(bool Permission, string ClaimName)> ClaimsMap => new()
{
(AccessBusinessPortal, "accessbusinessportal"),
(AccessEventLogs, "accesseventlogs"),
(AccessImportExport, "accessimportexport"),
(AccessReports, "accessreports"),

View File

@ -17,7 +17,6 @@ namespace Bit.Core.Models.Data
public bool Use2fa { get; set; }
public bool UseApi{ get; set; }
public bool UseResetPassword { get; set; }
public bool UseBusinessPortal => UsePolicies || UseSso;
public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; }
public int? Seats { get; set; }

View File

@ -457,9 +457,6 @@
<data name="Never">
<value>Never</value>
</data>
<data name="WelcomeToBusinessPortal">
<value>Welcome to the Bitwarden Business Portal</value>
</data>
<data name="IdpX509PublicCertValidationError" xml:space="preserve">
<value>The IdP public certificate provided is invalid: {0}</value>
</data>
@ -642,9 +639,6 @@
<value>Expected "acr" Claim Value In Response (acr validation)</value>
<comment>'acr' is an explicit OIDC claim type, see https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.2 (acr). It should not be translated.</comment>
</data>
<data name="LoggedOutMessage" xml:space="preserve">
<value>You have been logged out of the Bitwarden Business Portal.</value>
</data>
<data name="AccessDeniedError" xml:space="preserve">
<value>Access Denied to this resource.</value>
</data>

View File

@ -70,7 +70,6 @@ namespace Bit.Core.Services
Task<bool> CanAccessPremium(ITwoFactorProvidersUser user);
Task<bool> TwoFactorIsEnabledAsync(ITwoFactorProvidersUser user);
Task<bool> TwoFactorProviderIsEnabledAsync(TwoFactorProviderType provider, ITwoFactorProvidersUser user);
Task<string> GenerateEnterprisePortalSignInTokenAsync(User user);
Task<string> GenerateSignInTokenAsync(User user, string purpose);
Task RotateApiKeyAsync(User user);
string GetUserName(ClaimsPrincipal principal);

View File

@ -12,8 +12,6 @@ using System.Security.Claims;
using Bit.Core.Models;
using Bit.Core.Models.Business;
using U2fLib = U2F.Core.Crypto.U2F;
using U2F.Core.Models;
using U2F.Core.Utils;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
@ -1222,15 +1220,6 @@ namespace Bit.Core.Services
return await CanAccessPremium(user);
}
//TODO refactor this to use the below method and enum
public async Task<string> GenerateEnterprisePortalSignInTokenAsync(User user)
{
var token = await GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
"EnterprisePortalTokenSignIn");
return token;
}
public async Task<string> GenerateSignInTokenAsync(User user, string purpose)
{
var token = await GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,

View File

@ -117,14 +117,12 @@ namespace Bit.Core.Settings
private string _admin;
private string _notifications;
private string _sso;
private string _portal;
private string _internalApi;
private string _internalIdentity;
private string _internalAdmin;
private string _internalNotifications;
private string _internalSso;
private string _internalVault;
private string _internalPortal;
public BaseServiceUriSettings(GlobalSettings globalSettings)
{
@ -159,11 +157,6 @@ namespace Bit.Core.Settings
get => _globalSettings.BuildExternalUri(_sso, "sso");
set => _sso = value;
}
public string Portal
{
get => _globalSettings.BuildExternalUri(_portal, "portal");
set => _portal = value;
}
public string InternalNotifications
{
@ -195,11 +188,6 @@ namespace Bit.Core.Settings
get => _globalSettings.BuildInternalUri(_internalSso, "sso");
set => _internalSso = value;
}
public string InternalPortal
{
get => _globalSettings.BuildInternalUri(_internalPortal, "portal");
set => _internalPortal = value;
}
}
public class SqlSettings

View File

@ -7,14 +7,12 @@
"admin": "http://localhost:62911",
"notifications": "http://localhost:61840",
"sso": "http://localhost:51822",
"portal": "http://localhost:52313",
"internalNotifications": "http://localhost:61840",
"internalAdmin": "http://localhost:62911",
"internalIdentity": "http://localhost:33656",
"internalApi": "http://localhost:4000",
"internalVault": "http://localhost:4001",
"internalSso": "http://localhost:51822",
"internalPortal": "http://localhost:52313"
"internalSso": "http://localhost:51822"
}
}
}

View File

@ -7,14 +7,12 @@
"admin": "https://admin.bitwarden.com",
"notifications": "https://notifications.bitwarden.com",
"sso": "https://sso.bitwarden.com",
"portal": "http://portal.bitwarden.com",
"internalNotifications": "https://notifications.bitwarden.com",
"internalAdmin": "https://admin.bitwarden.com",
"internalIdentity": "https://identity.bitwarden.com",
"internalApi": "https://api.bitwarden.com",
"internalVault": "https://vault.bitwarden.com",
"internalSso": "https://sso.bitwarden.com",
"internalPortal": "https://portal.bitwarden.com"
"internalSso": "https://sso.bitwarden.com"
}
},
"Logging": {

View File

@ -7,14 +7,12 @@
"admin": "https://admin.qa.bitwarden.pw",
"notifications": "https://notifications.qa.bitwarden.pw",
"sso": "https://sso.qa.bitwarden.pw",
"portal": "http://portal.qa.bitwarden.pw",
"internalNotifications": "https://notifications.qa.bitwarden.pw",
"internalAdmin": "https://admin.qa.bitwarden.pw",
"internalIdentity": "https://identity.qa.bitwarden.pw",
"internalApi": "https://api.qa.bitwarden.pw",
"internalVault": "https://vault.qa.bitwarden.pw",
"internalSso": "https://sso.qa.bitwarden.pw",
"internalPortal": "https://portal.qa.bitwarden.pw"
"internalSso": "https://sso.qa.bitwarden.pw"
}
},
"Logging": {

View File

@ -7,14 +7,12 @@
"admin": null,
"notifications": null,
"sso": null,
"portal": null,
"internalNotifications": null,
"internalAdmin": null,
"internalIdentity": null,
"internalApi": null,
"internalVault": null,
"internalSso": null,
"internalPortal": null
"internalSso": null
}
}
}

View File

@ -7,14 +7,12 @@
"admin": "http://localhost:62911",
"notifications": "http://localhost:61840",
"sso": "http://localhost:51822",
"portal": "http://localhost:52313",
"internalNotifications": "http://localhost:61840",
"internalAdmin": "http://localhost:62911",
"internalIdentity": "http://localhost:33656",
"internalApi": "http://localhost:4000",
"internalVault": "http://localhost:4001",
"internalSso": "http://localhost:51822",
"internalPortal": "http://localhost:52313"
"internalSso": "http://localhost:51822"
}
}
}

View File

@ -7,14 +7,12 @@
"admin": "https://admin.bitwarden.com",
"notifications": "https://notifications.bitwarden.com",
"sso": "https://sso.bitwarden.com",
"portal": "http://portal.bitwarden.com",
"internalNotifications": "https://notifications.bitwarden.com",
"internalAdmin": "https://admin.bitwarden.com",
"internalIdentity": "https://identity.bitwarden.com",
"internalApi": "https://api.bitwarden.com",
"internalVault": "https://vault.bitwarden.com",
"internalSso": "https://sso.bitwarden.com",
"internalPortal": "https://portal.bitwarden.com"
"internalSso": "https://sso.bitwarden.com"
},
"braintree": {
"production": true

View File

@ -7,14 +7,12 @@
"admin": "https://admin.qa.bitwarden.pw",
"notifications": "https://notifications.qa.bitwarden.pw",
"sso": "https://sso.qa.bitwarden.pw",
"portal": "http://portal.qa.bitwarden.pw",
"internalNotifications": "https://notifications.qa.bitwarden.pw",
"internalAdmin": "https://admin.qa.bitwarden.pw",
"internalIdentity": "https://identity.qa.bitwarden.pw",
"internalApi": "https://api.qa.bitwarden.pw",
"internalVault": "https://vault.qa.bitwarden.pw",
"internalSso": "https://sso.qa.bitwarden.pw",
"internalPortal": "https://portal.qa.bitwarden.pw"
"internalSso": "https://sso.qa.bitwarden.pw"
},
"braintree": {
"production": false

View File

@ -7,14 +7,12 @@
"admin": null,
"notifications": null,
"sso": null,
"portal": null,
"internalNotifications": null,
"internalAdmin": null,
"internalIdentity": null,
"internalApi": null,
"internalVault": null,
"internalSso": null,
"internalPortal": null
"internalSso": null
}
}
}

View File

@ -7,14 +7,12 @@
"admin": "http://localhost:62911",
"notifications": "http://localhost:61840",
"sso": "http://localhost:51822",
"portal": "http://localhost:52313",
"internalNotifications": "http://localhost:61840",
"internalAdmin": "http://localhost:62911",
"internalIdentity": "http://localhost:33656",
"internalApi": "http://localhost:4000",
"internalVault": "http://localhost:4001",
"internalSso": "http://localhost:51822",
"internalPortal": "http://localhost:52313"
"internalSso": "http://localhost:51822"
}
}
}

Some files were not shown because too many files have changed in this diff Show More