mirror of
https://github.com/bitwarden/server
synced 2025-12-26 05:03:18 +00:00
Merge branch 'main' into billing/PM-24964/msp-unable-verfy-bank-account
This commit is contained in:
13
.github/workflows/load-test.yml
vendored
Normal file
13
.github/workflows/load-test.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Test Stub
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
permissions:
|
||||
contents: read
|
||||
name: Test
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Test
|
||||
run: exit 0
|
||||
4
.github/workflows/repository-management.yml
vendored
4
.github/workflows/repository-management.yml
vendored
@@ -82,7 +82,7 @@ jobs:
|
||||
version: ${{ inputs.version_number_override }}
|
||||
|
||||
- name: Generate GH App token
|
||||
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0
|
||||
uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}
|
||||
@@ -200,7 +200,7 @@ jobs:
|
||||
uses: bitwarden/gh-actions/azure-logout@main
|
||||
|
||||
- name: Generate GH App token
|
||||
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0
|
||||
uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
@@ -416,7 +417,7 @@ public class DynamicAuthenticationSchemeProvider : AuthenticationSchemeProvider
|
||||
SPOptions = spOptions,
|
||||
SignInScheme = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme,
|
||||
SignOutScheme = IdentityServerConstants.DefaultCookieAuthenticationScheme,
|
||||
CookieManager = new IdentityServer.DistributedCacheCookieManager(),
|
||||
CookieManager = new DistributedCacheCookieManager(),
|
||||
};
|
||||
options.IdentityProviders.Add(idp);
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Api.AdminConsole.Authorization;
|
||||
|
||||
@@ -554,18 +554,12 @@ public class OrganizationsController : Controller
|
||||
[HttpPut("{id}/collection-management")]
|
||||
public async Task<OrganizationResponseModel> PutCollectionManagement(Guid id, [FromBody] OrganizationCollectionManagementUpdateRequestModel model)
|
||||
{
|
||||
var organization = await _organizationRepository.GetByIdAsync(id);
|
||||
if (organization == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (!await _currentContext.OrganizationOwner(id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _organizationService.UpdateAsync(model.ToOrganization(organization, _featureService), eventType: EventType.Organization_CollectionManagement_Updated);
|
||||
var organization = await _organizationService.UpdateCollectionManagementSettingsAsync(id, model.ToSettings());
|
||||
var plan = await _pricingClient.GetPlan(organization.PlanType);
|
||||
return new OrganizationResponseModel(organization, plan);
|
||||
}
|
||||
|
||||
@@ -78,12 +78,14 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
||||
UseRiskInsights = organization.UseRiskInsights;
|
||||
UseOrganizationDomains = organization.UseOrganizationDomains;
|
||||
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
|
||||
SsoEnabled = organization.SsoEnabled ?? false;
|
||||
|
||||
if (organization.SsoConfig != null)
|
||||
{
|
||||
var ssoConfigData = SsoConfigurationData.Deserialize(organization.SsoConfig);
|
||||
KeyConnectorEnabled = ssoConfigData.MemberDecryptionType == MemberDecryptionType.KeyConnector && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl);
|
||||
KeyConnectorUrl = ssoConfigData.KeyConnectorUrl;
|
||||
SsoMemberDecryptionType = ssoConfigData.MemberDecryptionType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,4 +162,6 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
||||
public bool UseOrganizationDomains { get; set; }
|
||||
public bool UseAdminSponsoredFamilies { get; set; }
|
||||
public bool IsAdminInitiated { get; set; }
|
||||
public bool SsoEnabled { get; set; }
|
||||
public MemberDecryptionType? SsoMemberDecryptionType { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
|
||||
namespace Bit.Api.Models.Request.Organizations;
|
||||
|
||||
@@ -10,12 +9,11 @@ public class OrganizationCollectionManagementUpdateRequestModel
|
||||
public bool LimitItemDeletion { get; set; }
|
||||
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
||||
|
||||
public virtual Organization ToOrganization(Organization existingOrganization, IFeatureService featureService)
|
||||
public OrganizationCollectionManagementSettings ToSettings() => new()
|
||||
{
|
||||
existingOrganization.LimitCollectionCreation = LimitCollectionCreation;
|
||||
existingOrganization.LimitCollectionDeletion = LimitCollectionDeletion;
|
||||
existingOrganization.LimitItemDeletion = LimitItemDeletion;
|
||||
existingOrganization.AllowAdminAccessToAllCollectionItems = AllowAdminAccessToAllCollectionItems;
|
||||
return existingOrganization;
|
||||
}
|
||||
LimitCollectionCreation = LimitCollectionCreation,
|
||||
LimitCollectionDeletion = LimitCollectionDeletion,
|
||||
LimitItemDeletion = LimitItemDeletion,
|
||||
AllowAdminAccessToAllCollectionItems = AllowAdminAccessToAllCollectionItems
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
using Bit.Api.Models.Response;
|
||||
using Bit.Api.SecretsManager.Models.Request;
|
||||
using Bit.Api.SecretsManager.Models.Response;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
using Bit.Api.Models.Response;
|
||||
using Bit.Api.SecretsManager.Models.Request;
|
||||
using Bit.Api.SecretsManager.Models.Response;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Bit.Api.SecretsManager.Models.Response;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.SecretsManager.Commands.Trash.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
|
||||
@@ -13,7 +13,6 @@ using Bit.Api.KeyManagement.Validators;
|
||||
using Bit.Api.Tools.Models.Request;
|
||||
using Bit.Api.Vault.Models.Request;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.SharedWeb.Health;
|
||||
using Microsoft.IdentityModel.Logging;
|
||||
using Microsoft.OpenApi.Models;
|
||||
@@ -33,6 +32,8 @@ using Bit.Core.Tools.ImportFeatures;
|
||||
using Bit.Core.Auth.Models.Api.Request;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Tools.SendFeatures;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
|
||||
|
||||
#if !OSS
|
||||
using Bit.Commercial.Core.SecretsManager;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using Bit.Api.Tools.Authorization;
|
||||
using Bit.Api.Vault.AuthorizationHandlers.Collections;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Authorization;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.PhishingDomainFeatures;
|
||||
using Bit.Core.PhishingDomainFeatures.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
@@ -152,6 +152,12 @@ public class FreshdeskController : Controller
|
||||
return new BadRequestResult();
|
||||
}
|
||||
|
||||
// if there is no description, then we don't send anything to onyx
|
||||
if (string.IsNullOrEmpty(model.TicketDescriptionText.Trim()))
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// create the onyx `answer-with-citation` request
|
||||
var onyxRequestModel = new OnyxAnswerWithCitationRequestModel(model.TicketDescriptionText, _billingSettings.Onyx.PersonaId);
|
||||
var onyxRequest = new HttpRequestMessage(HttpMethod.Post,
|
||||
@@ -164,9 +170,12 @@ public class FreshdeskController : Controller
|
||||
// the CallOnyxApi will return a null if we have an error response
|
||||
if (onyxJsonResponse?.Answer == null || !string.IsNullOrEmpty(onyxJsonResponse?.ErrorMsg))
|
||||
{
|
||||
return BadRequest(
|
||||
string.Format("Failed to get a valid response from Onyx API. Response: {0}",
|
||||
JsonSerializer.Serialize(onyxJsonResponse ?? new OnyxAnswerWithCitationResponseModel())));
|
||||
_logger.LogWarning("Error getting answer from Onyx AI. Freshdesk model: {model}\r\n Onyx query {query}\r\nresponse: {response}. ",
|
||||
JsonSerializer.Serialize(model),
|
||||
JsonSerializer.Serialize(onyxRequestModel),
|
||||
JsonSerializer.Serialize(onyxJsonResponse));
|
||||
|
||||
return Ok(); // return ok so we don't retry
|
||||
}
|
||||
|
||||
// add the answer as a note to the ticket
|
||||
|
||||
@@ -70,7 +70,16 @@ public enum EventType : int
|
||||
Organization_EnabledKeyConnector = 1606,
|
||||
Organization_DisabledKeyConnector = 1607,
|
||||
Organization_SponsorshipsSynced = 1608,
|
||||
Organization_CollectionManagement_Updated = 1609,
|
||||
[Obsolete("Use other specific Organization_CollectionManagement events instead")]
|
||||
Organization_CollectionManagement_Updated = 1609, // TODO: Will be removed in PM-25315
|
||||
Organization_CollectionManagement_LimitCollectionCreationEnabled = 1610,
|
||||
Organization_CollectionManagement_LimitCollectionCreationDisabled = 1611,
|
||||
Organization_CollectionManagement_LimitCollectionDeletionEnabled = 1612,
|
||||
Organization_CollectionManagement_LimitCollectionDeletionDisabled = 1613,
|
||||
Organization_CollectionManagement_LimitItemDeletionEnabled = 1614,
|
||||
Organization_CollectionManagement_LimitItemDeletionDisabled = 1615,
|
||||
Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled = 1616,
|
||||
Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsDisabled = 1617,
|
||||
|
||||
Policy_Updated = 1700,
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Bit.Core.AdminConsole.Models.Business;
|
||||
|
||||
public record OrganizationCollectionManagementSettings
|
||||
{
|
||||
public bool LimitCollectionCreation { get; set; }
|
||||
public bool LimitCollectionDeletion { get; set; }
|
||||
public bool LimitItemDeletion { get; set; }
|
||||
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
||||
}
|
||||
@@ -49,6 +49,7 @@ public class OrganizationUserOrganizationDetails
|
||||
public string ProviderName { get; set; }
|
||||
public ProviderType? ProviderType { get; set; }
|
||||
public string FamilySponsorshipFriendlyName { get; set; }
|
||||
public bool? SsoEnabled { get; set; }
|
||||
public string SsoConfig { get; set; }
|
||||
public DateTime? FamilySponsorshipLastSyncDate { get; set; }
|
||||
public DateTime? FamilySponsorshipValidUntil { get; set; }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Auth.Identity;
|
||||
|
||||
namespace Bit.Core.Models.Data;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#nullable disable
|
||||
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
@@ -19,7 +20,8 @@ public interface IOrganizationService
|
||||
Task<string> AdjustSeatsAsync(Guid organizationId, int seatAdjustment);
|
||||
Task VerifyBankAsync(Guid organizationId, int amount1, int amount2);
|
||||
Task UpdateExpirationDateAsync(Guid organizationId, DateTime? expirationDate);
|
||||
Task UpdateAsync(Organization organization, bool updateBilling = false, EventType eventType = EventType.Organization_Updated);
|
||||
Task UpdateAsync(Organization organization, bool updateBilling = false);
|
||||
Task<Organization> UpdateCollectionManagementSettingsAsync(Guid organizationId, OrganizationCollectionManagementSettings settings);
|
||||
Task UpdateTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type);
|
||||
Task DisableTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type);
|
||||
Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId, EventSystemUser? systemUser,
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Text.Json;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
@@ -378,8 +379,7 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Organization organization, bool updateBilling = false,
|
||||
EventType eventType = EventType.Organization_Updated)
|
||||
public async Task UpdateAsync(Organization organization, bool updateBilling = false)
|
||||
{
|
||||
if (organization.Id == default(Guid))
|
||||
{
|
||||
@@ -395,7 +395,7 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
}
|
||||
|
||||
await ReplaceAndUpdateCacheAsync(organization, eventType);
|
||||
await ReplaceAndUpdateCacheAsync(organization, EventType.Organization_Updated);
|
||||
|
||||
if (updateBilling && !string.IsNullOrWhiteSpace(organization.GatewayCustomerId))
|
||||
{
|
||||
@@ -420,11 +420,35 @@ public class OrganizationService : IOrganizationService
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (eventType == EventType.Organization_CollectionManagement_Updated)
|
||||
public async Task<Organization> UpdateCollectionManagementSettingsAsync(Guid organizationId, OrganizationCollectionManagementSettings settings)
|
||||
{
|
||||
var existingOrganization = await _organizationRepository.GetByIdAsync(organizationId);
|
||||
if (existingOrganization == null)
|
||||
{
|
||||
await _pushNotificationService.PushSyncOrganizationCollectionManagementSettingsAsync(organization);
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// Create logging actions based on what will change
|
||||
var loggingActions = CreateCollectionManagementLoggingActions(existingOrganization, settings);
|
||||
|
||||
existingOrganization.LimitCollectionCreation = settings.LimitCollectionCreation;
|
||||
existingOrganization.LimitCollectionDeletion = settings.LimitCollectionDeletion;
|
||||
existingOrganization.LimitItemDeletion = settings.LimitItemDeletion;
|
||||
existingOrganization.AllowAdminAccessToAllCollectionItems = settings.AllowAdminAccessToAllCollectionItems;
|
||||
existingOrganization.RevisionDate = DateTime.UtcNow;
|
||||
|
||||
await ReplaceAndUpdateCacheAsync(existingOrganization);
|
||||
|
||||
if (loggingActions.Any())
|
||||
{
|
||||
await Task.WhenAll(loggingActions.Select(action => action()));
|
||||
}
|
||||
|
||||
await _pushNotificationService.PushSyncOrganizationCollectionManagementSettingsAsync(existingOrganization);
|
||||
|
||||
return existingOrganization;
|
||||
}
|
||||
|
||||
public async Task UpdateTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type)
|
||||
@@ -1214,4 +1238,44 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private List<Func<Task>> CreateCollectionManagementLoggingActions(
|
||||
Organization existingOrganization, OrganizationCollectionManagementSettings settings)
|
||||
{
|
||||
var loggingActions = new List<Func<Task>>();
|
||||
|
||||
if (existingOrganization.LimitCollectionCreation != settings.LimitCollectionCreation)
|
||||
{
|
||||
var eventType = settings.LimitCollectionCreation
|
||||
? EventType.Organization_CollectionManagement_LimitCollectionCreationEnabled
|
||||
: EventType.Organization_CollectionManagement_LimitCollectionCreationDisabled;
|
||||
loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType));
|
||||
}
|
||||
|
||||
if (existingOrganization.LimitCollectionDeletion != settings.LimitCollectionDeletion)
|
||||
{
|
||||
var eventType = settings.LimitCollectionDeletion
|
||||
? EventType.Organization_CollectionManagement_LimitCollectionDeletionEnabled
|
||||
: EventType.Organization_CollectionManagement_LimitCollectionDeletionDisabled;
|
||||
loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType));
|
||||
}
|
||||
|
||||
if (existingOrganization.LimitItemDeletion != settings.LimitItemDeletion)
|
||||
{
|
||||
var eventType = settings.LimitItemDeletion
|
||||
? EventType.Organization_CollectionManagement_LimitItemDeletionEnabled
|
||||
: EventType.Organization_CollectionManagement_LimitItemDeletionDisabled;
|
||||
loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType));
|
||||
}
|
||||
|
||||
if (existingOrganization.AllowAdminAccessToAllCollectionItems != settings.AllowAdminAccessToAllCollectionItems)
|
||||
{
|
||||
var eventType = settings.AllowAdminAccessToAllCollectionItems
|
||||
? EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled
|
||||
: EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsDisabled;
|
||||
loggingActions.Add(() => _eventService.LogOrganizationEventAsync(existingOrganization, eventType));
|
||||
}
|
||||
|
||||
return loggingActions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Bit.Core.Identity;
|
||||
namespace Bit.Core.Auth.Identity;
|
||||
|
||||
public static class Claims
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Bit.Core.Identity;
|
||||
namespace Bit.Core.Auth.Identity;
|
||||
|
||||
public enum IdentityClientType : byte
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using Duende.IdentityServer.Models;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Core.Auth.IdentityServer;
|
||||
|
||||
public static class ApiScopes
|
||||
{
|
||||
@@ -8,7 +8,7 @@ using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Core.Auth.IdentityServer;
|
||||
|
||||
public class ConfigureOpenIdConnectDistributedOptions : IPostConfigureOptions<CookieAuthenticationOptions>
|
||||
{
|
||||
@@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Core.Auth.IdentityServer;
|
||||
|
||||
public class DistributedCacheCookieManager : ICookieManager
|
||||
{
|
||||
@@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Core.Auth.IdentityServer;
|
||||
|
||||
public class DistributedCacheTicketDataFormatter : ISecureDataFormat<AuthenticationTicket>
|
||||
{
|
||||
@@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
namespace Bit.Core.Auth.IdentityServer;
|
||||
|
||||
public class DistributedCacheTicketStore : ITicketStore
|
||||
{
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Auth.Identity;
|
||||
|
||||
namespace Bit.Core.Auth.UserFeatures.SendAccess;
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ public static class FeatureFlagKeys
|
||||
public const string CreateDefaultLocation = "pm-19467-create-default-location";
|
||||
public const string DirectoryConnectorPreventUserRemoval = "pm-24592-directory-connector-prevent-user-removal";
|
||||
public const string CipherRepositoryBulkResourceCreation = "pm-24951-cipher-repository-bulk-resource-creation-service";
|
||||
public const string CollectionVaultRefactor = "pm-25030-resolve-ts-upgrade-errors";
|
||||
|
||||
/* Auth Team */
|
||||
public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence";
|
||||
@@ -160,7 +161,6 @@ public static class FeatureFlagKeys
|
||||
public const string TrialPayment = "PM-8163-trial-payment";
|
||||
public const string PM17772_AdminInitiatedSponsorships = "pm-17772-admin-initiated-sponsorships";
|
||||
public const string UsePricingService = "use-pricing-service";
|
||||
public const string PM12276Breadcrumbing = "pm-12276-breadcrumbing-for-business-features";
|
||||
public const string PM19422_AllowAutomaticTaxUpdates = "pm-19422-allow-automatic-tax-updates";
|
||||
public const string UseOrganizationWarningsService = "use-organization-warnings-service";
|
||||
public const string PM21881_ManagePaymentDetailsOutsideCheckout = "pm-21881-manage-payment-details-outside-checkout";
|
||||
|
||||
@@ -6,10 +6,10 @@ using Bit.Core.AdminConsole.Context;
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Billing.Extensions;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.AdminConsole.Context;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Auth.Identity;
|
||||
|
||||
namespace Bit.Core.Enums;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Models.Api.Request.OrganizationSponsorships;
|
||||
using Bit.Core.Models.Api.Response.OrganizationSponsorships;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationSponsorships;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Services;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
|
||||
namespace Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using LaunchDarkly.Logging;
|
||||
|
||||
@@ -16,10 +16,10 @@ using Azure.Storage.Queues.Models;
|
||||
using Bit.Core.AdminConsole.Context;
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Settings;
|
||||
using Duende.IdentityModel;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Globalization;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Duende.IdentityModel;
|
||||
using Duende.IdentityServer.Models;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Platform.Installations;
|
||||
using Duende.IdentityModel;
|
||||
using Duende.IdentityServer.Models;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Diagnostics;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Settings;
|
||||
using Duende.IdentityModel;
|
||||
using Duende.IdentityServer.Models;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Repositories;
|
||||
using Duende.IdentityModel;
|
||||
using Duende.IdentityServer.Models;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be null safe and then delete the line below
|
||||
#nullable disable
|
||||
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Billing.Services;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Utilities;
|
||||
using Duende.IdentityModel;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Billing.Services;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -8,12 +8,12 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.Models.Api.Response;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Api.Response;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
@@ -3,11 +3,11 @@ using System.Security.Claims;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Auth.Models.Api.Response;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Platform.Installations;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Identity.IdentityServer.Enums;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.KeyManagement.Sends;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Identity.IdentityServer.Enums;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Identity.IdentityServer.Enums;
|
||||
using Duende.IdentityServer.Models;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -56,6 +56,7 @@ public class OrganizationUserOrganizationDetailsViewQuery : IQuery<OrganizationU
|
||||
ProviderId = p.Id,
|
||||
ProviderName = p.Name,
|
||||
ProviderType = p.Type,
|
||||
SsoEnabled = ss.Enabled,
|
||||
SsoConfig = ss.Data,
|
||||
FamilySponsorshipFriendlyName = os.FriendlyName,
|
||||
FamilySponsorshipLastSyncDate = os.LastSyncDate,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Globalization;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.SharedWeb.Utilities;
|
||||
|
||||
@@ -30,8 +30,6 @@ using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.HostedServices;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.KeyManagement;
|
||||
using Bit.Core.NotificationCenter;
|
||||
using Bit.Core.OrganizationFeatures;
|
||||
|
||||
@@ -37,6 +37,7 @@ SELECT
|
||||
PO.[ProviderId],
|
||||
P.[Name] ProviderName,
|
||||
P.[Type] ProviderType,
|
||||
SS.[Enabled] SsoEnabled,
|
||||
SS.[Data] SsoConfig,
|
||||
OS.[FriendlyName] FamilySponsorshipFriendlyName,
|
||||
OS.[LastSyncDate] FamilySponsorshipLastSyncDate,
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
using AutoFixture.Xunit2;
|
||||
using Bit.Api.AdminConsole.Controllers;
|
||||
using Bit.Api.Auth.Models.Request.Accounts;
|
||||
using Bit.Api.Models.Request.Organizations;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
|
||||
@@ -29,6 +31,7 @@ using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tokens;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
@@ -293,4 +296,40 @@ public class OrganizationsControllerTests : IDisposable
|
||||
|
||||
Assert.True(result.ResetPasswordEnabled);
|
||||
}
|
||||
|
||||
[Theory, AutoData]
|
||||
public async Task PutCollectionManagement_ValidRequest_Success(
|
||||
Organization organization,
|
||||
OrganizationCollectionManagementUpdateRequestModel model)
|
||||
{
|
||||
// Arrange
|
||||
_currentContext.OrganizationOwner(organization.Id).Returns(true);
|
||||
|
||||
var plan = StaticStore.GetPlan(PlanType.EnterpriseAnnually);
|
||||
_pricingClient.GetPlan(Arg.Any<PlanType>()).Returns(plan);
|
||||
|
||||
_organizationService
|
||||
.UpdateCollectionManagementSettingsAsync(
|
||||
organization.Id,
|
||||
Arg.Is<OrganizationCollectionManagementSettings>(s =>
|
||||
s.LimitCollectionCreation == model.LimitCollectionCreation &&
|
||||
s.LimitCollectionDeletion == model.LimitCollectionDeletion &&
|
||||
s.LimitItemDeletion == model.LimitItemDeletion &&
|
||||
s.AllowAdminAccessToAllCollectionItems == model.AllowAdminAccessToAllCollectionItems))
|
||||
.Returns(organization);
|
||||
|
||||
// Act
|
||||
await _sut.PutCollectionManagement(organization.Id, model);
|
||||
|
||||
// Assert
|
||||
await _organizationService
|
||||
.Received(1)
|
||||
.UpdateCollectionManagementSettingsAsync(
|
||||
organization.Id,
|
||||
Arg.Is<OrganizationCollectionManagementSettings>(s =>
|
||||
s.LimitCollectionCreation == model.LimitCollectionCreation &&
|
||||
s.LimitCollectionDeletion == model.LimitCollectionDeletion &&
|
||||
s.LimitItemDeletion == model.LimitItemDeletion &&
|
||||
s.AllowAdminAccessToAllCollectionItems == model.AllowAdminAccessToAllCollectionItems));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ReceivedExtensions;
|
||||
@@ -126,7 +127,7 @@ public class FreshdeskControllerTests
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(WebhookKey)]
|
||||
public async Task PostWebhookOnyxAi_invalid_onyx_response_results_in_BadRequest(
|
||||
public async Task PostWebhookOnyxAi_invalid_onyx_response_results_is_logged(
|
||||
string freshdeskWebhookKey, FreshdeskOnyxAiWebhookModel model,
|
||||
SutProvider<FreshdeskController> sutProvider)
|
||||
{
|
||||
@@ -150,8 +151,18 @@ public class FreshdeskControllerTests
|
||||
|
||||
var response = await sutProvider.Sut.PostWebhookOnyxAi(freshdeskWebhookKey, model);
|
||||
|
||||
var result = Assert.IsAssignableFrom<BadRequestObjectResult>(response);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode);
|
||||
var statusCodeResult = Assert.IsAssignableFrom<StatusCodeResult>(response);
|
||||
Assert.Equal(StatusCodes.Status200OK, statusCodeResult.StatusCode);
|
||||
|
||||
var _logger = sutProvider.GetDependency<ILogger<FreshdeskController>>();
|
||||
|
||||
// workaround because _logger.Received(1).LogWarning(...) does not work
|
||||
_logger.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Log" && c.GetArguments()[1].ToString().Contains("Error getting answer from Onyx AI"));
|
||||
|
||||
// sent call to Onyx API - but we got an error response
|
||||
_ = mockOnyxHttpMessageHandler.Received(1).Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>());
|
||||
// did not call freshdesk to add a note since onyx failed
|
||||
_ = mockFreshdeskHttpMessageHandler.DidNotReceive().Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -174,10 +185,9 @@ public class FreshdeskControllerTests
|
||||
.Returns(mockFreshdeskAddNoteResponse);
|
||||
var freshdeskHttpClient = new HttpClient(mockFreshdeskHttpMessageHandler);
|
||||
|
||||
|
||||
// mocking Onyx api response given a ticket description
|
||||
var mockOnyxHttpMessageHandler = Substitute.ForPartsOf<MockHttpMessageHandler>();
|
||||
onyxResponse.ErrorMsg = string.Empty;
|
||||
onyxResponse.ErrorMsg = "string.Empty";
|
||||
var mockOnyxResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(JsonSerializer.Serialize(onyxResponse))
|
||||
@@ -195,6 +205,37 @@ public class FreshdeskControllerTests
|
||||
Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(WebhookKey)]
|
||||
public async Task PostWebhookOnyxAi_ticket_description_is_empty_return_success(
|
||||
string freshdeskWebhookKey, FreshdeskOnyxAiWebhookModel model,
|
||||
SutProvider<FreshdeskController> sutProvider)
|
||||
{
|
||||
var billingSettings = sutProvider.GetDependency<IOptions<BillingSettings>>().Value;
|
||||
billingSettings.FreshDesk.WebhookKey.Returns(freshdeskWebhookKey);
|
||||
billingSettings.Onyx.BaseUrl.Returns("http://simulate-onyx-api.com/api");
|
||||
|
||||
model.TicketDescriptionText = " "; // empty description
|
||||
|
||||
// mocking freshdesk api add note request (POST)
|
||||
var mockFreshdeskHttpMessageHandler = Substitute.ForPartsOf<MockHttpMessageHandler>();
|
||||
var freshdeskHttpClient = new HttpClient(mockFreshdeskHttpMessageHandler);
|
||||
|
||||
// mocking Onyx api response given a ticket description
|
||||
var mockOnyxHttpMessageHandler = Substitute.ForPartsOf<MockHttpMessageHandler>();
|
||||
var onyxHttpClient = new HttpClient(mockOnyxHttpMessageHandler);
|
||||
|
||||
sutProvider.GetDependency<IHttpClientFactory>().CreateClient("FreshdeskApi").Returns(freshdeskHttpClient);
|
||||
sutProvider.GetDependency<IHttpClientFactory>().CreateClient("OnyxApi").Returns(onyxHttpClient);
|
||||
|
||||
var response = await sutProvider.Sut.PostWebhookOnyxAi(freshdeskWebhookKey, model);
|
||||
|
||||
var result = Assert.IsAssignableFrom<OkResult>(response);
|
||||
Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
|
||||
_ = mockFreshdeskHttpMessageHandler.DidNotReceive().Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>());
|
||||
_ = mockOnyxHttpMessageHandler.DidNotReceive().Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
public class MockHttpMessageHandler : HttpMessageHandler
|
||||
{
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
using Bit.Core.AdminConsole.Models.Business;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
@@ -27,7 +28,6 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Bit.Test.Common.Fakes;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ExceptionExtensions;
|
||||
using NSubstitute.ReceivedExtensions;
|
||||
using NSubstitute.ReturnsExtensions;
|
||||
using Stripe;
|
||||
using Xunit;
|
||||
@@ -42,8 +42,6 @@ public class OrganizationServiceTests
|
||||
{
|
||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory = new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>();
|
||||
|
||||
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize(InviteeUserType = OrganizationUserType.User,
|
||||
InvitorUserType = OrganizationUserType.Owner), OrganizationCustomize, BitAutoData]
|
||||
@@ -1229,6 +1227,109 @@ public class OrganizationServiceTests
|
||||
.GetByIdentifierAsync(Arg.Is<string>(id => id == organization.Identifier));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(false, true, false, true)]
|
||||
[BitAutoData(true, false, true, false)]
|
||||
public async Task UpdateCollectionManagementSettingsAsync_WhenSettingsChanged_LogsSpecificEvents(
|
||||
bool newLimitCollectionCreation,
|
||||
bool newLimitCollectionDeletion,
|
||||
bool newLimitItemDeletion,
|
||||
bool newAllowAdminAccessToAllCollectionItems,
|
||||
Organization existingOrganization, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
existingOrganization.LimitCollectionCreation = false;
|
||||
existingOrganization.LimitCollectionDeletion = false;
|
||||
existingOrganization.LimitItemDeletion = false;
|
||||
existingOrganization.AllowAdminAccessToAllCollectionItems = false;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(existingOrganization.Id)
|
||||
.Returns(existingOrganization);
|
||||
|
||||
var settings = new OrganizationCollectionManagementSettings
|
||||
{
|
||||
LimitCollectionCreation = newLimitCollectionCreation,
|
||||
LimitCollectionDeletion = newLimitCollectionDeletion,
|
||||
LimitItemDeletion = newLimitItemDeletion,
|
||||
AllowAdminAccessToAllCollectionItems = newAllowAdminAccessToAllCollectionItems
|
||||
};
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.UpdateCollectionManagementSettingsAsync(existingOrganization.Id, settings);
|
||||
|
||||
// Assert
|
||||
var eventService = sutProvider.GetDependency<IEventService>();
|
||||
if (newLimitCollectionCreation)
|
||||
{
|
||||
await eventService.Received(1).LogOrganizationEventAsync(
|
||||
Arg.Is<Organization>(org => org.Id == existingOrganization.Id),
|
||||
Arg.Is<EventType>(e => e == EventType.Organization_CollectionManagement_LimitCollectionCreationEnabled));
|
||||
}
|
||||
else
|
||||
{
|
||||
await eventService.DidNotReceive().LogOrganizationEventAsync(
|
||||
Arg.Is<Organization>(org => org.Id == existingOrganization.Id),
|
||||
Arg.Is<EventType>(e => e == EventType.Organization_CollectionManagement_LimitCollectionCreationEnabled));
|
||||
}
|
||||
|
||||
if (newLimitCollectionDeletion)
|
||||
{
|
||||
await eventService.Received(1).LogOrganizationEventAsync(
|
||||
Arg.Is<Organization>(org => org.Id == existingOrganization.Id),
|
||||
Arg.Is<EventType>(e => e == EventType.Organization_CollectionManagement_LimitCollectionDeletionEnabled));
|
||||
}
|
||||
else
|
||||
{
|
||||
await eventService.DidNotReceive().LogOrganizationEventAsync(
|
||||
Arg.Is<Organization>(org => org.Id == existingOrganization.Id),
|
||||
Arg.Is<EventType>(e => e == EventType.Organization_CollectionManagement_LimitCollectionDeletionEnabled));
|
||||
}
|
||||
|
||||
if (newLimitItemDeletion)
|
||||
{
|
||||
await eventService.Received(1).LogOrganizationEventAsync(
|
||||
Arg.Is<Organization>(org => org.Id == existingOrganization.Id),
|
||||
Arg.Is<EventType>(e => e == EventType.Organization_CollectionManagement_LimitItemDeletionEnabled));
|
||||
}
|
||||
else
|
||||
{
|
||||
await eventService.DidNotReceive().LogOrganizationEventAsync(
|
||||
Arg.Is<Organization>(org => org.Id == existingOrganization.Id),
|
||||
Arg.Is<EventType>(e => e == EventType.Organization_CollectionManagement_LimitItemDeletionEnabled));
|
||||
}
|
||||
|
||||
if (newAllowAdminAccessToAllCollectionItems)
|
||||
{
|
||||
await eventService.Received(1).LogOrganizationEventAsync(
|
||||
Arg.Is<Organization>(org => org.Id == existingOrganization.Id),
|
||||
Arg.Is<EventType>(e => e == EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled));
|
||||
}
|
||||
else
|
||||
{
|
||||
await eventService.DidNotReceive().LogOrganizationEventAsync(
|
||||
Arg.Is<Organization>(org => org.Id == existingOrganization.Id),
|
||||
Arg.Is<EventType>(e => e == EventType.Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateCollectionManagementSettingsAsync_WhenOrganizationNotFound_ThrowsNotFoundException(
|
||||
Guid organizationId, OrganizationCollectionManagementSettings settings, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organizationId)
|
||||
.Returns((Organization)null);
|
||||
|
||||
// Act/Assert
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateCollectionManagementSettingsAsync(organizationId, settings));
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.Received(1)
|
||||
.GetByIdAsync(organizationId);
|
||||
}
|
||||
|
||||
// Must set real guids in order for dictionary of guids to not throw aggregate exceptions
|
||||
private void SetupOrgUserRepositoryCreateManyAsyncMock(IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.UserFeatures.SendAccess;
|
||||
using Bit.Core.Identity;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Auth.UserFeatures.SendAccess;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Bit.Core;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.KeyManagement.Sends;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Platform.Installations;
|
||||
using Bit.Identity.IdentityServer.ClientProviders;
|
||||
using Duende.IdentityModel;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Identity.IdentityServer.ClientProviders;
|
||||
using Duende.IdentityModel;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Specialized;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Specialized;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Specialized;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Auth.UserFeatures.SendAccess;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.KeyManagement.Sends;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Specialized;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Auth.UserFeatures.SendAccess;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.KeyManagement.Sends;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
@@ -442,7 +446,8 @@ public class OrganizationUserRepositoryTests
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task GetManyDetailsByUserAsync_Works(IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ISsoConfigRepository ssoConfigRepository)
|
||||
{
|
||||
var user1 = await userRepository.CreateAsync(new User
|
||||
{
|
||||
@@ -475,6 +480,18 @@ public class OrganizationUserRepositoryTests
|
||||
AccessSecretsManager = false
|
||||
});
|
||||
|
||||
var ssoConfigData = new SsoConfigurationData
|
||||
{
|
||||
MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption
|
||||
};
|
||||
|
||||
var ssoConfig = await ssoConfigRepository.CreateAsync(new SsoConfig
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Enabled = true,
|
||||
Data = ssoConfigData.Serialize()
|
||||
});
|
||||
|
||||
var responseModel = await organizationUserRepository.GetManyDetailsByUserAsync(user1.Id);
|
||||
|
||||
Assert.NotNull(responseModel);
|
||||
@@ -487,6 +504,8 @@ public class OrganizationUserRepositoryTests
|
||||
Assert.Equal(organization.UsePolicies, result.UsePolicies);
|
||||
Assert.Equal(organization.UseSso, result.UseSso);
|
||||
Assert.Equal(organization.UseKeyConnector, result.UseKeyConnector);
|
||||
Assert.Equal(ssoConfig.Enabled, result.SsoEnabled);
|
||||
Assert.Equal(ssoConfig.Data, result.SsoConfig);
|
||||
Assert.Equal(organization.UseScim, result.UseScim);
|
||||
Assert.Equal(organization.UseGroups, result.UseGroups);
|
||||
Assert.Equal(organization.UseDirectory, result.UseDirectory);
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
CREATE OR ALTER VIEW [dbo].[OrganizationUserOrganizationDetailsView]
|
||||
AS
|
||||
SELECT
|
||||
OU.[UserId],
|
||||
OU.[OrganizationId],
|
||||
OU.[Id] OrganizationUserId,
|
||||
O.[Name],
|
||||
O.[Enabled],
|
||||
O.[PlanType],
|
||||
O.[UsePolicies],
|
||||
O.[UseSso],
|
||||
O.[UseKeyConnector],
|
||||
O.[UseScim],
|
||||
O.[UseGroups],
|
||||
O.[UseDirectory],
|
||||
O.[UseEvents],
|
||||
O.[UseTotp],
|
||||
O.[Use2fa],
|
||||
O.[UseApi],
|
||||
O.[UseResetPassword],
|
||||
O.[SelfHost],
|
||||
O.[UsersGetPremium],
|
||||
O.[UseCustomPermissions],
|
||||
O.[UseSecretsManager],
|
||||
O.[Seats],
|
||||
O.[MaxCollections],
|
||||
O.[MaxStorageGb],
|
||||
O.[Identifier],
|
||||
OU.[Key],
|
||||
OU.[ResetPasswordKey],
|
||||
O.[PublicKey],
|
||||
O.[PrivateKey],
|
||||
OU.[Status],
|
||||
OU.[Type],
|
||||
SU.[ExternalId] SsoExternalId,
|
||||
OU.[Permissions],
|
||||
PO.[ProviderId],
|
||||
P.[Name] ProviderName,
|
||||
P.[Type] ProviderType,
|
||||
SS.[Enabled] SsoEnabled,
|
||||
SS.[Data] SsoConfig,
|
||||
OS.[FriendlyName] FamilySponsorshipFriendlyName,
|
||||
OS.[LastSyncDate] FamilySponsorshipLastSyncDate,
|
||||
OS.[ToDelete] FamilySponsorshipToDelete,
|
||||
OS.[ValidUntil] FamilySponsorshipValidUntil,
|
||||
OU.[AccessSecretsManager],
|
||||
O.[UsePasswordManager],
|
||||
O.[SmSeats],
|
||||
O.[SmServiceAccounts],
|
||||
O.[LimitCollectionCreation],
|
||||
O.[LimitCollectionDeletion],
|
||||
O.[AllowAdminAccessToAllCollectionItems],
|
||||
O.[UseRiskInsights],
|
||||
O.[LimitItemDeletion],
|
||||
O.[UseAdminSponsoredFamilies],
|
||||
O.[UseOrganizationDomains],
|
||||
OS.[IsAdminInitiated]
|
||||
FROM
|
||||
[dbo].[OrganizationUser] OU
|
||||
LEFT JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = OU.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[ProviderOrganization] PO ON PO.[OrganizationId] = O.[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[Provider] P ON P.[Id] = PO.[ProviderId]
|
||||
LEFT JOIN
|
||||
[dbo].[SsoConfig] SS ON SS.[OrganizationId] = OU.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[OrganizationSponsorship] OS ON OS.[SponsoringOrganizationUserID] = OU.[Id]
|
||||
GO
|
||||
|
||||
--Manually refresh [dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]
|
||||
IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]') IS NOT NULL
|
||||
BEGIN
|
||||
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]';
|
||||
END
|
||||
GO
|
||||
|
||||
--Manually refresh [dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]
|
||||
IF OBJECT_ID('[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]') IS NOT NULL
|
||||
BEGIN
|
||||
EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]';
|
||||
END
|
||||
GO
|
||||
Reference in New Issue
Block a user