mirror of
https://github.com/bitwarden/server.git
synced 2025-05-12 15:12:16 -05:00
Merge branch 'main' into ac/pm-15161/create-resellerclientorganizationsignup-command
This commit is contained in:
commit
46ebffa6fa
66
.github/renovate.json5
vendored
66
.github/renovate.json5
vendored
@ -20,7 +20,7 @@
|
|||||||
],
|
],
|
||||||
commitMessagePrefix: "[deps] BRE:",
|
commitMessagePrefix: "[deps] BRE:",
|
||||||
reviewers: ["team:dept-bre"],
|
reviewers: ["team:dept-bre"],
|
||||||
addLabels: ["hold"]
|
addLabels: ["hold"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
groupName: "dockerfile minor",
|
groupName: "dockerfile minor",
|
||||||
@ -37,6 +37,16 @@
|
|||||||
matchManagers: ["github-actions"],
|
matchManagers: ["github-actions"],
|
||||||
matchUpdateTypes: ["minor"],
|
matchUpdateTypes: ["minor"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// For any Microsoft.Extensions.* and Microsoft.AspNetCore.* packages, we want to create PRs for patch updates.
|
||||||
|
// This overrides the default that ignores patch updates for nuget dependencies.
|
||||||
|
matchPackageNames: [
|
||||||
|
"/^Microsoft\\.Extensions\\./",
|
||||||
|
"/^Microsoft\\.AspNetCore\\./",
|
||||||
|
],
|
||||||
|
matchUpdateTypes: ["patch"],
|
||||||
|
dependencyDashboardApproval: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
matchManagers: ["dockerfile", "docker-compose"],
|
matchManagers: ["dockerfile", "docker-compose"],
|
||||||
commitMessagePrefix: "[deps] BRE:",
|
commitMessagePrefix: "[deps] BRE:",
|
||||||
@ -59,6 +69,7 @@
|
|||||||
"DuoUniversal",
|
"DuoUniversal",
|
||||||
"Fido2.AspNet",
|
"Fido2.AspNet",
|
||||||
"Duende.IdentityServer",
|
"Duende.IdentityServer",
|
||||||
|
"Microsoft.AspNetCore.Authentication.JwtBearer",
|
||||||
"Microsoft.Extensions.Identity.Stores",
|
"Microsoft.Extensions.Identity.Stores",
|
||||||
"Otp.NET",
|
"Otp.NET",
|
||||||
"Sustainsys.Saml2.AspNetCore2",
|
"Sustainsys.Saml2.AspNetCore2",
|
||||||
@ -79,8 +90,6 @@
|
|||||||
"CsvHelper",
|
"CsvHelper",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp",
|
"Kralizek.AutoFixture.Extensions.MockHttp",
|
||||||
"Microsoft.AspNetCore.Mvc.Testing",
|
"Microsoft.AspNetCore.Mvc.Testing",
|
||||||
"Microsoft.Extensions.Logging",
|
|
||||||
"Microsoft.Extensions.Logging.Console",
|
|
||||||
"Newtonsoft.Json",
|
"Newtonsoft.Json",
|
||||||
"NSubstitute",
|
"NSubstitute",
|
||||||
"Sentry.Serilog",
|
"Sentry.Serilog",
|
||||||
@ -100,9 +109,9 @@
|
|||||||
reviewers: ["team:team-billing-dev"],
|
reviewers: ["team:team-billing-dev"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
matchPackagePatterns: ["^Microsoft.Extensions.Logging"],
|
matchPackageNames: ["/^Microsoft\\.EntityFrameworkCore\\./", "/^dotnet-ef/"],
|
||||||
groupName: "Microsoft.Extensions.Logging",
|
groupName: "EntityFrameworkCore",
|
||||||
description: "Group Microsoft.Extensions.Logging to exclude them from the dotnet monorepo preset",
|
description: "Group EntityFrameworkCore to exclude them from the dotnet monorepo preset",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
matchPackageNames: [
|
matchPackageNames: [
|
||||||
@ -117,9 +126,6 @@
|
|||||||
"Microsoft.EntityFrameworkCore.Relational",
|
"Microsoft.EntityFrameworkCore.Relational",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite",
|
"Microsoft.EntityFrameworkCore.Sqlite",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer",
|
"Microsoft.EntityFrameworkCore.SqlServer",
|
||||||
"Microsoft.Extensions.Caching.Cosmos",
|
|
||||||
"Microsoft.Extensions.Caching.SqlServer",
|
|
||||||
"Microsoft.Extensions.Caching.StackExchangeRedis",
|
|
||||||
"Npgsql.EntityFrameworkCore.PostgreSQL",
|
"Npgsql.EntityFrameworkCore.PostgreSQL",
|
||||||
"Pomelo.EntityFrameworkCore.MySql",
|
"Pomelo.EntityFrameworkCore.MySql",
|
||||||
],
|
],
|
||||||
@ -142,56 +148,40 @@
|
|||||||
"Azure.Messaging.ServiceBus",
|
"Azure.Messaging.ServiceBus",
|
||||||
"Azure.Storage.Blobs",
|
"Azure.Storage.Blobs",
|
||||||
"Azure.Storage.Queues",
|
"Azure.Storage.Queues",
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer",
|
"LaunchDarkly.ServerSdk",
|
||||||
"Microsoft.AspNetCore.Http",
|
"Microsoft.AspNetCore.Http",
|
||||||
|
"Microsoft.AspNetCore.SignalR.Protocols.MessagePack",
|
||||||
|
"Microsoft.AspNetCore.SignalR.StackExchangeRedis",
|
||||||
|
"Microsoft.Extensions.Configuration.EnvironmentVariables",
|
||||||
|
"Microsoft.Extensions.Configuration.UserSecrets",
|
||||||
|
"Microsoft.Extensions.Configuration",
|
||||||
|
"Microsoft.Extensions.DependencyInjection.Abstractions",
|
||||||
|
"Microsoft.Extensions.DependencyInjection",
|
||||||
|
"Microsoft.Extensions.Logging",
|
||||||
|
"Microsoft.Extensions.Logging.Console",
|
||||||
|
"Microsoft.Extensions.Caching.Cosmos",
|
||||||
|
"Microsoft.Extensions.Caching.SqlServer",
|
||||||
|
"Microsoft.Extensions.Caching.StackExchangeRedis",
|
||||||
"Quartz",
|
"Quartz",
|
||||||
],
|
],
|
||||||
description: "Platform owned dependencies",
|
description: "Platform owned dependencies",
|
||||||
commitMessagePrefix: "[deps] Platform:",
|
commitMessagePrefix: "[deps] Platform:",
|
||||||
reviewers: ["team:team-platform-dev"],
|
reviewers: ["team:team-platform-dev"],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
matchPackagePatterns: ["EntityFrameworkCore", "^dotnet-ef"],
|
|
||||||
groupName: "EntityFrameworkCore",
|
|
||||||
description: "Group EntityFrameworkCore to exclude them from the dotnet monorepo preset",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
matchPackageNames: [
|
matchPackageNames: [
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection",
|
||||||
"AWSSDK.SimpleEmail",
|
"AWSSDK.SimpleEmail",
|
||||||
"AWSSDK.SQS",
|
"AWSSDK.SQS",
|
||||||
"Handlebars.Net",
|
"Handlebars.Net",
|
||||||
"LaunchDarkly.ServerSdk",
|
|
||||||
"MailKit",
|
"MailKit",
|
||||||
"Microsoft.AspNetCore.SignalR.Protocols.MessagePack",
|
|
||||||
"Microsoft.AspNetCore.SignalR.StackExchangeRedis",
|
|
||||||
"Microsoft.Azure.NotificationHubs",
|
"Microsoft.Azure.NotificationHubs",
|
||||||
"Microsoft.Extensions.Configuration.EnvironmentVariables",
|
|
||||||
"Microsoft.Extensions.Configuration.UserSecrets",
|
|
||||||
"Microsoft.Extensions.Configuration",
|
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions",
|
|
||||||
"Microsoft.Extensions.DependencyInjection",
|
|
||||||
"SendGrid",
|
"SendGrid",
|
||||||
],
|
],
|
||||||
description: "Tools owned dependencies",
|
description: "Tools owned dependencies",
|
||||||
commitMessagePrefix: "[deps] Tools:",
|
commitMessagePrefix: "[deps] Tools:",
|
||||||
reviewers: ["team:team-tools-dev"],
|
reviewers: ["team:team-tools-dev"],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
matchPackagePatterns: ["^Microsoft.AspNetCore.SignalR"],
|
|
||||||
groupName: "SignalR",
|
|
||||||
description: "Group SignalR to exclude them from the dotnet monorepo preset",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matchPackagePatterns: ["^Microsoft.Extensions.Configuration"],
|
|
||||||
groupName: "Microsoft.Extensions.Configuration",
|
|
||||||
description: "Group Microsoft.Extensions.Configuration to exclude them from the dotnet monorepo preset",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matchPackagePatterns: ["^Microsoft.Extensions.DependencyInjection"],
|
|
||||||
groupName: "Microsoft.Extensions.DependencyInjection",
|
|
||||||
description: "Group Microsoft.Extensions.DependencyInjection to exclude them from the dotnet monorepo preset",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
matchPackageNames: [
|
matchPackageNames: [
|
||||||
"AngleSharp",
|
"AngleSharp",
|
||||||
|
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@ -14,6 +14,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
_AZ_REGISTRY: "bitwardenprod.azurecr.io"
|
_AZ_REGISTRY: "bitwardenprod.azurecr.io"
|
||||||
|
_GITHUB_PR_REPO_NAME: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
@ -234,12 +235,18 @@ jobs:
|
|||||||
- name: Generate Docker image tag
|
- name: Generate Docker image tag
|
||||||
id: tag
|
id: tag
|
||||||
run: |
|
run: |
|
||||||
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
|
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" || "${GITHUB_EVENT_NAME}" == "pull_request_target" ]]; then
|
||||||
IMAGE_TAG=$(echo "${GITHUB_HEAD_REF}" | sed "s#/#-#g")
|
IMAGE_TAG=$(echo "${GITHUB_HEAD_REF}" | sed "s/[^a-zA-Z0-9]/-/g") # Sanitize branch name to alphanumeric only
|
||||||
else
|
else
|
||||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g")
|
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||||
|
SANITIZED_REPO_NAME=$(echo "$_GITHUB_PR_REPO_NAME" | sed "s/[^a-zA-Z0-9]/-/g") # Sanitize repo name to alphanumeric only
|
||||||
|
IMAGE_TAG=$SANITIZED_REPO_NAME-$IMAGE_TAG # Add repo name to the tag
|
||||||
|
IMAGE_TAG=${IMAGE_TAG:0:128} # Limit to 128 characters, as that's the max length for Docker image tags
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$IMAGE_TAG" == "main" ]]; then
|
if [[ "$IMAGE_TAG" == "main" ]]; then
|
||||||
IMAGE_TAG=dev
|
IMAGE_TAG=dev
|
||||||
fi
|
fi
|
||||||
|
@ -6,10 +6,10 @@ using Bit.Core.AdminConsole.Models.Business;
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Commands;
|
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -76,9 +76,8 @@ public class PostUserCommand(
|
|||||||
var invitedOrganizationUserId = result switch
|
var invitedOrganizationUserId = result switch
|
||||||
{
|
{
|
||||||
Success<ScimInviteOrganizationUsersResponse> success => success.Value.InvitedUser.Id,
|
Success<ScimInviteOrganizationUsersResponse> success => success.Value.InvitedUser.Id,
|
||||||
Failure<ScimInviteOrganizationUsersResponse> failure when failure.Errors
|
Failure<ScimInviteOrganizationUsersResponse> { Error.Message: NoUsersToInviteError.Code } => (Guid?)null,
|
||||||
.Any(x => x.Message == NoUsersToInviteError.Code) => (Guid?)null,
|
Failure<ScimInviteOrganizationUsersResponse> failure => throw MapToBitException(failure.Error),
|
||||||
Failure<ScimInviteOrganizationUsersResponse> failure when failure.Errors.Length != 0 => throw MapToBitException(failure.Errors),
|
|
||||||
_ => throw new InvalidOperationException()
|
_ => throw new InvalidOperationException()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
using Bit.Core.Models.Commands;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Bit.Api.Utilities;
|
|
||||||
|
|
||||||
public static class CommandResultExtensions
|
|
||||||
{
|
|
||||||
public static IActionResult MapToActionResult<T>(this CommandResult<T> commandResult)
|
|
||||||
{
|
|
||||||
return commandResult switch
|
|
||||||
{
|
|
||||||
NoRecordFoundFailure<T> failure => new ObjectResult(failure.ErrorMessages) { StatusCode = StatusCodes.Status404NotFound },
|
|
||||||
BadRequestFailure<T> failure => new ObjectResult(failure.ErrorMessages) { StatusCode = StatusCodes.Status400BadRequest },
|
|
||||||
Failure<T> failure => new ObjectResult(failure.ErrorMessages) { StatusCode = StatusCodes.Status400BadRequest },
|
|
||||||
Success<T> success => new ObjectResult(success.Value) { StatusCode = StatusCodes.Status200OK },
|
|
||||||
_ => throw new InvalidOperationException($"Unhandled commandResult type: {commandResult.GetType().Name}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IActionResult MapToActionResult(this CommandResult commandResult)
|
|
||||||
{
|
|
||||||
return commandResult switch
|
|
||||||
{
|
|
||||||
NoRecordFoundFailure failure => new ObjectResult(failure.ErrorMessages) { StatusCode = StatusCodes.Status404NotFound },
|
|
||||||
BadRequestFailure failure => new ObjectResult(failure.ErrorMessages) { StatusCode = StatusCodes.Status400BadRequest },
|
|
||||||
Failure failure => new ObjectResult(failure.ErrorMessages) { StatusCode = StatusCodes.Status400BadRequest },
|
|
||||||
Success => new ObjectResult(new { }) { StatusCode = StatusCodes.Status200OK },
|
|
||||||
_ => throw new InvalidOperationException($"Unhandled commandResult type: {commandResult.GetType().Name}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
||||||
using Bit.Core.Models.Commands;
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||||
using Bit.Core.Models.Commands;
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Errors;
|
|
||||||
using Bit.Core.AdminConsole.Interfaces;
|
using Bit.Core.AdminConsole.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Models.Business;
|
using Bit.Core.AdminConsole.Models.Business;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Models.Commands;
|
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -50,11 +50,11 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
|
|||||||
{
|
{
|
||||||
case Failure<InviteOrganizationUsersResponse> failure:
|
case Failure<InviteOrganizationUsersResponse> failure:
|
||||||
return new Failure<ScimInviteOrganizationUsersResponse>(
|
return new Failure<ScimInviteOrganizationUsersResponse>(
|
||||||
failure.Errors.Select(error => new Error<ScimInviteOrganizationUsersResponse>(error.Message,
|
new Error<ScimInviteOrganizationUsersResponse>(failure.Error.Message,
|
||||||
new ScimInviteOrganizationUsersResponse
|
new ScimInviteOrganizationUsersResponse
|
||||||
{
|
{
|
||||||
InvitedUser = error.ErroredValue.InvitedUsers.FirstOrDefault()
|
InvitedUser = failure.Error.ErroredValue.InvitedUsers.FirstOrDefault()
|
||||||
})));
|
}));
|
||||||
|
|
||||||
case Success<InviteOrganizationUsersResponse> success when success.Value.InvitedUsers.Any():
|
case Success<InviteOrganizationUsersResponse> success when success.Value.InvitedUsers.Any():
|
||||||
var user = success.Value.InvitedUsers.First();
|
var user = success.Value.InvitedUsers.First();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.GlobalSettings;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.Models.Business;
|
||||||
using Bit.Core.AdminConsole.Models.Business;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Models.Business;
|
using Bit.Core.AdminConsole.Models.Business;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.V
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Provider;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Provider;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Payments;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Payments;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Payments;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Payments;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Provider;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Provider;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Provider;
|
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Provider;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using Bit.Core.AdminConsole.Models.Data;
|
using Bit.Core.AdminConsole.Models.Data;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Commands;
|
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.Shared.Validation;
|
|
||||||
|
|
||||||
public abstract record ValidationResult<T>;
|
|
||||||
|
|
||||||
public record Valid<T> : ValidationResult<T>
|
|
||||||
{
|
|
||||||
public Valid() { }
|
|
||||||
|
|
||||||
public Valid(T Value)
|
|
||||||
{
|
|
||||||
this.Value = Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Value { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public record Invalid<T> : ValidationResult<T>
|
|
||||||
{
|
|
||||||
public IEnumerable<Error<T>> Errors { get; init; } = [];
|
|
||||||
|
|
||||||
public string ErrorMessageString => string.Join(" ", Errors.Select(e => e.Message));
|
|
||||||
|
|
||||||
public Invalid() { }
|
|
||||||
|
|
||||||
public Invalid(Error<T> error) : this([error]) { }
|
|
||||||
|
|
||||||
public Invalid(IEnumerable<Error<T>> errors)
|
|
||||||
{
|
|
||||||
Errors = errors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ValidationResultMappers
|
|
||||||
{
|
|
||||||
public static ValidationResult<B> Map<A, B>(this ValidationResult<A> validationResult, B invalidValue) =>
|
|
||||||
validationResult switch
|
|
||||||
{
|
|
||||||
Valid<A> => new Valid<B>(invalidValue),
|
|
||||||
Invalid<A> invalid => new Invalid<B>(invalid.Errors.Select(x => x.ToError(invalidValue))),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(validationResult), "Unhandled validation result type")
|
|
||||||
};
|
|
||||||
}
|
|
51
src/Core/AdminConsole/Utilities/Commands/CommandResult.cs
Normal file
51
src/Core/AdminConsole/Utilities/Commands/CommandResult.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
|
|
||||||
|
namespace Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
|
|
||||||
|
public abstract class CommandResult<T>;
|
||||||
|
|
||||||
|
public class Success<T>(T value) : CommandResult<T>
|
||||||
|
{
|
||||||
|
public T Value { get; } = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Failure<T>(Error<T> error) : CommandResult<T>
|
||||||
|
{
|
||||||
|
public Error<T> Error { get; } = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Partial<T>(IEnumerable<T> successfulItems, IEnumerable<Error<T>> failedItems)
|
||||||
|
: CommandResult<T>
|
||||||
|
{
|
||||||
|
public IEnumerable<T> Successes { get; } = successfulItems;
|
||||||
|
public IEnumerable<Error<T>> Failures { get; } = failedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CommandResultExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is to help map between the InvalidT ValidationResult and the FailureT CommandResult types.
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="invalidResult">This is the invalid type from validating the object.</param>
|
||||||
|
/// <param name="mappingFunction">This function will map between the two types for the inner ErrorT</param>
|
||||||
|
/// <typeparam name="A">Invalid object's type</typeparam>
|
||||||
|
/// <typeparam name="B">Failure object's type</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static CommandResult<B> MapToFailure<A, B>(this Invalid<A> invalidResult, Func<A, B> mappingFunction) =>
|
||||||
|
new Failure<B>(invalidResult.Error.ToError(mappingFunction(invalidResult.Error.ErroredValue)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use CommandResult<T> instead. This will be removed once old code is updated.")]
|
||||||
|
public class CommandResult(IEnumerable<string> errors)
|
||||||
|
{
|
||||||
|
public CommandResult(string error) : this([error]) { }
|
||||||
|
|
||||||
|
public bool Success => ErrorMessages.Count == 0;
|
||||||
|
public bool HasErrors => ErrorMessages.Count > 0;
|
||||||
|
public List<string> ErrorMessages { get; } = errors.ToList();
|
||||||
|
public CommandResult() : this(Array.Empty<string>()) { }
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace Bit.Core.AdminConsole.Errors;
|
namespace Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
public record Error<T>(string Message, T ErroredValue);
|
public record Error<T>(string Message, T ErroredValue);
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
namespace Bit.Core.AdminConsole.Errors;
|
namespace Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
public record InsufficientPermissionsError<T>(string Message, T ErroredValue) : Error<T>(Message, ErroredValue)
|
public record InsufficientPermissionsError<T>(string Message, T ErroredValue) : Error<T>(Message, ErroredValue)
|
||||||
{
|
{
|
@ -1,4 +1,4 @@
|
|||||||
namespace Bit.Core.AdminConsole.Errors;
|
namespace Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
public record InvalidResultTypeError<T>(T Value) : Error<T>(Code, Value)
|
public record InvalidResultTypeError<T>(T Value) : Error<T>(Code, Value)
|
||||||
{
|
{
|
@ -1,4 +1,4 @@
|
|||||||
namespace Bit.Core.AdminConsole.Errors;
|
namespace Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
public record RecordNotFoundError<T>(string Message, T ErroredValue) : Error<T>(Message, ErroredValue)
|
public record RecordNotFoundError<T>(string Message, T ErroredValue) : Error<T>(Message, ErroredValue)
|
||||||
{
|
{
|
@ -1,4 +1,4 @@
|
|||||||
namespace Bit.Core.AdminConsole.Shared.Validation;
|
namespace Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
|
|
||||||
public interface IValidator<T>
|
public interface IValidator<T>
|
||||||
{
|
{
|
@ -0,0 +1,20 @@
|
|||||||
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
|
||||||
|
namespace Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
|
|
||||||
|
public abstract record ValidationResult<T>;
|
||||||
|
|
||||||
|
public record Valid<T>(T Value) : ValidationResult<T>;
|
||||||
|
|
||||||
|
public record Invalid<T>(Error<T> Error) : ValidationResult<T>;
|
||||||
|
|
||||||
|
public static class ValidationResultMappers
|
||||||
|
{
|
||||||
|
public static ValidationResult<B> Map<A, B>(this ValidationResult<A> validationResult, B invalidValue) =>
|
||||||
|
validationResult switch
|
||||||
|
{
|
||||||
|
Valid<A> => new Valid<B>(invalidValue),
|
||||||
|
Invalid<A> invalid => new Invalid<B>(invalid.Error.ToError(invalidValue)),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(validationResult), "Unhandled validation result type")
|
||||||
|
};
|
||||||
|
}
|
@ -16,10 +16,11 @@ public class DuoUniversalTokenProvider(
|
|||||||
IDuoUniversalTokenService duoUniversalTokenService) : IUserTwoFactorTokenProvider<User>
|
IDuoUniversalTokenService duoUniversalTokenService) : IUserTwoFactorTokenProvider<User>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// We need the IServiceProvider to resolve the IUserService. There is a complex dependency dance
|
/// We need the IServiceProvider to resolve the <see cref="IUserService"/>. There is a complex dependency dance
|
||||||
/// occurring between IUserService, which extends the UserManager<User>, and the usage of the
|
/// occurring between <see cref="IUserService"/>, which extends the <see cref="UserManager{User}"/>, and the usage
|
||||||
/// UserManager<User> within this class. Trying to resolve the IUserService using the DI pipeline
|
/// of the <see cref="UserManager{User}"/> within this class. Trying to resolve the <see cref="IUserService"/> using
|
||||||
/// will not allow the server to start and it will hang and give no helpful indication as to the problem.
|
/// the DI pipeline will not allow the server to start and it will hang and give no helpful indication as to the
|
||||||
|
/// problem.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IServiceProvider _serviceProvider = serviceProvider;
|
private readonly IServiceProvider _serviceProvider = serviceProvider;
|
||||||
private readonly IDataProtectorTokenFactory<DuoUserStateTokenable> _tokenDataFactory = tokenDataFactory;
|
private readonly IDataProtectorTokenFactory<DuoUserStateTokenable> _tokenDataFactory = tokenDataFactory;
|
||||||
|
@ -7,4 +7,5 @@ public class TrialSendVerificationEmailRequestModel : RegisterSendVerificationEm
|
|||||||
{
|
{
|
||||||
public ProductTierType ProductTier { get; set; }
|
public ProductTierType ProductTier { get; set; }
|
||||||
public IEnumerable<ProductType> Products { get; set; }
|
public IEnumerable<ProductType> Products { get; set; }
|
||||||
|
public int? TrialLength { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.Auth.Models.Mail;
|
using Bit.Core.Auth.Models.Mail;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
namespace Bit.Core.Billing.Models.Mail;
|
namespace Bit.Core.Billing.Models.Mail;
|
||||||
|
|
||||||
@ -16,13 +17,26 @@ public class TrialInitiationVerifyEmail : RegisterVerifyEmail
|
|||||||
$"&email={Email}" +
|
$"&email={Email}" +
|
||||||
$"&fromEmail=true" +
|
$"&fromEmail=true" +
|
||||||
$"&productTier={(int)ProductTier}" +
|
$"&productTier={(int)ProductTier}" +
|
||||||
$"&product={string.Join(",", Product.Select(p => (int)p))}";
|
$"&product={string.Join(",", Product.Select(p => (int)p))}" +
|
||||||
|
$"&trialLength={TrialLength}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string VerifyYourEmailHTMLCopy =>
|
||||||
|
TrialLength == 7
|
||||||
|
? "Verify your email address below to finish signing up for your free trial."
|
||||||
|
: $"Verify your email address below to finish signing up for your {ProductTier.GetDisplayName()} plan.";
|
||||||
|
|
||||||
|
public string VerifyYourEmailTextCopy =>
|
||||||
|
TrialLength == 7
|
||||||
|
? "Verify your email address using the link below and start your free trial of Bitwarden."
|
||||||
|
: $"Verify your email address using the link below and start your {ProductTier.GetDisplayName()} Bitwarden plan.";
|
||||||
|
|
||||||
public ProductTierType ProductTier { get; set; }
|
public ProductTierType ProductTier { get; set; }
|
||||||
|
|
||||||
public IEnumerable<ProductType> Product { get; set; }
|
public IEnumerable<ProductType> Product { get; set; }
|
||||||
|
|
||||||
|
public int TrialLength { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Currently we only support one product type at a time, despite Product being a collection.
|
/// Currently we only support one product type at a time, despite Product being a collection.
|
||||||
/// If we receive both PasswordManager and SecretsManager, we'll send the user to the PM trial route
|
/// If we receive both PasswordManager and SecretsManager, we'll send the user to the PM trial route
|
||||||
|
@ -10,5 +10,6 @@ public interface ISendTrialInitiationEmailForRegistrationCommand
|
|||||||
string? name,
|
string? name,
|
||||||
bool receiveMarketingEmails,
|
bool receiveMarketingEmails,
|
||||||
ProductTierType productTier,
|
ProductTierType productTier,
|
||||||
IEnumerable<ProductType> products);
|
IEnumerable<ProductType> products,
|
||||||
|
int trialLength);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,8 @@ public class SendTrialInitiationEmailForRegistrationCommand(
|
|||||||
string? name,
|
string? name,
|
||||||
bool receiveMarketingEmails,
|
bool receiveMarketingEmails,
|
||||||
ProductTierType productTier,
|
ProductTierType productTier,
|
||||||
IEnumerable<ProductType> products)
|
IEnumerable<ProductType> products,
|
||||||
|
int trialLength)
|
||||||
{
|
{
|
||||||
ArgumentException.ThrowIfNullOrWhiteSpace(email, nameof(email));
|
ArgumentException.ThrowIfNullOrWhiteSpace(email, nameof(email));
|
||||||
|
|
||||||
@ -43,7 +44,12 @@ public class SendTrialInitiationEmailForRegistrationCommand(
|
|||||||
|
|
||||||
await PerformConstantTimeOperationsAsync();
|
await PerformConstantTimeOperationsAsync();
|
||||||
|
|
||||||
await mailService.SendTrialInitiationSignupEmailAsync(userExists, email, token, productTier, products);
|
if (trialLength != 0 && trialLength != 7)
|
||||||
|
{
|
||||||
|
trialLength = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
await mailService.SendTrialInitiationSignupEmailAsync(userExists, email, token, productTier, products, trialLength);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -114,8 +114,6 @@ public static class FeatureFlagKeys
|
|||||||
public const string PM9112DeviceApprovalPersistence = "pm-9112-device-approval-persistence";
|
public const string PM9112DeviceApprovalPersistence = "pm-9112-device-approval-persistence";
|
||||||
public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence";
|
public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence";
|
||||||
public const string EmailVerification = "email-verification";
|
public const string EmailVerification = "email-verification";
|
||||||
public const string DeviceTrustLogging = "pm-8285-device-trust-logging";
|
|
||||||
public const string AuthenticatorTwoFactorToken = "authenticator-2fa-token";
|
|
||||||
public const string UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh";
|
public const string UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh";
|
||||||
public const string NewDeviceVerification = "new-device-verification";
|
public const string NewDeviceVerification = "new-device-verification";
|
||||||
public const string SetInitialPasswordRefactor = "pm-16117-set-initial-password-refactor";
|
public const string SetInitialPasswordRefactor = "pm-16117-set-initial-password-refactor";
|
||||||
@ -151,6 +149,7 @@ public static class FeatureFlagKeys
|
|||||||
public const string PM199566_UpdateMSPToChargeAutomatically = "pm-199566-update-msp-to-charge-automatically";
|
public const string PM199566_UpdateMSPToChargeAutomatically = "pm-199566-update-msp-to-charge-automatically";
|
||||||
public const string PM19956_RequireProviderPaymentMethodDuringSetup = "pm-19956-require-provider-payment-method-during-setup";
|
public const string PM19956_RequireProviderPaymentMethodDuringSetup = "pm-19956-require-provider-payment-method-during-setup";
|
||||||
public const string UseOrganizationWarningsService = "use-organization-warnings-service";
|
public const string UseOrganizationWarningsService = "use-organization-warnings-service";
|
||||||
|
public const string PM20322_AllowTrialLength0 = "pm-20322-allow-trial-length-0";
|
||||||
|
|
||||||
/* Data Insights and Reporting Team */
|
/* Data Insights and Reporting Team */
|
||||||
public const string RiskInsightsCriticalApplication = "pm-14466-risk-insights-critical-application";
|
public const string RiskInsightsCriticalApplication = "pm-14466-risk-insights-critical-application";
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<GenerateUserSecretsAttribute>false</GenerateUserSecretsAttribute>
|
<GenerateUserSecretsAttribute>false</GenerateUserSecretsAttribute>
|
||||||
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
||||||
<!-- Temp exclusions until warnings are fixed -->
|
<!-- Temp exclusions until warnings are fixed -->
|
||||||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS1570;CS1574;CS9113;CS1998</WarningsNotAsErrors>
|
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS1574;CS9113;CS1998</WarningsNotAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
18
src/Core/Enums/EnumExtensions.cs
Normal file
18
src/Core/Enums/EnumExtensions.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Bit.Core.Enums;
|
||||||
|
|
||||||
|
public static class EnumExtensions
|
||||||
|
{
|
||||||
|
public static string GetDisplayName(this Enum value)
|
||||||
|
{
|
||||||
|
var field = value.GetType().GetField(value.ToString());
|
||||||
|
if (field?.GetCustomAttribute<DisplayAttribute>() is { } attribute)
|
||||||
|
{
|
||||||
|
return attribute.Name ?? value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.ToString();
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<tr style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: left;" valign="top" align="center">
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: left;" valign="top" align="center">
|
||||||
Verify your email address below to finish signing up for your free trial.
|
{{VerifyYourEmailHTMLCopy}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
<tr style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{#>BasicTextLayout}}
|
{{#>BasicTextLayout}}
|
||||||
Verify your email address using the link below and start your free trial of Bitwarden.
|
{{VerifyYourEmailTextCopy}}
|
||||||
|
|
||||||
If you did not request this email from Bitwarden, you can safely ignore it.
|
If you did not request this email from Bitwarden, you can safely ignore it.
|
||||||
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
namespace Bit.Core.Models.Commands;
|
|
||||||
|
|
||||||
public class BadRequestFailure<T> : Failure<T>
|
|
||||||
{
|
|
||||||
public BadRequestFailure(IEnumerable<string> errorMessage) : base(errorMessage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public BadRequestFailure(string errorMessage) : base(errorMessage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BadRequestFailure : Failure
|
|
||||||
{
|
|
||||||
public BadRequestFailure(IEnumerable<string> errorMessage) : base(errorMessage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public BadRequestFailure(string errorMessage) : base(errorMessage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
using Bit.Core.AdminConsole.Errors;
|
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
|
||||||
|
|
||||||
namespace Bit.Core.Models.Commands;
|
|
||||||
|
|
||||||
public class CommandResult(IEnumerable<string> errors)
|
|
||||||
{
|
|
||||||
public CommandResult(string error) : this([error]) { }
|
|
||||||
|
|
||||||
public bool Success => ErrorMessages.Count == 0;
|
|
||||||
public bool HasErrors => ErrorMessages.Count > 0;
|
|
||||||
public List<string> ErrorMessages { get; } = errors.ToList();
|
|
||||||
public CommandResult() : this(Array.Empty<string>()) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Failure : CommandResult
|
|
||||||
{
|
|
||||||
protected Failure(IEnumerable<string> errorMessages) : base(errorMessages)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
public Failure(string errorMessage) : base(errorMessage)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Success : CommandResult
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class CommandResult<T>;
|
|
||||||
|
|
||||||
public class Success<T>(T value) : CommandResult<T>
|
|
||||||
{
|
|
||||||
public T Value { get; } = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Failure<T>(IEnumerable<string> errorMessages) : CommandResult<T>
|
|
||||||
{
|
|
||||||
public List<string> ErrorMessages { get; } = errorMessages.ToList();
|
|
||||||
public Error<T>[] Errors { get; set; } = [];
|
|
||||||
|
|
||||||
public string ErrorMessage => string.Join(" ", ErrorMessages);
|
|
||||||
|
|
||||||
public Failure(string error) : this([error])
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Failure(IEnumerable<Error<T>> errors) : this(errors.Select(e => e.Message))
|
|
||||||
{
|
|
||||||
Errors = errors.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Failure(Error<T> error) : this([error.Message])
|
|
||||||
{
|
|
||||||
Errors = [error];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Partial<T> : CommandResult<T>
|
|
||||||
{
|
|
||||||
public T[] Successes { get; set; } = [];
|
|
||||||
public Error<T>[] Failures { get; set; } = [];
|
|
||||||
|
|
||||||
public Partial(IEnumerable<T> successfulItems, IEnumerable<Error<T>> failedItems)
|
|
||||||
{
|
|
||||||
Successes = successfulItems.ToArray();
|
|
||||||
Failures = failedItems.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class CommandResultExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This is to help map between the InvalidT ValidationResult and the FailureT CommandResult types.
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="invalidResult">This is the invalid type from validating the object.</param>
|
|
||||||
/// <param name="mappingFunction">This function will map between the two types for the inner ErrorT</param>
|
|
||||||
/// <typeparam name="A">Invalid object's type</typeparam>
|
|
||||||
/// <typeparam name="B">Failure object's type</typeparam>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static CommandResult<B> MapToFailure<A, B>(this Invalid<A> invalidResult, Func<A, B> mappingFunction) =>
|
|
||||||
new Failure<B>(invalidResult.Errors.Select(errorA => errorA.ToError(mappingFunction(errorA.ErroredValue))));
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
namespace Bit.Core.Models.Commands;
|
|
||||||
|
|
||||||
public class NoRecordFoundFailure<T> : Failure<T>
|
|
||||||
{
|
|
||||||
public NoRecordFoundFailure(IEnumerable<string> errorMessage) : base(errorMessage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public NoRecordFoundFailure(string errorMessage) : base(errorMessage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NoRecordFoundFailure : Failure
|
|
||||||
{
|
|
||||||
public NoRecordFoundFailure(IEnumerable<string> errorMessage) : base(errorMessage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public NoRecordFoundFailure(string errorMessage) : base(errorMessage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -21,7 +21,8 @@ public interface IMailService
|
|||||||
string email,
|
string email,
|
||||||
string token,
|
string token,
|
||||||
ProductTierType productTier,
|
ProductTierType productTier,
|
||||||
IEnumerable<ProductType> products);
|
IEnumerable<ProductType> products,
|
||||||
|
int trialLength);
|
||||||
Task SendVerifyDeleteEmailAsync(string email, Guid userId, string token);
|
Task SendVerifyDeleteEmailAsync(string email, Guid userId, string token);
|
||||||
Task SendCannotDeleteClaimedAccountEmailAsync(string email);
|
Task SendCannotDeleteClaimedAccountEmailAsync(string email);
|
||||||
Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail);
|
Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail);
|
||||||
|
@ -84,7 +84,8 @@ public class HandlebarsMailService : IMailService
|
|||||||
string email,
|
string email,
|
||||||
string token,
|
string token,
|
||||||
ProductTierType productTier,
|
ProductTierType productTier,
|
||||||
IEnumerable<ProductType> products)
|
IEnumerable<ProductType> products,
|
||||||
|
int trialLength)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage("Verify your email", email);
|
var message = CreateDefaultMessage("Verify your email", email);
|
||||||
var model = new TrialInitiationVerifyEmail
|
var model = new TrialInitiationVerifyEmail
|
||||||
@ -95,7 +96,8 @@ public class HandlebarsMailService : IMailService
|
|||||||
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
||||||
SiteName = _globalSettings.SiteName,
|
SiteName = _globalSettings.SiteName,
|
||||||
ProductTier = productTier,
|
ProductTier = productTier,
|
||||||
Product = products
|
Product = products,
|
||||||
|
TrialLength = trialLength
|
||||||
};
|
};
|
||||||
await AddMessageContentAsync(message, "Billing.TrialInitiationVerifyEmail", model);
|
await AddMessageContentAsync(message, "Billing.TrialInitiationVerifyEmail", model);
|
||||||
message.MetaData.Add("SendGridBypassListManagement", true);
|
message.MetaData.Add("SendGridBypassListManagement", true);
|
||||||
|
@ -112,6 +112,8 @@ public class StripePaymentService : IPaymentService
|
|||||||
throw new BadRequestException("You do not have an active subscription. Reinstate your subscription to make changes.");
|
throw new BadRequestException("You do not have an active subscription. Reinstate your subscription to make changes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var existingCoupon = sub.Customer.Discount?.Coupon?.Id;
|
||||||
|
|
||||||
var collectionMethod = sub.CollectionMethod;
|
var collectionMethod = sub.CollectionMethod;
|
||||||
var daysUntilDue = sub.DaysUntilDue;
|
var daysUntilDue = sub.DaysUntilDue;
|
||||||
var chargeNow = collectionMethod == "charge_automatically";
|
var chargeNow = collectionMethod == "charge_automatically";
|
||||||
@ -216,6 +218,19 @@ public class StripePaymentService : IPaymentService
|
|||||||
DaysUntilDue = daysUntilDue,
|
DaysUntilDue = daysUntilDue,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var customer = await _stripeAdapter.CustomerGetAsync(sub.CustomerId);
|
||||||
|
|
||||||
|
var newCoupon = customer.Discount?.Coupon?.Id;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(existingCoupon) && string.IsNullOrEmpty(newCoupon))
|
||||||
|
{
|
||||||
|
// Re-add the lost coupon due to the update.
|
||||||
|
await _stripeAdapter.CustomerUpdateAsync(sub.CustomerId, new CustomerUpdateOptions
|
||||||
|
{
|
||||||
|
Coupon = existingCoupon
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return paymentIntentClientSecret;
|
return paymentIntentClientSecret;
|
||||||
|
@ -33,7 +33,8 @@ public class NoopMailService : IMailService
|
|||||||
string email,
|
string email,
|
||||||
string token,
|
string token,
|
||||||
ProductTierType productTier,
|
ProductTierType productTier,
|
||||||
IEnumerable<ProductType> products)
|
IEnumerable<ProductType> products,
|
||||||
|
int trailLength)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using Bit.Core.Billing.Models.Api.Requests.Accounts;
|
using Bit.Core;
|
||||||
|
using Bit.Core.Billing.Models.Api.Requests.Accounts;
|
||||||
using Bit.Core.Billing.TrialInitiation.Registration;
|
using Bit.Core.Billing.TrialInitiation.Registration;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Tools.Enums;
|
using Bit.Core.Tools.Enums;
|
||||||
using Bit.Core.Tools.Models.Business;
|
using Bit.Core.Tools.Models.Business;
|
||||||
using Bit.Core.Tools.Services;
|
using Bit.Core.Tools.Services;
|
||||||
@ -15,18 +17,24 @@ namespace Bit.Identity.Billing.Controller;
|
|||||||
public class AccountsController(
|
public class AccountsController(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
ISendTrialInitiationEmailForRegistrationCommand sendTrialInitiationEmailForRegistrationCommand,
|
ISendTrialInitiationEmailForRegistrationCommand sendTrialInitiationEmailForRegistrationCommand,
|
||||||
IReferenceEventService referenceEventService) : Microsoft.AspNetCore.Mvc.Controller
|
IReferenceEventService referenceEventService,
|
||||||
|
IFeatureService featureService) : Microsoft.AspNetCore.Mvc.Controller
|
||||||
{
|
{
|
||||||
[HttpPost("trial/send-verification-email")]
|
[HttpPost("trial/send-verification-email")]
|
||||||
[SelfHosted(NotSelfHostedOnly = true)]
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
public async Task<IActionResult> PostTrialInitiationSendVerificationEmailAsync([FromBody] TrialSendVerificationEmailRequestModel model)
|
public async Task<IActionResult> PostTrialInitiationSendVerificationEmailAsync([FromBody] TrialSendVerificationEmailRequestModel model)
|
||||||
{
|
{
|
||||||
|
var allowTrialLength0 = featureService.IsEnabled(FeatureFlagKeys.PM20322_AllowTrialLength0);
|
||||||
|
|
||||||
|
var trialLength = allowTrialLength0 ? model.TrialLength ?? 7 : 7;
|
||||||
|
|
||||||
var token = await sendTrialInitiationEmailForRegistrationCommand.Handle(
|
var token = await sendTrialInitiationEmailForRegistrationCommand.Handle(
|
||||||
model.Email,
|
model.Email,
|
||||||
model.Name,
|
model.Name,
|
||||||
model.ReceiveMarketingEmails,
|
model.ReceiveMarketingEmails,
|
||||||
model.ProductTier,
|
model.ProductTier,
|
||||||
model.Products);
|
model.Products,
|
||||||
|
trialLength);
|
||||||
|
|
||||||
var refEvent = new ReferenceEvent
|
var refEvent = new ReferenceEvent
|
||||||
{
|
{
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<UserSecretsId>bitwarden-Identity</UserSecretsId>
|
<UserSecretsId>bitwarden-Identity</UserSecretsId>
|
||||||
<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
|
<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
|
||||||
<!-- Temp exclusions until warnings are fixed -->
|
|
||||||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS0162</WarningsNotAsErrors>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(RunConfiguration)' == 'Identity' " />
|
<PropertyGroup Condition=" '$(RunConfiguration)' == 'Identity' " />
|
||||||
|
@ -145,6 +145,7 @@ public class Startup
|
|||||||
// Services
|
// Services
|
||||||
services.AddBaseServices(globalSettings);
|
services.AddBaseServices(globalSettings);
|
||||||
services.AddDefaultServices(globalSettings);
|
services.AddDefaultServices(globalSettings);
|
||||||
|
services.AddOptionality();
|
||||||
services.AddCoreLocalizationServices();
|
services.AddCoreLocalizationServices();
|
||||||
services.AddBillingOperations();
|
services.AddBillingOperations();
|
||||||
|
|
||||||
|
@ -17,15 +17,11 @@ public class DeviceRepository : Repository<Device, Guid>, IDeviceRepository
|
|||||||
private readonly IGlobalSettings _globalSettings;
|
private readonly IGlobalSettings _globalSettings;
|
||||||
|
|
||||||
public DeviceRepository(GlobalSettings globalSettings)
|
public DeviceRepository(GlobalSettings globalSettings)
|
||||||
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
|
: base(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
|
||||||
{
|
{
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceRepository(string connectionString, string readOnlyConnectionString)
|
|
||||||
: base(connectionString, readOnlyConnectionString)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public async Task<Device?> GetByIdAsync(Guid id, Guid userId)
|
public async Task<Device?> GetByIdAsync(Guid id, Guid userId)
|
||||||
{
|
{
|
||||||
var device = await GetByIdAsync(id);
|
var device = await GetByIdAsync(id);
|
||||||
|
@ -304,7 +304,7 @@ public class GroupsControllerPutTests
|
|||||||
// Arrange repositories
|
// Arrange repositories
|
||||||
sutProvider.GetDependency<IGroupRepository>().GetManyUserIdsByIdAsync(group.Id).Returns(currentGroupUsers ?? []);
|
sutProvider.GetDependency<IGroupRepository>().GetManyUserIdsByIdAsync(group.Id).Returns(currentGroupUsers ?? []);
|
||||||
sutProvider.GetDependency<IGroupRepository>().GetByIdWithCollectionsAsync(group.Id)
|
sutProvider.GetDependency<IGroupRepository>().GetByIdWithCollectionsAsync(group.Id)
|
||||||
.Returns(new Tuple<Group, ICollection<CollectionAccessSelection>>(group, currentCollectionAccess ?? []));
|
.Returns(new Tuple<Group?, ICollection<CollectionAccessSelection>>(group, currentCollectionAccess ?? []));
|
||||||
if (savingUser != null)
|
if (savingUser != null)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(orgId, savingUser.UserId!.Value)
|
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(orgId, savingUser.UserId!.Value)
|
||||||
|
@ -8,7 +8,6 @@ using Bit.Core.Entities;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -23,7 +22,6 @@ public class DevicesControllerTest
|
|||||||
private readonly IUntrustDevicesCommand _untrustDevicesCommand;
|
private readonly IUntrustDevicesCommand _untrustDevicesCommand;
|
||||||
private readonly IUserRepository _userRepositoryMock;
|
private readonly IUserRepository _userRepositoryMock;
|
||||||
private readonly ICurrentContext _currentContextMock;
|
private readonly ICurrentContext _currentContextMock;
|
||||||
private readonly IGlobalSettings _globalSettingsMock;
|
|
||||||
private readonly ILogger<DevicesController> _loggerMock;
|
private readonly ILogger<DevicesController> _loggerMock;
|
||||||
private readonly DevicesController _sut;
|
private readonly DevicesController _sut;
|
||||||
|
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
using Bit.Api.Utilities;
|
|
||||||
using Bit.Core.Models.Commands;
|
|
||||||
using Bit.Core.Vault.Entities;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Bit.Api.Test.Utilities;
|
|
||||||
|
|
||||||
public class CommandResultExtensionTests
|
|
||||||
{
|
|
||||||
public static IEnumerable<object[]> WithGenericTypeTestCases()
|
|
||||||
{
|
|
||||||
yield return new object[]
|
|
||||||
{
|
|
||||||
new NoRecordFoundFailure<Cipher>(new[] { "Error 1", "Error 2" }),
|
|
||||||
new ObjectResult(new[] { "Error 1", "Error 2" }) { StatusCode = StatusCodes.Status404NotFound }
|
|
||||||
};
|
|
||||||
yield return new object[]
|
|
||||||
{
|
|
||||||
new BadRequestFailure<Cipher>("Error 3"),
|
|
||||||
new ObjectResult(new[] { "Error 3" }) { StatusCode = StatusCodes.Status400BadRequest }
|
|
||||||
};
|
|
||||||
yield return new object[]
|
|
||||||
{
|
|
||||||
new Failure<Cipher>("Error 4"),
|
|
||||||
new ObjectResult(new[] { "Error 4" }) { StatusCode = StatusCodes.Status400BadRequest }
|
|
||||||
};
|
|
||||||
var cipher = new Cipher() { Id = Guid.NewGuid() };
|
|
||||||
|
|
||||||
yield return new object[]
|
|
||||||
{
|
|
||||||
new Success<Cipher>(cipher),
|
|
||||||
new ObjectResult(cipher) { StatusCode = StatusCodes.Status200OK }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(WithGenericTypeTestCases))]
|
|
||||||
public void MapToActionResult_WithGenericType_ShouldMapToHttpResponse(CommandResult<Cipher> input, ObjectResult expected)
|
|
||||||
{
|
|
||||||
var result = input.MapToActionResult();
|
|
||||||
|
|
||||||
Assert.Equivalent(expected, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MapToActionResult_WithGenericType_ShouldThrowExceptionForUnhandledCommandResult()
|
|
||||||
{
|
|
||||||
var result = new NotImplementedCommandResult();
|
|
||||||
|
|
||||||
Assert.Throws<InvalidOperationException>(() => result.MapToActionResult());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<object[]> TestCases()
|
|
||||||
{
|
|
||||||
yield return new object[]
|
|
||||||
{
|
|
||||||
new NoRecordFoundFailure(new[] { "Error 1", "Error 2" }),
|
|
||||||
new ObjectResult(new[] { "Error 1", "Error 2" }) { StatusCode = StatusCodes.Status404NotFound }
|
|
||||||
};
|
|
||||||
yield return new object[]
|
|
||||||
{
|
|
||||||
new BadRequestFailure("Error 3"),
|
|
||||||
new ObjectResult(new[] { "Error 3" }) { StatusCode = StatusCodes.Status400BadRequest }
|
|
||||||
};
|
|
||||||
yield return new object[]
|
|
||||||
{
|
|
||||||
new Failure("Error 4"),
|
|
||||||
new ObjectResult(new[] { "Error 4" }) { StatusCode = StatusCodes.Status400BadRequest }
|
|
||||||
};
|
|
||||||
yield return new object[]
|
|
||||||
{
|
|
||||||
new Success(),
|
|
||||||
new ObjectResult(new { }) { StatusCode = StatusCodes.Status200OK }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(TestCases))]
|
|
||||||
public void MapToActionResult_ShouldMapToHttpResponse(CommandResult input, ObjectResult expected)
|
|
||||||
{
|
|
||||||
var result = input.MapToActionResult();
|
|
||||||
|
|
||||||
Assert.Equivalent(expected, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MapToActionResult_ShouldThrowExceptionForUnhandledCommandResult()
|
|
||||||
{
|
|
||||||
var result = new NotImplementedCommandResult<Cipher>();
|
|
||||||
|
|
||||||
Assert.Throws<InvalidOperationException>(() => result.MapToActionResult());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NotImplementedCommandResult<T> : CommandResult<T>
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NotImplementedCommandResult : CommandResult
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -2,7 +2,6 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Errors;
|
|
||||||
using Bit.Core.AdminConsole.Models.Business;
|
using Bit.Core.AdminConsole.Models.Business;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||||
@ -11,12 +10,13 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.M
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Billing.Models.StaticStore.Plans;
|
using Bit.Core.Billing.Models.StaticStore.Plans;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Models.Commands;
|
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Models.StaticStore;
|
using Bit.Core.Models.StaticStore;
|
||||||
@ -80,7 +80,7 @@ public class InviteOrganizationUserCommandTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.IsType<Failure<ScimInviteOrganizationUsersResponse>>(result);
|
Assert.IsType<Failure<ScimInviteOrganizationUsersResponse>>(result);
|
||||||
Assert.Equal(NoUsersToInviteError.Code, (result as Failure<ScimInviteOrganizationUsersResponse>).ErrorMessage);
|
Assert.Equal(NoUsersToInviteError.Code, (result as Failure<ScimInviteOrganizationUsersResponse>)!.Error.Message);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IPaymentService>()
|
await sutProvider.GetDependency<IPaymentService>()
|
||||||
.DidNotReceiveWithAnyArgs()
|
.DidNotReceiveWithAnyArgs()
|
||||||
@ -209,7 +209,7 @@ public class InviteOrganizationUserCommandTests
|
|||||||
Assert.IsType<Failure<ScimInviteOrganizationUsersResponse>>(result);
|
Assert.IsType<Failure<ScimInviteOrganizationUsersResponse>>(result);
|
||||||
var failure = result as Failure<ScimInviteOrganizationUsersResponse>;
|
var failure = result as Failure<ScimInviteOrganizationUsersResponse>;
|
||||||
|
|
||||||
Assert.Equal(errorMessage, failure!.ErrorMessage);
|
Assert.Equal(errorMessage, failure!.Error.Message);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
.DidNotReceive()
|
.DidNotReceive()
|
||||||
@ -571,7 +571,7 @@ public class InviteOrganizationUserCommandTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.IsType<Failure<ScimInviteOrganizationUsersResponse>>(result);
|
Assert.IsType<Failure<ScimInviteOrganizationUsersResponse>>(result);
|
||||||
Assert.Equal(FailedToInviteUsersError.Code, (result as Failure<ScimInviteOrganizationUsersResponse>)!.ErrorMessage);
|
Assert.Equal(FailedToInviteUsersError.Code, (result as Failure<ScimInviteOrganizationUsersResponse>)!.Error.Message);
|
||||||
|
|
||||||
// org user revert
|
// org user revert
|
||||||
await orgUserRepository.Received(1).DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(x => x.Count() == 1));
|
await orgUserRepository.Received(1).DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(x => x.Count() == 1));
|
||||||
@ -677,7 +677,7 @@ public class InviteOrganizationUserCommandTests
|
|||||||
// Assert
|
// Assert
|
||||||
Assert.IsType<Success<ScimInviteOrganizationUsersResponse>>(result);
|
Assert.IsType<Success<ScimInviteOrganizationUsersResponse>>(result);
|
||||||
|
|
||||||
sutProvider.GetDependency<IMailService>().Received(1)
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||||
.SendOrganizationMaxSeatLimitReachedEmailAsync(organization, 2,
|
.SendOrganizationMaxSeatLimitReachedEmailAsync(organization, 2,
|
||||||
Arg.Is<IEnumerable<string>>(emails => emails.Any(email => email == "provider@email.com")));
|
Arg.Is<IEnumerable<string>>(emails => emails.Any(email => email == "provider@email.com")));
|
||||||
}
|
}
|
||||||
@ -768,7 +768,7 @@ public class InviteOrganizationUserCommandTests
|
|||||||
// Assert
|
// Assert
|
||||||
Assert.IsType<Success<ScimInviteOrganizationUsersResponse>>(result);
|
Assert.IsType<Success<ScimInviteOrganizationUsersResponse>>(result);
|
||||||
|
|
||||||
sutProvider.GetDependency<IMailService>().Received(1)
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||||
.SendOrganizationAutoscaledEmailAsync(organization, 1,
|
.SendOrganizationAutoscaledEmailAsync(organization, 1,
|
||||||
Arg.Is<IEnumerable<string>>(emails => emails.Any(email => email == "provider@email.com")));
|
Arg.Is<IEnumerable<string>>(emails => emails.Any(email => email == "provider@email.com")));
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using Bit.Core.AdminConsole.Models.Business;
|
using Bit.Core.AdminConsole.Models.Business;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Billing.Models.StaticStore.Plans;
|
using Bit.Core.Billing.Models.StaticStore.Plans;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
@ -61,7 +61,7 @@ public class InviteOrganizationUsersValidatorTests
|
|||||||
|
|
||||||
_ = await sutProvider.Sut.ValidateAsync(request);
|
_ = await sutProvider.Sut.ValidateAsync(request);
|
||||||
|
|
||||||
sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>()
|
await sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>()
|
||||||
.Received(1)
|
.Received(1)
|
||||||
.ValidateUpdateAsync(Arg.Is<SecretsManagerSubscriptionUpdate>(x =>
|
.ValidateUpdateAsync(Arg.Is<SecretsManagerSubscriptionUpdate>(x =>
|
||||||
x.SmSeatsChanged == true && x.SmSeats == 12));
|
x.SmSeatsChanged == true && x.SmSeats == 12));
|
||||||
@ -156,6 +156,6 @@ public class InviteOrganizationUsersValidatorTests
|
|||||||
var result = await sutProvider.Sut.ValidateAsync(request);
|
var result = await sutProvider.Sut.ValidateAsync(request);
|
||||||
|
|
||||||
Assert.IsType<Invalid<InviteOrganizationUsersValidationRequest>>(result);
|
Assert.IsType<Invalid<InviteOrganizationUsersValidationRequest>>(result);
|
||||||
Assert.Equal("Some Secrets Manager Failure", (result as Invalid<InviteOrganizationUsersValidationRequest>)!.ErrorMessageString);
|
Assert.Equal("Some Secrets Manager Failure", (result as Invalid<InviteOrganizationUsersValidationRequest>)!.Error.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Models.Business;
|
using Bit.Core.AdminConsole.Models.Business;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Billing.Models.StaticStore.Plans;
|
using Bit.Core.Billing.Models.StaticStore.Plans;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
@ -36,7 +36,7 @@ public class InviteUserOrganizationValidationTests
|
|||||||
var result = await sutProvider.Sut.ValidateAsync(inviteOrganization);
|
var result = await sutProvider.Sut.ValidateAsync(inviteOrganization);
|
||||||
|
|
||||||
Assert.IsType<Invalid<InviteOrganization>>(result);
|
Assert.IsType<Invalid<InviteOrganization>>(result);
|
||||||
Assert.Equal(OrganizationNoPaymentMethodFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString);
|
Assert.Equal(OrganizationNoPaymentMethodFoundError.Code, (result as Invalid<InviteOrganization>)!.Error.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -53,6 +53,6 @@ public class InviteUserOrganizationValidationTests
|
|||||||
var result = await sutProvider.Sut.ValidateAsync(inviteOrganization);
|
var result = await sutProvider.Sut.ValidateAsync(inviteOrganization);
|
||||||
|
|
||||||
Assert.IsType<Invalid<InviteOrganization>>(result);
|
Assert.IsType<Invalid<InviteOrganization>>(result);
|
||||||
Assert.Equal(OrganizationNoSubscriptionFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString);
|
Assert.Equal(OrganizationNoSubscriptionFoundError.Code, (result as Invalid<InviteOrganization>)!.Error.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ using Bit.Core.AdminConsole.Models.Business;
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Payments;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Payments;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Models.StaticStore.Plans;
|
using Bit.Core.Billing.Models.StaticStore.Plans;
|
||||||
@ -39,7 +39,7 @@ public class InviteUserPaymentValidationTests
|
|||||||
});
|
});
|
||||||
|
|
||||||
Assert.IsType<Invalid<PaymentsSubscription>>(result);
|
Assert.IsType<Invalid<PaymentsSubscription>>(result);
|
||||||
Assert.Equal(PaymentCancelledSubscriptionError.Code, (result as Invalid<PaymentsSubscription>)!.ErrorMessageString);
|
Assert.Equal(PaymentCancelledSubscriptionError.Code, (result as Invalid<PaymentsSubscription>)!.Error.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Models.Business;
|
using Bit.Core.AdminConsole.Models.Business;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Models.StaticStore.Plans;
|
using Bit.Core.Billing.Models.StaticStore.Plans;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
@ -67,7 +67,7 @@ public class InviteUsersPasswordManagerValidatorTests
|
|||||||
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
|
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
|
||||||
|
|
||||||
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
|
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
|
||||||
Assert.Equal(PasswordManagerSeatLimitHasBeenReachedError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.ErrorMessageString);
|
Assert.Equal(PasswordManagerSeatLimitHasBeenReachedError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.Error.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -88,6 +88,6 @@ public class InviteUsersPasswordManagerValidatorTests
|
|||||||
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
|
var result = await sutProvider.Sut.ValidateAsync(subscriptionUpdate);
|
||||||
|
|
||||||
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
|
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
|
||||||
Assert.Equal(PasswordManagerPlanDoesNotAllowAdditionalSeatsError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.ErrorMessageString);
|
Assert.Equal(PasswordManagerPlanDoesNotAllowAdditionalSeatsError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.Error.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
using Bit.Core.Auth.Entities;
|
using Bit.Core.Auth.Entities;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Models.Data;
|
using Bit.Core.Auth.Models.Data;
|
||||||
@ -11,7 +12,6 @@ using Bit.Core.Auth.Repositories;
|
|||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Commands;
|
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
@ -4,11 +4,11 @@ using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyValidators;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Commands;
|
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
@ -26,8 +26,8 @@ public class EventRouteServiceTests
|
|||||||
|
|
||||||
await Subject.CreateAsync(eventMessage);
|
await Subject.CreateAsync(eventMessage);
|
||||||
|
|
||||||
_broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any<EventMessage>());
|
await _broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any<EventMessage>());
|
||||||
_storageEventWriteService.Received(1).CreateAsync(eventMessage);
|
await _storageEventWriteService.Received(1).CreateAsync(eventMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@ -37,8 +37,8 @@ public class EventRouteServiceTests
|
|||||||
|
|
||||||
await Subject.CreateAsync(eventMessage);
|
await Subject.CreateAsync(eventMessage);
|
||||||
|
|
||||||
_broadcastEventWriteService.Received(1).CreateAsync(eventMessage);
|
await _broadcastEventWriteService.Received(1).CreateAsync(eventMessage);
|
||||||
_storageEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any<EventMessage>());
|
await _storageEventWriteService.DidNotReceiveWithAnyArgs().CreateAsync(Arg.Any<EventMessage>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@ -48,8 +48,8 @@ public class EventRouteServiceTests
|
|||||||
|
|
||||||
await Subject.CreateManyAsync(eventMessages);
|
await Subject.CreateManyAsync(eventMessages);
|
||||||
|
|
||||||
_broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any<IEnumerable<EventMessage>>());
|
await _broadcastEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any<IEnumerable<EventMessage>>());
|
||||||
_storageEventWriteService.Received(1).CreateManyAsync(eventMessages);
|
await _storageEventWriteService.Received(1).CreateManyAsync(eventMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@ -59,7 +59,7 @@ public class EventRouteServiceTests
|
|||||||
|
|
||||||
await Subject.CreateManyAsync(eventMessages);
|
await Subject.CreateManyAsync(eventMessages);
|
||||||
|
|
||||||
_broadcastEventWriteService.Received(1).CreateManyAsync(eventMessages);
|
await _broadcastEventWriteService.Received(1).CreateManyAsync(eventMessages);
|
||||||
_storageEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any<IEnumerable<EventMessage>>());
|
await _storageEventWriteService.DidNotReceiveWithAnyArgs().CreateManyAsync(Arg.Any<IEnumerable<EventMessage>>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ public class SlackEventHandlerTests
|
|||||||
var sutProvider = GetSutProvider(OneConfiguration());
|
var sutProvider = GetSutProvider(OneConfiguration());
|
||||||
|
|
||||||
await sutProvider.Sut.HandleEventAsync(eventMessage);
|
await sutProvider.Sut.HandleEventAsync(eventMessage);
|
||||||
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
|
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
|
||||||
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
|
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
|
||||||
Arg.Is(AssertHelper.AssertPropertyEqual(
|
Arg.Is(AssertHelper.AssertPropertyEqual(
|
||||||
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
|
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
|
||||||
@ -103,13 +103,13 @@ public class SlackEventHandlerTests
|
|||||||
var sutProvider = GetSutProvider(TwoConfigurations());
|
var sutProvider = GetSutProvider(TwoConfigurations());
|
||||||
|
|
||||||
await sutProvider.Sut.HandleEventAsync(eventMessage);
|
await sutProvider.Sut.HandleEventAsync(eventMessage);
|
||||||
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
|
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
|
||||||
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
|
Arg.Is(AssertHelper.AssertPropertyEqual(_token)),
|
||||||
Arg.Is(AssertHelper.AssertPropertyEqual(
|
Arg.Is(AssertHelper.AssertPropertyEqual(
|
||||||
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
|
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
|
||||||
Arg.Is(AssertHelper.AssertPropertyEqual(_channelId))
|
Arg.Is(AssertHelper.AssertPropertyEqual(_channelId))
|
||||||
);
|
);
|
||||||
sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
|
await sutProvider.GetDependency<ISlackService>().Received(1).SendSlackMessageByChannelIdAsync(
|
||||||
Arg.Is(AssertHelper.AssertPropertyEqual(_token2)),
|
Arg.Is(AssertHelper.AssertPropertyEqual(_token2)),
|
||||||
Arg.Is(AssertHelper.AssertPropertyEqual(
|
Arg.Is(AssertHelper.AssertPropertyEqual(
|
||||||
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
|
$"Date: {eventMessage.Date}, Type: {eventMessage.Type}, UserId: {eventMessage.UserId}")),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
using Bit.Core.AdminConsole.Shared.Validation;
|
using Bit.Core.AdminConsole.Utilities.Validation;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Core.Test.AdminConsole.Shared;
|
namespace Bit.Core.Test.AdminConsole.Shared;
|
||||||
@ -22,13 +22,11 @@ public class IValidatorTests
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value.Name))
|
if (string.IsNullOrWhiteSpace(value.Name))
|
||||||
{
|
{
|
||||||
return Task.FromResult<ValidationResult<TestClass>>(new Invalid<TestClass>
|
return Task.FromResult<ValidationResult<TestClass>>(
|
||||||
{
|
new Invalid<TestClass>(new InvalidRequestError<TestClass>(value)));
|
||||||
Errors = [new InvalidRequestError<TestClass>(value)]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult<ValidationResult<TestClass>>(new Valid<TestClass> { Value = value });
|
return Task.FromResult<ValidationResult<TestClass>>(new Valid<TestClass>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +39,7 @@ public class IValidatorTests
|
|||||||
|
|
||||||
Assert.IsType<Invalid<TestClass>>(result);
|
Assert.IsType<Invalid<TestClass>>(result);
|
||||||
var invalidResult = result as Invalid<TestClass>;
|
var invalidResult = result as Invalid<TestClass>;
|
||||||
Assert.Equal(InvalidRequestError<TestClass>.Code, invalidResult.Errors.First().Message);
|
Assert.Equal(InvalidRequestError<TestClass>.Code, invalidResult!.Error.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
using Bit.Core.AdminConsole.Errors;
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
using Bit.Core.Models.Commands;
|
using Bit.Core.AdminConsole.Utilities.Errors;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Core.Test.Models.Commands;
|
namespace Bit.Core.Test.AdminConsole.Utilities.Commands;
|
||||||
|
|
||||||
public class CommandResultTests
|
public class CommandResultTests
|
||||||
{
|
{
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<!-- Temp exclusions until warnings are fixed -->
|
|
||||||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS0672;CS1998</WarningsNotAsErrors>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -96,6 +96,7 @@ IBaseRequestValidatorTestWrapper
|
|||||||
return context.ValidatedTokenRequest.Subject ?? new ClaimsPrincipal();
|
return context.ValidatedTokenRequest.Subject ?? new ClaimsPrincipal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
protected override void SetErrorResult(
|
protected override void SetErrorResult(
|
||||||
BaseRequestValidationContextFake context,
|
BaseRequestValidationContextFake context,
|
||||||
Dictionary<string, object> customResponse)
|
Dictionary<string, object> customResponse)
|
||||||
@ -103,6 +104,7 @@ IBaseRequestValidatorTestWrapper
|
|||||||
context.GrantResult = new GrantValidationResult(TokenRequestErrors.InvalidGrant, customResponse: customResponse);
|
context.GrantResult = new GrantValidationResult(TokenRequestErrors.InvalidGrant, customResponse: customResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
protected override void SetSsoResult(
|
protected override void SetSsoResult(
|
||||||
BaseRequestValidationContextFake context,
|
BaseRequestValidationContextFake context,
|
||||||
Dictionary<string, object> customResponse)
|
Dictionary<string, object> customResponse)
|
||||||
@ -121,6 +123,7 @@ IBaseRequestValidatorTestWrapper
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
protected override void SetTwoFactorResult(
|
protected override void SetTwoFactorResult(
|
||||||
BaseRequestValidationContextFake context,
|
BaseRequestValidationContextFake context,
|
||||||
Dictionary<string, object> customResponse)
|
Dictionary<string, object> customResponse)
|
||||||
|
@ -56,9 +56,9 @@ public class UserManagerTestWrapper<TUser> : UserManager<TUser> where TUser : cl
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user"></param>
|
/// <param name="user"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<bool> GetTwoFactorEnabledAsync(TUser user)
|
public override Task<bool> GetTwoFactorEnabledAsync(TUser user)
|
||||||
{
|
{
|
||||||
return TWO_FACTOR_ENABLED;
|
return Task.FromResult(TWO_FACTOR_ENABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -66,9 +66,9 @@ public class UserManagerTestWrapper<TUser> : UserManager<TUser> where TUser : cl
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user"></param>
|
/// <param name="user"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<IList<string>> GetValidTwoFactorProvidersAsync(TUser user)
|
public override Task<IList<string>> GetValidTwoFactorProvidersAsync(TUser user)
|
||||||
{
|
{
|
||||||
return TWO_FACTOR_PROVIDERS;
|
return Task.FromResult(TWO_FACTOR_PROVIDERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -77,9 +77,9 @@ public class UserManagerTestWrapper<TUser> : UserManager<TUser> where TUser : cl
|
|||||||
/// <param name="user"></param>
|
/// <param name="user"></param>
|
||||||
/// <param name="tokenProvider"></param>
|
/// <param name="tokenProvider"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<string> GenerateTwoFactorTokenAsync(TUser user, string tokenProvider)
|
public override Task<string> GenerateTwoFactorTokenAsync(TUser user, string tokenProvider)
|
||||||
{
|
{
|
||||||
return TWO_FACTOR_TOKEN;
|
return Task.FromResult(TWO_FACTOR_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -89,8 +89,8 @@ public class UserManagerTestWrapper<TUser> : UserManager<TUser> where TUser : cl
|
|||||||
/// <param name="tokenProvider"></param>
|
/// <param name="tokenProvider"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<bool> VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token)
|
public override Task<bool> VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token)
|
||||||
{
|
{
|
||||||
return TWO_FACTOR_TOKEN_VERIFIED;
|
return Task.FromResult(TWO_FACTOR_TOKEN_VERIFIED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user