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:
parent
fccfce1048
commit
79447b6671
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@ -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
|
||||
|
1
.github/workflows/qa-deploy.yml
vendored
1
.github/workflows/qa-deploy.yml
vendored
@ -75,7 +75,6 @@ jobs:
|
||||
- name: Billing
|
||||
- name: Events
|
||||
- name: Sso
|
||||
- name: Portal
|
||||
- name: Identity
|
||||
steps:
|
||||
- name: Setup
|
||||
|
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@ -56,7 +56,6 @@ jobs:
|
||||
- name: Billing
|
||||
- name: Events
|
||||
- name: Sso
|
||||
- name: Portal
|
||||
- name: Identity
|
||||
steps:
|
||||
- name: Setup
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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"]
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.Portal.Models
|
||||
{
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
@ -1,8 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.Portal
|
||||
{
|
||||
public class PortalSettings
|
||||
{
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
[assembly: ResourceLocation("Resources")]
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
@inject Bit.Core.Services.II18nService i18nService
|
||||
@{
|
||||
ViewData["Title"] = i18nService.T("AccessDenied");
|
||||
}
|
||||
|
||||
<p>
|
||||
@i18nService.T("AccessDeniedError")
|
||||
</p>
|
@ -1,8 +0,0 @@
|
||||
@inject Bit.Core.Services.II18nService i18nService
|
||||
@{
|
||||
ViewData["Title"] = i18nService.T("LoggedOut");
|
||||
}
|
||||
|
||||
<p>
|
||||
@i18nService.T("LoggedOutMessage")
|
||||
</p>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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">
|
||||
© @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>
|
@ -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>
|
@ -1,4 +0,0 @@
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@using Bit.Portal
|
||||
@using Bit.Portal.Models
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@ -1,3 +0,0 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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": {
|
||||
|
||||
}
|
||||
}
|
@ -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
|
@ -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/."
|
@ -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
|
@ -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;
|
@ -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>
|
4835
bitwarden_license/src/Portal/package-lock.json
generated
4835
bitwarden_license/src/Portal/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
}
|
||||
}
|
@ -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 |
@ -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.
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -133,7 +133,6 @@ function dockerComposeVolumes() {
|
||||
createDir "logs/nginx"
|
||||
createDir "logs/notifications"
|
||||
createDir "logs/sso"
|
||||
createDir "logs/portal"
|
||||
createDir "mssql/backups"
|
||||
createDir "mssql/data"
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"),
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
51
src/Core/Models/Api/Response/OrganizationSsoResponseModel.cs
Normal file
51
src/Core/Models/Api/Response/OrganizationSsoResponseModel.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -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; }
|
||||
|
@ -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; }
|
||||
|
@ -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"),
|
||||
|
@ -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; }
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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": {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user