Skip to content

Commit

Permalink
Merge branch 'main' into billing/PM-11516/license-refactor
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/Core/Constants.cs
  • Loading branch information
cturnbull-bitwarden committed Dec 5, 2024
2 parents ee8763f + 0e32dcc commit 9bf7d98
Show file tree
Hide file tree
Showing 80 changed files with 2,400 additions and 1,436 deletions.
5 changes: 5 additions & 0 deletions .devcontainer/internal_dev/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@
}
},
"postCreateCommand": "bash .devcontainer/internal_dev/postCreateCommand.sh",
"forwardPorts": [1080, 1433],
"portsAttributes": {
"1080": {
"label": "Mail Catcher",
"onAutoForward": "notify"
},
"1433": {
"label": "SQL Server",
"onAutoForward": "notify"
}
}
}
16 changes: 16 additions & 0 deletions .devcontainer/internal_dev/postCreateCommand.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,22 @@ Press <Enter> to continue."
sleep 5 # wait for DB container to start
dotnet run --project ./util/MsSqlMigratorUtility "$SQL_CONNECTION_STRING"
fi
read -r -p "Would you like to install the Stripe CLI? [y/N] " stripe_response
if [[ "$stripe_response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
install_stripe_cli
fi
}

# Install Stripe CLI
install_stripe_cli() {
echo "Installing Stripe CLI..."
# Add Stripe CLI GPG key so that apt can verify the packages authenticity.
# If Stripe ever changes the key, we'll need to update this. Visit https://docs.stripe.com/stripe-cli?install-method=apt if so
curl -s https://packages.stripe.dev/api/security/keypair/stripe-cli-gpg/public | gpg --dearmor | sudo tee /usr/share/keyrings/stripe.gpg >/dev/null
# Add Stripe CLI repository to apt sources
echo "deb [signed-by=/usr/share/keyrings/stripe.gpg] https://packages.stripe.dev/stripe-cli-debian-local stable main" | sudo tee -a /etc/apt/sources.list.d/stripe.list >/dev/null
sudo apt update
sudo apt install -y stripe
}

# main
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,9 @@ jobs:

self-host-build:
name: Trigger self-host build
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
if: |
github.event_name != 'pull_request_target'
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
runs-on: ubuntu-22.04
needs:
- build-docker
Expand Down
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>

<Version>2024.11.0</Version>
<Version>2024.12.0</Version>

<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down Expand Up @@ -64,4 +64,4 @@
</ItemGroup>
</Target>

</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ namespace Bit.Commercial.Core.AdminConsole.Services;

public class ProviderService : IProviderService
{
public static PlanType[] ProviderDisallowedOrganizationTypes = new[] { PlanType.Free, PlanType.FamiliesAnnually, PlanType.FamiliesAnnually2019 };
private static readonly PlanType[] _resellerDisallowedOrganizationTypes = [
PlanType.Free,
PlanType.FamiliesAnnually,
PlanType.FamiliesAnnually2019
];

private readonly IDataProtector _dataProtector;
private readonly IMailService _mailService;
Expand Down Expand Up @@ -690,13 +694,14 @@ private void ThrowOnInvalidPlanType(ProviderType providerType, PlanType requeste
throw new BadRequestException($"Multi-organization Enterprise Providers cannot manage organizations with the plan type {requestedType}. Only Enterprise (Monthly) and Enterprise (Annually) are allowed.");
}
break;
case ProviderType.Reseller:
if (_resellerDisallowedOrganizationTypes.Contains(requestedType))
{
throw new BadRequestException($"Providers cannot manage organizations with the requested plan type ({requestedType}). Only Teams and Enterprise accounts are allowed.");
}
break;
default:
throw new BadRequestException($"Unsupported provider type {providerType}.");
}

if (ProviderDisallowedOrganizationTypes.Contains(requestedType))
{
throw new BadRequestException($"Providers cannot manage organizations with the requested plan type ({requestedType}). Only Teams and Enterprise accounts are allowed.");
}
}
}
2 changes: 1 addition & 1 deletion src/Admin/AdminConsole/Models/OrganizationEditModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public OrganizationEditModel(
[Display(Name = "SCIM")]
public bool UseScim { get; set; }
[Display(Name = "Secrets Manager")]
public bool UseSecretsManager { get; set; }
public new bool UseSecretsManager { get; set; }
[Display(Name = "Self Host")]
public bool SelfHost { get; set; }
[Display(Name = "Users Get Premium")]
Expand Down
10 changes: 3 additions & 7 deletions src/Admin/Models/UserEditModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ namespace Bit.Admin.Models;

public class UserEditModel
{
public UserEditModel()
{

}
public UserEditModel() { }

public UserEditModel(
User user,
Expand All @@ -21,10 +18,9 @@ public UserEditModel(
BillingInfo billingInfo,
BillingHistoryInfo billingHistoryInfo,
GlobalSettings globalSettings,
bool? domainVerified
)
bool? claimedAccount)
{
User = UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, domainVerified);
User = UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, claimedAccount);

BillingInfo = billingInfo;
BillingHistoryInfo = billingHistoryInfo;
Expand Down
14 changes: 7 additions & 7 deletions src/Admin/Models/UserViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class UserViewModel
public bool Premium { get; }
public short? MaxStorageGb { get; }
public bool EmailVerified { get; }
public bool? DomainVerified { get; }
public bool? ClaimedAccount { get; }
public bool TwoFactorEnabled { get; }
public DateTime AccountRevisionDate { get; }
public DateTime RevisionDate { get; }
Expand All @@ -36,7 +36,7 @@ public UserViewModel(Guid id,
bool premium,
short? maxStorageGb,
bool emailVerified,
bool? domainVerified,
bool? claimedAccount,
bool twoFactorEnabled,
DateTime accountRevisionDate,
DateTime revisionDate,
Expand All @@ -58,7 +58,7 @@ public UserViewModel(Guid id,
Premium = premium;
MaxStorageGb = maxStorageGb;
EmailVerified = emailVerified;
DomainVerified = domainVerified;
ClaimedAccount = claimedAccount;
TwoFactorEnabled = twoFactorEnabled;
AccountRevisionDate = accountRevisionDate;
RevisionDate = revisionDate;
Expand All @@ -79,7 +79,7 @@ public static IEnumerable<UserViewModel> MapViewModels(
users.Select(user => MapViewModel(user, lookup, false));

public static UserViewModel MapViewModel(User user,
IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup, bool? domainVerified) =>
IEnumerable<(Guid userId, bool twoFactorIsEnabled)> lookup, bool? claimedAccount) =>
new(
user.Id,
user.Name,
Expand All @@ -89,7 +89,7 @@ public static UserViewModel MapViewModel(User user,
user.Premium,
user.MaxStorageGb,
user.EmailVerified,
domainVerified,
claimedAccount,
IsTwoFactorEnabled(user, lookup),
user.AccountRevisionDate,
user.RevisionDate,
Expand All @@ -106,7 +106,7 @@ public static UserViewModel MapViewModel(User user,
public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled) =>
MapViewModel(user, isTwoFactorEnabled, Array.Empty<Cipher>(), false);

public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled, IEnumerable<Cipher> ciphers, bool? domainVerified) =>
public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled, IEnumerable<Cipher> ciphers, bool? claimedAccount) =>
new(
user.Id,
user.Name,
Expand All @@ -116,7 +116,7 @@ public static UserViewModel MapViewModel(User user, bool isTwoFactorEnabled, IEn
user.Premium,
user.MaxStorageGb,
user.EmailVerified,
domainVerified,
claimedAccount,
isTwoFactorEnabled,
user.AccountRevisionDate,
user.RevisionDate,
Expand Down
7 changes: 4 additions & 3 deletions src/Admin/Views/Users/_ViewInformation.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
<dt class="col-sm-4 col-lg-3">Email Verified</dt>
<dd class="col-sm-8 col-lg-9">@(Model.EmailVerified ? "Yes" : "No")</dd>

@if(Model.DomainVerified.HasValue){
<dt class="col-sm-4 col-lg-3">Domain Verified</dt>
<dd class="col-sm-8 col-lg-9">@(Model.DomainVerified.Value == true ? "Yes" : "No")</dd>
@if(Model.ClaimedAccount.HasValue)
{
<dt class="col-sm-4 col-lg-3">Claimed Account</dt>
<dd class="col-sm-8 col-lg-9">@(Model.ClaimedAccount.Value ? "Yes" : "No")</dd>
}

<dt class="col-sm-4 col-lg-3">Using 2FA</dt>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRemo
var userId = _userService.GetProperUserId(User);
var result = await _removeOrganizationUserCommand.RemoveUsersAsync(orgId, model.Ids, userId.Value);
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.ErrorMessage)));
}

[RequireFeature(FeatureFlagKeys.AccountDeprovisioning)]
Expand Down
29 changes: 12 additions & 17 deletions src/Api/AdminConsole/Controllers/PoliciesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Context;
using Bit.Core.Enums;
Expand All @@ -28,7 +28,6 @@ namespace Bit.Api.AdminConsole.Controllers;
public class PoliciesController : Controller
{
private readonly IPolicyRepository _policyRepository;
private readonly IPolicyService _policyService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
Expand All @@ -37,21 +36,21 @@ public class PoliciesController : Controller
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
private readonly IFeatureService _featureService;
private readonly IOrganizationHasVerifiedDomainsQuery _organizationHasVerifiedDomainsQuery;
private readonly ISavePolicyCommand _savePolicyCommand;

public PoliciesController(
IPolicyRepository policyRepository,
IPolicyService policyService,
IOrganizationUserRepository organizationUserRepository,
IUserService userService,
ICurrentContext currentContext,
GlobalSettings globalSettings,
IDataProtectionProvider dataProtectionProvider,
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
IFeatureService featureService,
IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery)
IOrganizationHasVerifiedDomainsQuery organizationHasVerifiedDomainsQuery,
ISavePolicyCommand savePolicyCommand)
{
_policyRepository = policyRepository;
_policyService = policyService;
_organizationUserRepository = organizationUserRepository;
_userService = userService;
_currentContext = currentContext;
Expand All @@ -62,6 +61,7 @@ public PoliciesController(
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
_featureService = featureService;
_organizationHasVerifiedDomainsQuery = organizationHasVerifiedDomainsQuery;
_savePolicyCommand = savePolicyCommand;
}

[HttpGet("{type}")]
Expand Down Expand Up @@ -178,25 +178,20 @@ public async Task<PolicyResponseModel> GetMasterPasswordPolicy(Guid orgId)
}

[HttpPut("{type}")]
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody] PolicyRequestModel model)
public async Task<PolicyResponseModel> Put(Guid orgId, PolicyType type, [FromBody] PolicyRequestModel model)
{
var orgIdGuid = new Guid(orgId);
if (!await _currentContext.ManagePolicies(orgIdGuid))
if (!await _currentContext.ManagePolicies(orgId))
{
throw new NotFoundException();
}
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(new Guid(orgId), (PolicyType)type);
if (policy == null)
{
policy = model.ToPolicy(orgIdGuid);
}
else

if (type != model.Type)
{
policy = model.ToPolicy(policy);
throw new BadRequestException("Mismatched policy type");
}

var userId = _userService.GetProperUserId(User);
await _policyService.SaveAsync(policy, userId);
var policyUpdate = await model.ToPolicyUpdateAsync(orgId, _currentContext);
var policy = await _savePolicyCommand.SaveAsync(policyUpdate);
return new PolicyResponseModel(policy);
}
}
25 changes: 10 additions & 15 deletions src/Api/AdminConsole/Models/Request/PolicyRequestModel.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
using Bit.Core.Context;

namespace Bit.Api.AdminConsole.Models.Request;

Expand All @@ -13,19 +15,12 @@ public class PolicyRequestModel
public bool? Enabled { get; set; }
public Dictionary<string, object> Data { get; set; }

public Policy ToPolicy(Guid orgId)
public async Task<PolicyUpdate> ToPolicyUpdateAsync(Guid organizationId, ICurrentContext currentContext) => new()
{
return ToPolicy(new Policy
{
Type = Type.Value,
OrganizationId = orgId
});
}

public Policy ToPolicy(Policy existingPolicy)
{
existingPolicy.Enabled = Enabled.GetValueOrDefault();
existingPolicy.Data = Data != null ? JsonSerializer.Serialize(Data) : null;
return existingPolicy;
}
Type = Type!.Value,
OrganizationId = organizationId,
Data = Data != null ? JsonSerializer.Serialize(Data) : null,
Enabled = Enabled.GetValueOrDefault(),
PerformedBy = new StandardUser(currentContext.UserId!.Value, await currentContext.OrganizationOwner(organizationId))
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Net;
using Bit.Api.AdminConsole.Public.Models.Request;
using Bit.Api.AdminConsole.Public.Models.Response;
using Bit.Api.Models.Public.Response;
using Bit.Core.Context;
using Bit.Core.Enums;
Expand Down Expand Up @@ -38,7 +37,7 @@ public OrganizationController(
/// </remarks>
/// <param name="model">The request model.</param>
[HttpPost("import")]
[ProducesResponseType(typeof(MemberResponseModel), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(OkResult), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Import([FromBody] OrganizationImportRequestModel model)
{
Expand Down
Loading

0 comments on commit 9bf7d98

Please sign in to comment.