1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 08:02:49 -05:00

Move request/response models (#1754)

This commit is contained in:
Oscar Hinton
2021-12-14 15:05:07 +00:00
committed by GitHub
parent 3ae573bd8d
commit 63f6dd9a24
206 changed files with 641 additions and 516 deletions

View File

@ -2,8 +2,6 @@
using Bit.Core;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Api;
using Bit.Core.Models.Api.Request.Accounts;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
@ -17,6 +15,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Request;
using Bit.Api.Models.Request.Accounts;
using Bit.Api.Models.Response;
using Bit.Core.Enums.Provider;
namespace Bit.Api.Controllers

View File

@ -4,7 +4,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Context;
@ -15,6 +14,10 @@ using Bit.Core.Models.Table;
using Bit.Core.Settings;
using Core.Models.Data;
using Azure.Messaging.EventGrid;
using Bit.Api.Models.Request;
using Bit.Api.Models.Request.Accounts;
using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response;
using Bit.Core.Models.Data;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
@ -600,8 +603,8 @@ namespace Bit.Api.Controllers
throw new BadRequestException($"Max file size is {CipherService.MAX_FILE_SIZE_READABLE}.");
}
var (attachmentId, uploadUrl) = await _cipherService.CreateAttachmentForDelayedUploadAsync(cipher, request, userId);
var (attachmentId, uploadUrl) = await _cipherService.CreateAttachmentForDelayedUploadAsync(cipher,
request.Key, request.FileName, request.FileSize, request.AdminRequest, userId);
return new AttachmentUploadDataResponseModel
{
AttachmentId = attachmentId,
@ -713,7 +716,8 @@ namespace Bit.Api.Controllers
{
var userId = _userService.GetProperUserId(User).Value;
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
return await _cipherService.GetAttachmentDownloadDataAsync(cipher, attachmentId);
var result = await _cipherService.GetAttachmentDownloadDataAsync(cipher, attachmentId);
return new AttachmentResponseModel(result.Id, result.Data, result.Cipher, _globalSettings);
}
[HttpPost("{id}/attachment/{attachmentId}/share")]

View File

@ -4,12 +4,13 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Context;
using Bit.Core.Models.Table;
using System.Collections.Generic;
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
namespace Bit.Api.Controllers
{

View File

@ -2,10 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Models.Table;
using Bit.Core.Services;

View File

@ -1,13 +1,14 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Request;
using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response;
using Bit.Core.Exceptions;
using Bit.Core.Models.Api;
using Bit.Core.Models.Api.Request;
using Bit.Core.Models.Api.Response;
using Bit.Core.Models.Table;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -20,15 +21,18 @@ namespace Bit.Api.Controllers
private readonly IUserService _userService;
private readonly IEmergencyAccessRepository _emergencyAccessRepository;
private readonly IEmergencyAccessService _emergencyAccessService;
private readonly IGlobalSettings _globalSettings;
public EmergencyAccessController(
IUserService userService,
IEmergencyAccessRepository emergencyAccessRepository,
IEmergencyAccessService emergencyAccessService)
IEmergencyAccessService emergencyAccessService,
IGlobalSettings globalSettings)
{
_userService = userService;
_emergencyAccessRepository = emergencyAccessRepository;
_emergencyAccessService = emergencyAccessService;
_globalSettings = globalSettings;
}
[HttpGet("trusted")]
@ -161,14 +165,17 @@ namespace Bit.Api.Controllers
public async Task<EmergencyAccessViewResponseModel> ViewCiphers(string id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
return await _emergencyAccessService.ViewAsync(new Guid(id), user);
var viewResult = await _emergencyAccessService.ViewAsync(new Guid(id), user);
return new EmergencyAccessViewResponseModel(_globalSettings, viewResult.EmergencyAccess, viewResult.Ciphers);
}
[HttpGet("{id}/{cipherId}/attachment/{attachmentId}")]
public async Task<AttachmentResponseModel> GetAttachmentData(string id, string cipherId, string attachmentId)
{
var user = await _userService.GetUserByPrincipalAsync(User);
return await _emergencyAccessService.GetAttachmentDownloadAsync(new Guid(id), cipherId, attachmentId, user);
var result =
await _emergencyAccessService.GetAttachmentDownloadAsync(new Guid(id), cipherId, attachmentId, user);
return new AttachmentResponseModel(result.Id, result.Data, result.Cipher, _globalSettings);
}
}
}

View File

@ -1,11 +1,11 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Response;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Exceptions;
using Bit.Core.Models.Api;
using Bit.Core.Services;
using Bit.Core.Context;
using Bit.Core.Models.Data;

View File

@ -1,10 +1,11 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;

View File

@ -4,11 +4,12 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Context;
using System.Collections.Generic;
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
namespace Bit.Api.Controllers
{

View File

@ -1,8 +1,9 @@
using System;
using System.Threading.Tasks;
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Bit.Core.Models.Api;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;

View File

@ -1,12 +1,10 @@
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Models.Api;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Bit.Api.Models.Request;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Settings;
using Stripe;
using System.Linq;
using System.Collections.Generic;
namespace Bit.Api.Controllers
{

View File

@ -1,9 +1,8 @@
using System;
using System.Threading.Tasks;
using Bit.Api.Models.Request.Organizations;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Models.Api;
using Bit.Core.Models.Api.Request;
using Bit.Core.Models.Table;
using Bit.Core.Repositories;
using Bit.Core.Services;

View File

@ -4,11 +4,12 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Context;
using System.Collections.Generic;
using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response;
using Bit.Core.Enums;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
@ -136,7 +137,7 @@ namespace Bit.Api.Controllers
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.InviteUsersAsync(orgGuidId, userId.Value,
new (OrganizationUserInvite, string)[] { (new OrganizationUserInvite(model), null) });
new (OrganizationUserInvite, string)[] { (new OrganizationUserInvite(model.ToData()), null) });
}
[HttpPost("reinvite")]

View File

@ -1,16 +1,18 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Request;
using Bit.Api.Models.Request.Accounts;
using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Enums;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Context;
using Bit.Api.Utilities;
using Bit.Core.Models.Api.Response;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;

View File

@ -1,10 +1,10 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Utilities;
using Bit.Core.Models.Api;
using System.Linq;
using Bit.Core.Repositories;
using System.Threading.Tasks;
using Bit.Api.Models.Response;
namespace Bit.Api.Controllers
{

View File

@ -1,10 +1,11 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Context;

View File

@ -1,10 +1,11 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Request.Providers;
using Bit.Api.Models.Response;
using Bit.Api.Models.Response.Providers;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Models.Api;
using Bit.Core.Models.Api.Request;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Utilities;

View File

@ -2,10 +2,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Api.Models.Request.Providers;
using Bit.Api.Models.Response;
using Bit.Api.Models.Response.Providers;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Context;

View File

@ -1,8 +1,9 @@
using System;
using System.Threading.Tasks;
using Bit.Api.Models.Request.Providers;
using Bit.Api.Models.Response.Providers;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Models.Api;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;

View File

@ -1,14 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Bit.Core;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Models.Api;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Bit.Api.Utilities;
using Bit.Core.Models.Api;
using Bit.Core.Utilities;
using Bit.Core.Settings;
using Microsoft.Extensions.Hosting;

View File

@ -4,17 +4,17 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.Core.Settings;
using Bit.Core.Models.Api.Response;
using Bit.Core.Enums;
using Bit.Core.Context;
using Azure.Messaging.EventGrid;
using Bit.Api.Utilities;
using System.Collections.Generic;
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
using Bit.Core.Models.Table;
using Newtonsoft.Json;
using Bit.Core.Models.Data;

View File

@ -1,8 +1,9 @@
using System;
using System.Threading.Tasks;
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Models.Api;
using Bit.Core.Services;
namespace Bit.Api.Controllers

View File

@ -2,7 +2,6 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Models.Api;
using Bit.Core.Services;
using Bit.Core.Repositories;
using Bit.Core.Enums;
@ -10,6 +9,7 @@ using Bit.Core.Exceptions;
using System.Linq;
using Bit.Core.Models.Table;
using System.Collections.Generic;
using Bit.Api.Models.Response;
using Bit.Core.Enums.Provider;
using Bit.Core.Models.Data;
using Bit.Core.Settings;

View File

@ -2,13 +2,16 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Models.Api;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Microsoft.AspNetCore.Identity;
using Bit.Core.Models.Table;
using Bit.Core.Enums;
using System.Linq;
using Bit.Api.Models.Request;
using Bit.Api.Models.Request.Accounts;
using Bit.Api.Models.Response;
using Bit.Api.Models.Response.TwoFactor;
using Bit.Core.Context;
using Bit.Core.Repositories;
using Bit.Core.Utilities;

View File

@ -1,10 +1,10 @@
using System;
using System.Threading.Tasks;
using Bit.Api.Models.Response;
using Microsoft.AspNetCore.Mvc;
using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Bit.Core.Exceptions;
using Bit.Core.Models.Api;
namespace Bit.Api.Controllers
{

View File

@ -0,0 +1,21 @@
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
{
public class CipherAttachmentModel
{
public CipherAttachmentModel() { }
public CipherAttachmentModel(CipherAttachment.MetaData data)
{
FileName = data.FileName;
Key = data.Key;
}
[EncryptedStringLength(1000)]
public string FileName { get; set; }
[EncryptedStringLength(1000)]
public string Key { get; set; }
}
}

View File

@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
{
public class CipherCardModel
{
public CipherCardModel() { }
public CipherCardModel(CipherCardData data)
{
CardholderName = data.CardholderName;
Brand = data.Brand;
Number = data.Number;
ExpMonth = data.ExpMonth;
ExpYear = data.ExpYear;
Code = data.Code;
}
[EncryptedString]
[EncryptedStringLength(1000)]
public string CardholderName { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Brand { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Number { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string ExpMonth { get; set; }
[EncryptedString]
[StringLength(1000)]
public string ExpYear { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Code { get; set; }
}
}

View File

@ -0,0 +1,37 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
{
public class CipherFieldModel
{
public CipherFieldModel() { }
public CipherFieldModel(CipherFieldData data)
{
Type = data.Type;
Name = data.Name;
Value = data.Value;
LinkedId = data.LinkedId ?? null;
}
public FieldType Type { get; set; }
[EncryptedStringLength(1000)]
public string Name { get; set; }
[EncryptedStringLength(5000)]
public string Value { get; set; }
public int? LinkedId { get; set; }
public CipherFieldData ToCipherFieldData()
{
return new CipherFieldData
{
Type = Type,
Name = Name,
Value = Value,
LinkedId = LinkedId ?? null,
};
}
}
}

View File

@ -0,0 +1,88 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
{
public class CipherIdentityModel
{
public CipherIdentityModel() { }
public CipherIdentityModel(CipherIdentityData data)
{
Title = data.Title;
FirstName = data.FirstName;
MiddleName = data.MiddleName;
LastName = data.LastName;
Address1 = data.Address1;
Address2 = data.Address2;
Address3 = data.Address3;
City = data.City;
State = data.State;
PostalCode = data.PostalCode;
Country = data.Country;
Company = data.Company;
Email = data.Email;
Phone = data.Phone;
SSN = data.SSN;
Username = data.Username;
PassportNumber = data.PassportNumber;
LicenseNumber = data.LicenseNumber;
}
[EncryptedString]
[EncryptedStringLength(1000)]
public string Title { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string FirstName { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string MiddleName { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string LastName { get; set; }
[EncryptedString]
[StringLength(1000)]
public string Address1 { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Address2 { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Address3 { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string City { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string State { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string PostalCode { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Country { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Company { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Email { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Phone { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string SSN { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Username { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string PassportNumber { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string LicenseNumber { get; set; }
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
{
public class CipherLoginModel
{
public CipherLoginModel() { }
public CipherLoginModel(CipherLoginData data)
{
Uris = data.Uris?.Select(u => new CipherLoginUriModel(u))?.ToList();
if (!Uris?.Any() ?? true)
{
Uri = data.Uri;
}
Username = data.Username;
Password = data.Password;
PasswordRevisionDate = data.PasswordRevisionDate;
Totp = data.Totp;
AutofillOnPageLoad = data.AutofillOnPageLoad;
}
[EncryptedString]
[EncryptedStringLength(10000)]
public string Uri
{
get => Uris?.FirstOrDefault()?.Uri;
set
{
if (string.IsNullOrWhiteSpace(value))
{
return;
}
if (Uris == null)
{
Uris = new List<CipherLoginUriModel>();
}
Uris.Add(new CipherLoginUriModel(value));
}
}
public List<CipherLoginUriModel> Uris { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Username { get; set; }
[EncryptedString]
[EncryptedStringLength(5000)]
public string Password { get; set; }
public DateTime? PasswordRevisionDate { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Totp { get; set; }
public bool? AutofillOnPageLoad { get; set; }
public class CipherLoginUriModel
{
public CipherLoginUriModel() { }
public CipherLoginUriModel(string uri)
{
Uri = uri;
}
public CipherLoginUriModel(CipherLoginData.CipherLoginUriData uri)
{
Uri = uri.Uri;
Match = uri.Match;
}
[EncryptedString]
[EncryptedStringLength(10000)]
public string Uri { get; set; }
public UriMatchType? Match { get; set; } = null;
public CipherLoginData.CipherLoginUriData ToCipherLoginUriData()
{
return new CipherLoginData.CipherLoginUriData { Uri = Uri, Match = Match, };
}
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
{
public class CipherPasswordHistoryModel
{
public CipherPasswordHistoryModel() { }
public CipherPasswordHistoryModel(CipherPasswordHistoryData data)
{
Password = data.Password;
LastUsedDate = data.LastUsedDate;
}
[EncryptedString]
[EncryptedStringLength(5000)]
[Required]
public string Password { get; set; }
[Required]
public DateTime? LastUsedDate { get; set; }
public CipherPasswordHistoryData ToCipherPasswordHistoryData()
{
return new CipherPasswordHistoryData { Password = Password, LastUsedDate = LastUsedDate.Value, };
}
}
}

View File

@ -0,0 +1,17 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
namespace Bit.Api.Models
{
public class CipherSecureNoteModel
{
public CipherSecureNoteModel() { }
public CipherSecureNoteModel(CipherSecureNoteData data)
{
Type = data.Type;
}
public SecureNoteType Type { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public
{
public abstract class AssociationWithPermissionsBaseModel
{
/// <summary>
/// The associated object's unique identifier.
/// </summary>
/// <example>bfbc8338-e329-4dc0-b0c9-317c2ebf1a09</example>
[Required]
public Guid? Id { get; set; }
/// <summary>
/// When true, the read only permission will not allow the user or group to make changes to items.
/// </summary>
[Required]
public bool? ReadOnly { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public
{
public abstract class CollectionBaseModel
{
/// <summary>
/// External identifier for reference or linking this collection to another system.
/// </summary>
/// <example>external_id_123456</example>
[StringLength(300)]
public string ExternalId { get; set; }
}
}

View File

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public
{
public abstract class GroupBaseModel
{
/// <summary>
/// The name of the group.
/// </summary>
/// <example>Development Team</example>
[Required]
[StringLength(100)]
public string Name { get; set; }
/// <summary>
/// Determines if this group can access all collections within the organization, or only the associated
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
/// </summary>
[Required]
public bool? AccessAll { get; set; }
/// <summary>
/// External identifier for reference or linking this group to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[StringLength(300)]
public string ExternalId { get; set; }
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Public
{
public abstract class MemberBaseModel
{
public MemberBaseModel() { }
public MemberBaseModel(OrganizationUser user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Type = user.Type;
AccessAll = user.AccessAll;
ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null;
}
public MemberBaseModel(OrganizationUserUserDetails user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Type = user.Type;
AccessAll = user.AccessAll;
ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null;
}
/// <summary>
/// The member's type (or role) within the organization.
/// </summary>
[Required]
public OrganizationUserType? Type { get; set; }
/// <summary>
/// Determines if this member can access all collections within the organization, or only the associated
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
/// </summary>
[Required]
public bool? AccessAll { get; set; }
/// <summary>
/// External identifier for reference or linking this member to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[StringLength(300)]
public string ExternalId { get; set; }
/// <summary>
/// Returns <c>true</c> if the member has enrolled in Password Reset assistance within the organization
/// </summary>
[Required]
public bool ResetPasswordEnrolled { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public
{
public abstract class PolicyBaseModel
{
/// <summary>
/// Determines if this policy is enabled and enforced.
/// </summary>
[Required]
public bool? Enabled { get; set; }
/// <summary>
/// Data for the policy.
/// </summary>
public Dictionary<string, object> Data { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using Bit.Core.Models.Data;
namespace Bit.Api.Models.Public.Request
{
public class AssociationWithPermissionsRequestModel : AssociationWithPermissionsBaseModel
{
public SelectionReadOnly ToSelectionReadOnly()
{
return new SelectionReadOnly
{
Id = Id.Value,
ReadOnly = ReadOnly.Value
};
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Public.Request
{
public class CollectionUpdateRequestModel : CollectionBaseModel
{
/// <summary>
/// The associated groups that this collection is assigned to.
/// </summary>
public IEnumerable<AssociationWithPermissionsRequestModel> Groups { get; set; }
public Collection ToCollection(Collection existingCollection)
{
existingCollection.ExternalId = ExternalId;
return existingCollection;
}
}
}

View File

@ -0,0 +1,51 @@
using System;
using Bit.Core.Exceptions;
namespace Bit.Api.Models.Public.Request
{
public class EventFilterRequestModel
{
/// <summary>
/// The start date. Must be less than the end date.
/// </summary>
public DateTime? Start { get; set; }
/// <summary>
/// The end date. Must be greater than the start date.
/// </summary>
public DateTime? End { get; set; }
/// <summary>
/// The unique identifier of the user that performed the event.
/// </summary>
public Guid? ActingUserId { get; set; }
/// <summary>
/// The unique identifier of the related item that the event describes.
/// </summary>
public Guid? ItemId { get; set; }
/// <summary>
/// A cursor for use in pagination.
/// </summary>
public string ContinuationToken { get; set; }
public Tuple<DateTime, DateTime> ToDateRange()
{
if (!End.HasValue || !Start.HasValue)
{
End = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
Start = DateTime.UtcNow.Date.AddDays(-30);
}
else if (Start.Value > End.Value)
{
var newEnd = Start;
Start = End;
End = newEnd;
}
if ((End.Value - Start.Value) > TimeSpan.FromDays(367))
{
throw new BadRequestException("Date range must be < 367 days.");
}
return new Tuple<DateTime, DateTime>(Start.Value, End.Value);
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Public.Request
{
public class GroupCreateUpdateRequestModel : GroupBaseModel
{
/// <summary>
/// The associated collections that this group can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsRequestModel> Collections { get; set; }
public Group ToGroup(Guid orgId)
{
return ToGroup(new Group
{
OrganizationId = orgId
});
}
public Group ToGroup(Group existingGroup)
{
existingGroup.Name = Name;
existingGroup.AccessAll = AccessAll.Value;
existingGroup.ExternalId = ExternalId;
return existingGroup;
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Public.Request
{
public class MemberCreateRequestModel : MemberUpdateRequestModel
{
/// <summary>
/// The member's email address.
/// </summary>
/// <example>jsmith@example.com</example>
[Required]
[StringLength(256)]
[StrictEmailAddress]
public string Email { get; set; }
public override OrganizationUser ToOrganizationUser(OrganizationUser existingUser)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Public.Request
{
public class MemberUpdateRequestModel : MemberBaseModel
{
/// <summary>
/// The associated collections that this member can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsRequestModel> Collections { get; set; }
public virtual OrganizationUser ToOrganizationUser(OrganizationUser existingUser)
{
existingUser.Type = Type.Value;
existingUser.AccessAll = AccessAll.Value;
existingUser.ExternalId = ExternalId;
return existingUser;
}
}
}

View File

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Business;
using Table = Bit.Core.Models.Table;
namespace Bit.Api.Models.Public.Request
{
public class OrganizationImportRequestModel
{
/// <summary>
/// Groups to import.
/// </summary>
public OrganizationImportGroupRequestModel[] Groups { get; set; }
/// <summary>
/// Members to import.
/// </summary>
public OrganizationImportMemberRequestModel[] Members { get; set; }
/// <summary>
/// Determines if the data in this request should overwrite or append to the existing organization data.
/// </summary>
[Required]
public bool? OverwriteExisting { get; set; }
/// <summary>
/// Indicates an import of over 2000 users and/or groups is expected
/// </summary>
public bool LargeImport { get; set; } = false;
public class OrganizationImportGroupRequestModel
{
/// <summary>
/// The name of the group.
/// </summary>
/// <example>Development Team</example>
[Required]
[StringLength(100)]
public string Name { get; set; }
/// <summary>
/// External identifier for reference or linking this group to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[Required]
[StringLength(300)]
public string ExternalId { get; set; }
/// <summary>
/// The associated external ids for members in this group.
/// </summary>
public IEnumerable<string> MemberExternalIds { get; set; }
public ImportedGroup ToImportedGroup(Guid organizationId)
{
var importedGroup = new ImportedGroup
{
Group = new Table.Group
{
OrganizationId = organizationId,
Name = Name,
ExternalId = ExternalId
},
ExternalUserIds = new HashSet<string>(MemberExternalIds)
};
return importedGroup;
}
}
public class OrganizationImportMemberRequestModel : IValidatableObject
{
/// <summary>
/// The member's email address. Required for non-deleted users.
/// </summary>
/// <example>jsmith@example.com</example>
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
/// <summary>
/// External identifier for reference or linking this member to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[Required]
[StringLength(300)]
public string ExternalId { get; set; }
/// <summary>
/// Determines if this member should be removed from the organization during import.
/// </summary>
public bool Deleted { get; set; }
public ImportedOrganizationUser ToImportedOrganizationUser()
{
var importedUser = new ImportedOrganizationUser
{
Email = Email.ToLowerInvariant(),
ExternalId = ExternalId
};
return importedUser;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(Email) && !Deleted)
{
yield return new ValidationResult("Email is required for enabled members.",
new string[] { nameof(Email) });
}
}
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using Bit.Core.Models.Table;
using Newtonsoft.Json;
namespace Bit.Api.Models.Public.Request
{
public class PolicyUpdateRequestModel : PolicyBaseModel
{
public Policy ToPolicy(Guid orgId)
{
return ToPolicy(new Policy
{
OrganizationId = orgId
});
}
public virtual Policy ToPolicy(Policy existingPolicy)
{
existingPolicy.Enabled = Enabled.GetValueOrDefault();
existingPolicy.Data = Data != null ? JsonConvert.SerializeObject(Data) : null;
return existingPolicy;
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
namespace Bit.Api.Models.Public.Request
{
public class UpdateGroupIdsRequestModel
{
/// <summary>
/// The associated group ids that this object can access.
/// </summary>
public IEnumerable<Guid> GroupIds { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
namespace Bit.Api.Models.Public.Request
{
public class UpdateMemberIdsRequestModel
{
/// <summary>
/// The associated member ids that have access to this object.
/// </summary>
public IEnumerable<Guid> MemberIds { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using Bit.Core.Models.Data;
namespace Bit.Api.Models.Public.Response
{
public class AssociationWithPermissionsResponseModel : AssociationWithPermissionsBaseModel
{
public AssociationWithPermissionsResponseModel(SelectionReadOnly selection)
{
if (selection == null)
{
throw new ArgumentNullException(nameof(selection));
}
Id = selection.Id;
ReadOnly = selection.ReadOnly;
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Public.Response
{
/// <summary>
/// A collection.
/// </summary>
public class CollectionResponseModel : CollectionBaseModel, IResponseModel
{
public CollectionResponseModel(Collection collection, IEnumerable<SelectionReadOnly> groups)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
Id = collection.Id;
ExternalId = collection.ExternalId;
Groups = groups?.Select(c => new AssociationWithPermissionsResponseModel(c));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>collection</example>
[Required]
public string Object => "collection";
/// <summary>
/// The collection's unique identifier.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The associated groups that this collection is assigned to.
/// </summary>
public IEnumerable<AssociationWithPermissionsResponseModel> Groups { get; set; }
}
}

View File

@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Bit.Api.Models.Public.Response
{
public class ErrorResponseModel : IResponseModel
{
public ErrorResponseModel(string message)
{
Message = message;
}
public ErrorResponseModel(ModelStateDictionary modelState)
{
Message = "The request's model state is invalid.";
Errors = new Dictionary<string, IEnumerable<string>>();
var keys = modelState.Keys.ToList();
var values = modelState.Values.ToList();
for (var i = 0; i < values.Count; i++)
{
var value = values[i];
if (keys.Count <= i)
{
// Keys not available for some reason.
break;
}
var key = keys[i];
if (value.ValidationState != ModelValidationState.Invalid || value.Errors.Count == 0)
{
continue;
}
var errors = value.Errors.Select(e => e.ErrorMessage);
Errors.Add(key, errors);
}
}
public ErrorResponseModel(Dictionary<string, IEnumerable<string>> errors)
: this("Errors have occurred.", errors)
{ }
public ErrorResponseModel(string errorKey, string errorValue)
: this(errorKey, new string[] { errorValue })
{ }
public ErrorResponseModel(string errorKey, IEnumerable<string> errorValues)
: this(new Dictionary<string, IEnumerable<string>> { { errorKey, errorValues } })
{ }
public ErrorResponseModel(string message, Dictionary<string, IEnumerable<string>> errors)
{
Message = message;
Errors = errors;
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>error</example>
[Required]
public string Object => "error";
/// <summary>
/// A human-readable message providing details about the error.
/// </summary>
/// <example>The request model is invalid.</example>
[Required]
public string Message { get; set; }
/// <summary>
/// If multiple errors occurred, they are listed in dictionary. Errors related to a specific
/// request parameter will include a dictionary key describing that parameter.
/// </summary>
public Dictionary<string, IEnumerable<string>> Errors { get; set; }
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
namespace Bit.Api.Models.Public.Response
{
/// <summary>
/// An event log.
/// </summary>
public class EventResponseModel : IResponseModel
{
public EventResponseModel(IEvent ev)
{
if (ev == null)
{
throw new ArgumentNullException(nameof(ev));
}
Type = ev.Type;
ItemId = ev.CipherId;
CollectionId = ev.CollectionId;
GroupId = ev.GroupId;
PolicyId = ev.PolicyId;
MemberId = ev.OrganizationUserId;
ActingUserId = ev.ActingUserId;
Date = ev.Date;
Device = ev.DeviceType;
IpAddress = ev.IpAddress;
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>event</example>
[Required]
public string Object => "event";
/// <summary>
/// The type of event.
/// </summary>
[Required]
public EventType Type { get; set; }
/// <summary>
/// The unique identifier of the related item that the event describes.
/// </summary>
/// <example>3767a302-8208-4dc6-b842-030428a1cfad</example>
public Guid? ItemId { get; set; }
/// <summary>
/// The unique identifier of the related collection that the event describes.
/// </summary>
/// <example>bce212a4-25f3-4888-8a0a-4c5736d851e0</example>
public Guid? CollectionId { get; set; }
/// <summary>
/// The unique identifier of the related group that the event describes.
/// </summary>
/// <example>f29a2515-91d2-4452-b49b-5e8040e6b0f4</example>
public Guid? GroupId { get; set; }
/// <summary>
/// The unique identifier of the related policy that the event describes.
/// </summary>
/// <example>f29a2515-91d2-4452-b49b-5e8040e6b0f4</example>
public Guid? PolicyId { get; set; }
/// <summary>
/// The unique identifier of the related member that the event describes.
/// </summary>
/// <example>e68b8629-85eb-4929-92c0-b84464976ba4</example>
public Guid? MemberId { get; set; }
/// <summary>
/// The unique identifier of the user that performed the event.
/// </summary>
/// <example>a2549f79-a71f-4eb9-9234-eb7247333f94</example>
public Guid? ActingUserId { get; set; }
/// <summary>
/// The date/timestamp when the event occurred.
/// </summary>
[Required]
public DateTime Date { get; set; }
/// <summary>
/// The type of device used by the acting user when the event occurred.
/// </summary>
public DeviceType? Device { get; set; }
/// <summary>
/// The IP address of the acting user.
/// </summary>
/// <example>172.16.254.1</example>
public string IpAddress { get; set; }
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Public.Response
{
/// <summary>
/// A user group.
/// </summary>
public class GroupResponseModel : GroupBaseModel, IResponseModel
{
public GroupResponseModel(Group group, IEnumerable<SelectionReadOnly> collections)
{
if (group == null)
{
throw new ArgumentNullException(nameof(group));
}
Id = group.Id;
Name = group.Name;
AccessAll = group.AccessAll;
ExternalId = group.ExternalId;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>group</example>
[Required]
public string Object => "group";
/// <summary>
/// The group's unique identifier.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The associated collections that this group can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsResponseModel> Collections { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace Bit.Api.Models.Public.Response
{
public interface IResponseModel
{
string Object { get; }
}
}

View File

@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public.Response
{
public class ListResponseModel<T> : IResponseModel where T : IResponseModel
{
public ListResponseModel(IEnumerable<T> data, string continuationToken = null)
{
Data = data;
ContinuationToken = continuationToken;
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>list</example>
[Required]
public string Object => "list";
/// <summary>
/// An array containing the actual response elements, paginated by any request parameters.
/// </summary>
[Required]
public IEnumerable<T> Data { get; set; }
/// <summary>
/// A cursor for use in pagination.
/// </summary>
public string ContinuationToken { get; set; }
}
}

View File

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Public.Response
{
/// <summary>
/// An organization member.
/// </summary>
public class MemberResponseModel : MemberBaseModel, IResponseModel
{
public MemberResponseModel(OrganizationUser user, IEnumerable<SelectionReadOnly> collections)
: base(user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Id = user.Id;
UserId = user.UserId;
Email = user.Email;
Status = user.Status;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
}
public MemberResponseModel(OrganizationUserUserDetails user, bool twoFactorEnabled,
IEnumerable<SelectionReadOnly> collections)
: base(user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Id = user.Id;
UserId = user.UserId;
Name = user.Name;
Email = user.Email;
TwoFactorEnabled = twoFactorEnabled;
Status = user.Status;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>member</example>
[Required]
public string Object => "member";
/// <summary>
/// The member's unique identifier within the organization.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The member's unique identifier across Bitwarden.
/// </summary>
/// <example>48b47ee1-493e-4c67-aef7-014996c40eca</example>
[Required]
public Guid? UserId { get; set; }
/// <summary>
/// The member's name, set from their user account profile.
/// </summary>
/// <example>John Smith</example>
public string Name { get; set; }
/// <summary>
/// The member's email address.
/// </summary>
/// <example>jsmith@example.com</example>
[Required]
public string Email { get; set; }
/// <summary>
/// Returns <c>true</c> if the member has a two-step login method enabled on their user account.
/// </summary>
[Required]
public bool TwoFactorEnabled { get; set; }
/// <summary>
/// The member's status within the organization. All created members start with a status of "Invited".
/// Once a member accept's their invitation to join the organization, their status changes to "Accepted".
/// Accepted members are then "Confirmed" by an organization administrator. Once a member is "Confirmed",
/// their status can no longer change.
/// </summary>
[Required]
public OrganizationUserStatusType Status { get; set; }
/// <summary>
/// The associated collections that this member can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsResponseModel> Collections { get; set; }
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Newtonsoft.Json;
namespace Bit.Api.Models.Public.Response
{
/// <summary>
/// A policy.
/// </summary>
public class PolicyResponseModel : PolicyBaseModel, IResponseModel
{
public PolicyResponseModel(Policy policy)
{
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
Id = policy.Id;
Type = policy.Type;
Enabled = policy.Enabled;
if (!string.IsNullOrWhiteSpace(policy.Data))
{
Data = JsonConvert.DeserializeObject<Dictionary<string, object>>(policy.Data);
}
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>policy</example>
[Required]
public string Object => "policy";
/// <summary>
/// The policy's unique identifier.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The type of policy.
/// </summary>
[Required]
public PolicyType? Type { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class DeleteRecoverRequestModel
{
[Required]
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request.Accounts
{
public class EmailRequestModel : SecretVerificationRequestModel
{
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string NewEmail { get; set; }
[Required]
[StringLength(300)]
public string NewMasterPasswordHash { get; set; }
[Required]
public string Token { get; set; }
[Required]
public string Key { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request.Accounts
{
public class EmailTokenRequestModel : SecretVerificationRequestModel
{
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string NewEmail { get; set; }
}
}

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace Bit.Api.Models.Request.Accounts
{
public class ImportCiphersRequestModel
{
public FolderRequestModel[] Folders { get; set; }
public CipherRequestModel[] Ciphers { get; set; }
public KeyValuePair<int, int>[] FolderRelationships { get; set; }
}
}

View File

@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
namespace Bit.Api.Models.Request.Accounts
{
public class KdfRequestModel : PasswordRequestModel, IValidatableObject
{
[Required]
public KdfType? Kdf { get; set; }
[Required]
public int? KdfIterations { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Kdf.HasValue && KdfIterations.HasValue)
{
switch (Kdf.Value)
{
case KdfType.PBKDF2_SHA256:
if (KdfIterations.Value < 5000 || KdfIterations.Value > 2_000_000)
{
yield return new ValidationResult("KDF iterations must be between 5000 and 2000000.");
}
break;
default:
break;
}
}
}
}
}

View File

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Request.Accounts
{
public class KeysRequestModel
{
public string PublicKey { get; set; }
[Required]
public string EncryptedPrivateKey { get; set; }
public User ToUser(User existingUser)
{
if (string.IsNullOrWhiteSpace(existingUser.PublicKey) && !string.IsNullOrWhiteSpace(PublicKey))
{
existingUser.PublicKey = PublicKey;
}
if (string.IsNullOrWhiteSpace(existingUser.PrivateKey))
{
existingUser.PrivateKey = EncryptedPrivateKey;
}
return existingUser;
}
}
}

View File

@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class PasswordHintRequestModel
{
[Required]
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class PasswordRequestModel : SecretVerificationRequestModel
{
[Required]
[StringLength(300)]
public string NewMasterPasswordHash { get; set; }
[Required]
public string Key { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class PreloginRequestModel
{
[Required]
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
}
}

View File

@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Http;
using Enums = Bit.Core.Enums;
namespace Bit.Api.Models.Request.Accounts
{
public class PremiumRequestModel : IValidatableObject
{
[Required]
public Enums.PaymentMethodType? PaymentMethodType { get; set; }
public string PaymentToken { get; set; }
[Range(0, 99)]
public short? AdditionalStorageGb { get; set; }
public IFormFile License { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
public bool Validate(GlobalSettings globalSettings)
{
if (!(License == null && !globalSettings.SelfHosted) ||
(License != null && globalSettings.SelfHosted))
{
return false;
}
return globalSettings.SelfHosted || !string.IsNullOrWhiteSpace(Country);
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var creditType = PaymentMethodType.HasValue && PaymentMethodType.Value == Enums.PaymentMethodType.Credit;
if (string.IsNullOrWhiteSpace(PaymentToken) && !creditType && License == null)
{
yield return new ValidationResult("Payment token or license is required.");
}
if (Country == "US" && string.IsNullOrWhiteSpace(PostalCode))
{
yield return new ValidationResult("Zip / postal code is required.",
new string[] { nameof(PostalCode) });
}
}
}
}

View File

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class RegenerateTwoFactorRequestModel
{
[Required]
public string MasterPasswordHash { get; set; }
[Required]
[StringLength(50)]
public string Token { get; set; }
}
}

View File

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Api;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
using Newtonsoft.Json;
namespace Bit.Api.Models.Request.Accounts
{
public class RegisterRequestModel : IValidatableObject, ICaptchaProtectedModel
{
[StringLength(50)]
public string Name { get; set; }
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string Email { get; set; }
[Required]
[StringLength(1000)]
public string MasterPasswordHash { get; set; }
[StringLength(50)]
public string MasterPasswordHint { get; set; }
public string CaptchaResponse { get; set; }
public string Key { get; set; }
public KeysRequestModel Keys { get; set; }
public string Token { get; set; }
public Guid? OrganizationUserId { get; set; }
public KdfType? Kdf { get; set; }
public int? KdfIterations { get; set; }
public Dictionary<string, object> ReferenceData { get; set; }
public User ToUser()
{
var user = new User
{
Name = Name,
Email = Email,
MasterPasswordHint = MasterPasswordHint,
Kdf = Kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256),
KdfIterations = KdfIterations.GetValueOrDefault(5000),
};
if (ReferenceData != null)
{
user.ReferenceData = JsonConvert.SerializeObject(ReferenceData);
}
if (Key != null)
{
user.Key = Key;
}
if (Keys != null)
{
Keys.ToUser(user);
}
return user;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Kdf.HasValue && KdfIterations.HasValue)
{
switch (Kdf.Value)
{
case KdfType.PBKDF2_SHA256:
if (KdfIterations.Value < 5000 || KdfIterations.Value > 1_000_000)
{
yield return new ValidationResult("KDF iterations must be between 5000 and 1000000.");
}
break;
default:
break;
}
}
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class SecretVerificationRequestModel : IValidatableObject
{
[StringLength(300)]
public string MasterPasswordHash { get; set; }
public string OTP { get; set; }
public string Secret => !string.IsNullOrEmpty(MasterPasswordHash) ? MasterPasswordHash : OTP;
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrEmpty(Secret))
{
yield return new ValidationResult("MasterPasswordHash or OTP must be supplied.");
}
}
}
}

View File

@ -0,0 +1,29 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Request.Accounts
{
public class SetKeyConnectorKeyRequestModel
{
[Required]
public string Key { get; set; }
[Required]
public KeysRequestModel Keys { get; set; }
[Required]
public KdfType Kdf { get; set; }
[Required]
public int KdfIterations { get; set; }
[Required]
public string OrgIdentifier { get; set; }
public User ToUser(User existingUser)
{
existingUser.Kdf = Kdf;
existingUser.KdfIterations = KdfIterations;
existingUser.Key = Key;
Keys.ToUser(existingUser);
return existingUser;
}
}
}

View File

@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Request.Accounts
{
public class SetPasswordRequestModel
{
[Required]
[StringLength(300)]
public string MasterPasswordHash { get; set; }
[Required]
public string Key { get; set; }
[StringLength(50)]
public string MasterPasswordHint { get; set; }
[Required]
public KeysRequestModel Keys { get; set; }
[Required]
public KdfType Kdf { get; set; }
[Required]
public int KdfIterations { get; set; }
public string OrgIdentifier { get; set; }
public User ToUser(User existingUser)
{
existingUser.MasterPasswordHint = MasterPasswordHint;
existingUser.Kdf = Kdf;
existingUser.KdfIterations = KdfIterations;
existingUser.Key = Key;
Keys.ToUser(existingUser);
return existingUser;
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class StorageRequestModel : IValidatableObject
{
[Required]
public short? StorageGbAdjustment { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (StorageGbAdjustment == 0)
{
yield return new ValidationResult("Storage adjustment cannot be 0.",
new string[] { nameof(StorageGbAdjustment) });
}
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class TaxInfoUpdateRequestModel : IValidatableObject
{
[Required]
public string Country { get; set; }
public string PostalCode { get; set; }
public virtual IEnumerable<ValidationResult> Validate (ValidationContext validationContext)
{
if (Country == "US" && string.IsNullOrWhiteSpace(PostalCode))
{
yield return new ValidationResult("Zip / postal code is required.",
new string[] { nameof(PostalCode) });
}
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class UpdateKeyRequestModel
{
[Required]
[StringLength(300)]
public string MasterPasswordHash { get; set; }
[Required]
public IEnumerable<CipherWithIdRequestModel> Ciphers { get; set; }
[Required]
public IEnumerable<FolderWithIdRequestModel> Folders { get; set; }
public IEnumerable<SendWithIdRequestModel> Sends { get; set; }
[Required]
public string PrivateKey { get; set; }
[Required]
public string Key { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Request.Accounts
{
public class UpdateProfileRequestModel
{
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
public string MasterPasswordHint { get; set; }
public User ToUser(User existingUser)
{
existingUser.Name = Name;
existingUser.MasterPasswordHint = string.IsNullOrWhiteSpace(MasterPasswordHint) ? null : MasterPasswordHint;
return existingUser;
}
}
}

View File

@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
using Bit.Api.Models.Request.Organizations;
namespace Bit.Api.Models.Request.Accounts
{
public class UpdateTempPasswordRequestModel : OrganizationUserResetPasswordRequestModel
{
[StringLength(50)]
public string MasterPasswordHint { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class VerifyDeleteRecoverRequestModel
{
[Required]
public string UserId { get; set; }
[Required]
public string Token { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class VerifyEmailRequestModel
{
[Required]
public string UserId { get; set; }
[Required]
public string Token { get; set; }
}
}

View File

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class VerifyOTPRequestModel
{
[Required]
public string OTP { get; set; }
}
}

View File

@ -0,0 +1,10 @@
namespace Bit.Api.Models.Request
{
public class AttachmentRequestModel
{
public string Key { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public bool AdminRequest { get; set; } = false;
}
}

View File

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Settings;
namespace Bit.Api.Models.Request
{
public class BitPayInvoiceRequestModel : IValidatableObject
{
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public bool Credit { get; set; }
[Required]
public decimal? Amount { get; set; }
public string ReturnUrl { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public BitPayLight.Models.Invoice.Invoice ToBitpayInvoice(GlobalSettings globalSettings)
{
var inv = new BitPayLight.Models.Invoice.Invoice
{
Price = Convert.ToDouble(Amount.Value),
Currency = "USD",
RedirectUrl = ReturnUrl,
Buyer = new BitPayLight.Models.Invoice.Buyer
{
Email = Email,
Name = Name
},
NotificationUrl = globalSettings.BitPay.NotificationUrl,
FullNotifications = true,
ExtendedNotifications = true
};
var posData = string.Empty;
if (UserId.HasValue)
{
posData = "userId:" + UserId.Value;
}
else if (OrganizationId.HasValue)
{
posData = "organizationId:" + OrganizationId.Value;
}
if (Credit)
{
posData += ",accountCredit:1";
inv.ItemDesc = "Bitwarden Account Credit";
}
else
{
inv.ItemDesc = "Bitwarden";
}
inv.PosData = posData;
return inv;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!UserId.HasValue && !OrganizationId.HasValue)
{
yield return new ValidationResult("User or Ooganization is required.");
}
}
}
}

View File

@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request
{
public class CipherPartialRequestModel
{
[StringLength(36)]
public string FolderId { get; set; }
public bool Favorite { get; set; }
}
}

View File

@ -0,0 +1,353 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
using Core.Models.Data;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Bit.Api.Models.Request
{
public class CipherRequestModel
{
public CipherType Type { get; set; }
[StringLength(36)]
public string OrganizationId { get; set; }
public string FolderId { get; set; }
public bool Favorite { get; set; }
public CipherRepromptType Reprompt { get; set; }
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
public string Name { get; set; }
[EncryptedString]
[EncryptedStringLength(10000)]
public string Notes { get; set; }
public IEnumerable<CipherFieldModel> Fields { get; set; }
public IEnumerable<CipherPasswordHistoryModel> PasswordHistory { get; set; }
[Obsolete]
public Dictionary<string, string> Attachments { get; set; }
// TODO: Rename to Attachments whenever the above is finally removed.
public Dictionary<string, CipherAttachmentModel> Attachments2 { get; set; }
public CipherLoginModel Login { get; set; }
public CipherCardModel Card { get; set; }
public CipherIdentityModel Identity { get; set; }
public CipherSecureNoteModel SecureNote { get; set; }
public DateTime? LastKnownRevisionDate { get; set; } = null;
public CipherDetails ToCipherDetails(Guid userId, bool allowOrgIdSet = true)
{
var hasOrgId = !string.IsNullOrWhiteSpace(OrganizationId);
var cipher = new CipherDetails
{
Type = Type,
UserId = !hasOrgId ? (Guid?)userId : null,
OrganizationId = allowOrgIdSet && hasOrgId ? new Guid(OrganizationId) : (Guid?)null,
Edit = true,
ViewPassword = true,
};
ToCipherDetails(cipher);
return cipher;
}
public CipherDetails ToCipherDetails(CipherDetails existingCipher)
{
existingCipher.FolderId = string.IsNullOrWhiteSpace(FolderId) ? null : (Guid?)new Guid(FolderId);
existingCipher.Favorite = Favorite;
ToCipher(existingCipher);
return existingCipher;
}
public Cipher ToCipher(Cipher existingCipher)
{
switch (existingCipher.Type)
{
case CipherType.Login:
var loginObj = JObject.FromObject(ToCipherLoginData(),
new JsonSerializer { NullValueHandling = NullValueHandling.Ignore });
loginObj[nameof(CipherLoginData.Uri)]?.Parent?.Remove();
existingCipher.Data = loginObj.ToString(Formatting.None);
break;
case CipherType.Card:
existingCipher.Data = JsonConvert.SerializeObject(ToCipherCardData(),
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
break;
case CipherType.Identity:
existingCipher.Data = JsonConvert.SerializeObject(ToCipherIdentityData(),
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
break;
case CipherType.SecureNote:
existingCipher.Data = JsonConvert.SerializeObject(ToCipherSecureNoteData(),
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
break;
default:
throw new ArgumentException("Unsupported type: " + nameof(Type) + ".");
}
existingCipher.Reprompt = Reprompt;
var hasAttachments2 = (Attachments2?.Count ?? 0) > 0;
var hasAttachments = (Attachments?.Count ?? 0) > 0;
if (!hasAttachments2 && !hasAttachments)
{
return existingCipher;
}
var attachments = existingCipher.GetAttachments();
if ((attachments?.Count ?? 0) == 0)
{
return existingCipher;
}
if (hasAttachments2)
{
foreach (var attachment in attachments.Where(a => Attachments2.ContainsKey(a.Key)))
{
var attachment2 = Attachments2[attachment.Key];
attachment.Value.FileName = attachment2.FileName;
attachment.Value.Key = attachment2.Key;
}
}
else if (hasAttachments)
{
foreach (var attachment in attachments.Where(a => Attachments.ContainsKey(a.Key)))
{
attachment.Value.FileName = Attachments[attachment.Key];
attachment.Value.Key = null;
}
}
existingCipher.SetAttachments(attachments);
return existingCipher;
}
public Cipher ToOrganizationCipher()
{
if (string.IsNullOrWhiteSpace(OrganizationId))
{
throw new ArgumentNullException(nameof(OrganizationId));
}
return ToCipher(new Cipher
{
Type = Type,
OrganizationId = new Guid(OrganizationId)
});
}
public CipherDetails ToOrganizationCipherDetails(Guid orgId)
{
return ToCipherDetails(new CipherDetails
{
Type = Type,
OrganizationId = orgId,
Edit = true
});
}
private CipherLoginData ToCipherLoginData()
{
return new CipherLoginData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
Uris =
Login.Uris?.Where(u => u != null)
.Select(u => u.ToCipherLoginUriData()),
Username = Login.Username,
Password = Login.Password,
PasswordRevisionDate = Login.PasswordRevisionDate,
Totp = Login.Totp,
AutofillOnPageLoad = Login.AutofillOnPageLoad,
};
}
private CipherIdentityData ToCipherIdentityData()
{
return new CipherIdentityData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
Title = Identity.Title,
FirstName = Identity.FirstName,
MiddleName = Identity.MiddleName,
LastName = Identity.LastName,
Address1 = Identity.Address1,
Address2 = Identity.Address2,
Address3 = Identity.Address3,
City = Identity.City,
State = Identity.State,
PostalCode = Identity.PostalCode,
Country = Identity.Country,
Company = Identity.Company,
Email = Identity.Email,
Phone = Identity.Phone,
SSN = Identity.SSN,
Username = Identity.Username,
PassportNumber = Identity.PassportNumber,
LicenseNumber = Identity.LicenseNumber,
};
}
private CipherCardData ToCipherCardData()
{
return new CipherCardData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
CardholderName = Card.CardholderName,
Brand = Card.Brand,
Number = Card.Number,
ExpMonth = Card.ExpMonth,
ExpYear = Card.ExpYear,
Code = Card.Code,
};
}
private CipherSecureNoteData ToCipherSecureNoteData()
{
return new CipherSecureNoteData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
Type = SecureNote.Type,
};
}
}
public class CipherWithIdRequestModel : CipherRequestModel
{
[Required]
public Guid? Id { get; set; }
}
public class CipherCreateRequestModel : IValidatableObject
{
public IEnumerable<Guid> CollectionIds { get; set; }
[Required]
public CipherRequestModel Cipher { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(Cipher.OrganizationId) && (!CollectionIds?.Any() ?? true))
{
yield return new ValidationResult("You must select at least one collection.",
new string[] { nameof(CollectionIds) });
}
}
}
public class CipherShareRequestModel : IValidatableObject
{
[Required]
public IEnumerable<string> CollectionIds { get; set; }
[Required]
public CipherRequestModel Cipher { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(Cipher.OrganizationId))
{
yield return new ValidationResult("Cipher OrganizationId is required.",
new string[] { nameof(Cipher.OrganizationId) });
}
if (!CollectionIds?.Any() ?? true)
{
yield return new ValidationResult("You must select at least one collection.",
new string[] { nameof(CollectionIds) });
}
}
}
public class CipherCollectionsRequestModel
{
[Required]
public IEnumerable<string> CollectionIds { get; set; }
}
public class CipherBulkDeleteRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
public string OrganizationId { get; set; }
}
public class CipherBulkRestoreRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
}
public class CipherBulkMoveRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
public string FolderId { get; set; }
}
public class CipherBulkShareRequestModel : IValidatableObject
{
[Required]
public IEnumerable<string> CollectionIds { get; set; }
[Required]
public IEnumerable<CipherWithIdRequestModel> Ciphers { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!Ciphers?.Any() ?? true)
{
yield return new ValidationResult("You must select at least one cipher.",
new string[] { nameof(Ciphers) });
}
else
{
var allHaveIds = true;
var organizationIds = new HashSet<string>();
foreach (var c in Ciphers)
{
organizationIds.Add(c.OrganizationId);
if (allHaveIds)
{
allHaveIds = !(!c.Id.HasValue || string.IsNullOrWhiteSpace(c.OrganizationId));
}
}
if (!allHaveIds)
{
yield return new ValidationResult("All Ciphers must have an Id and OrganizationId.",
new string[] { nameof(Ciphers) });
}
else if (organizationIds.Count != 1)
{
yield return new ValidationResult("All ciphers must be for the same organization.");
}
}
if (!CollectionIds?.Any() ?? true)
{
yield return new ValidationResult("You must select at least one collection.",
new string[] { nameof(CollectionIds) });
}
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request
{
public class CollectionRequestModel
{
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
public string Name { get; set; }
[StringLength(300)]
public string ExternalId { get; set; }
public IEnumerable<SelectionReadOnlyRequestModel> Groups { get; set; }
public Collection ToCollection(Guid orgId)
{
return ToCollection(new Collection
{
OrganizationId = orgId
});
}
public Collection ToCollection(Collection existingCollection)
{
existingCollection.Name = Name;
existingCollection.ExternalId = ExternalId;
return existingCollection;
}
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Request
{
public class DeviceRequestModel
{
[Required]
public DeviceType? Type { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[StringLength(50)]
public string Identifier { get; set; }
[StringLength(255)]
public string PushToken { get; set; }
public Device ToDevice(Guid? userId = null)
{
return ToDevice(new Device
{
UserId = userId == null ? default(Guid) : userId.Value
});
}
public Device ToDevice(Device existingDevice)
{
existingDevice.Name = Name;
existingDevice.Identifier = Identifier;
existingDevice.PushToken = PushToken;
existingDevice.Type = Type.Value;
return existingDevice;
}
}
public class DeviceTokenRequestModel
{
[StringLength(255)]
public string PushToken { get; set; }
public Device ToDevice(Device existingDevice)
{
existingDevice.PushToken = PushToken;
return existingDevice;
}
}
}

View File

@ -0,0 +1,49 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request
{
public class EmergencyAccessInviteRequestModel
{
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string Email { get; set; }
[Required]
public EmergencyAccessType? Type { get; set; }
[Required]
public int WaitTimeDays { get; set; }
}
public class EmergencyAccessUpdateRequestModel
{
[Required]
public EmergencyAccessType Type { get; set; }
[Required]
public int WaitTimeDays { get; set; }
public string KeyEncrypted { get; set; }
public EmergencyAccess ToEmergencyAccess(EmergencyAccess existingEmergencyAccess)
{
// Ensure we only set keys for a confirmed emergency access.
if (!string.IsNullOrWhiteSpace(existingEmergencyAccess.KeyEncrypted) && !string.IsNullOrWhiteSpace(KeyEncrypted))
{
existingEmergencyAccess.KeyEncrypted = KeyEncrypted;
}
existingEmergencyAccess.Type = Type;
existingEmergencyAccess.WaitTimeDays = WaitTimeDays;
return existingEmergencyAccess;
}
}
public class EmergencyAccessPasswordRequestModel
{
[Required]
[StringLength(300)]
public string NewMasterPasswordHash { get; set; }
[Required]
public string Key { get; set; }
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request
{
public class FolderRequestModel
{
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
public string Name { get; set; }
public Folder ToFolder(Guid userId)
{
return ToFolder(new Folder
{
UserId = userId
});
}
public Folder ToFolder(Folder existingFolder)
{
existingFolder.Name = Name;
return existingFolder;
}
}
public class FolderWithIdRequestModel : FolderRequestModel
{
public Guid Id { get; set; }
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Request
{
public class GroupRequestModel
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
public bool? AccessAll { get; set; }
[StringLength(300)]
public string ExternalId { get; set; }
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
public Group ToGroup(Guid orgId)
{
return ToGroup(new Group
{
OrganizationId = orgId
});
}
public Group ToGroup(Group existingGroup)
{
existingGroup.Name = Name;
existingGroup.AccessAll = AccessAll.Value;
existingGroup.ExternalId = ExternalId;
return existingGroup;
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Enums = Bit.Core.Enums;
namespace Bit.Api.Models.Request
{
public class IapCheckRequestModel : IValidatableObject
{
[Required]
public Enums.PaymentMethodType? PaymentMethodType { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (PaymentMethodType != Enums.PaymentMethodType.AppleInApp)
{
yield return new ValidationResult("Not a supported in-app purchase payment method.",
new string[] { nameof(PaymentMethodType) });
}
}
}
}

View File

@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request
{
public class InstallationRequestModel
{
[Required]
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
public Installation ToInstallation()
{
return new Installation
{
Key = CoreHelpers.SecureRandomString(20),
Email = Email,
Enabled = true
};
}
}
}

View File

@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Http;
namespace Bit.Api.Models.Request
{
public class LicenseRequestModel
{
[Required]
public IFormFile License { get; set; }
}
}

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace Bit.Api.Models.Request.Organizations
{
public class ImportOrganizationCiphersRequestModel
{
public CollectionRequestModel[] Collections { get; set; }
public CipherRequestModel[] Ciphers { get; set; }
public KeyValuePair<int, int>[] CollectionRelationships { get; set; }
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Business;
using Table = Bit.Core.Models.Table;
namespace Bit.Api.Models.Request.Organizations
{
public class ImportOrganizationUsersRequestModel
{
public Group[] Groups { get; set; }
public User[] Users { get; set; }
public bool OverwriteExisting { get; set; }
public bool LargeImport { get; set; }
public class Group
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
[StringLength(300)]
public string ExternalId { get; set; }
public IEnumerable<string> Users { get; set; }
public ImportedGroup ToImportedGroup(Guid organizationId)
{
var importedGroup = new ImportedGroup
{
Group = new Table.Group
{
OrganizationId = organizationId,
Name = Name,
ExternalId = ExternalId
},
ExternalUserIds = new HashSet<string>(Users)
};
return importedGroup;
}
}
public class User : IValidatableObject
{
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
public bool Deleted { get; set; }
[Required]
[StringLength(300)]
public string ExternalId { get; set; }
public ImportedOrganizationUser ToImportedOrganizationUser()
{
var importedUser = new ImportedOrganizationUser
{
Email = Email.ToLowerInvariant(),
ExternalId = ExternalId
};
return importedUser;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(Email) && !Deleted)
{
yield return new ValidationResult("Email is required for enabled users.", new string[] { nameof(Email) });
}
}
}
}
}

View File

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request.Organizations
{
public class OrganizationCreateLicenseRequestModel : LicenseRequestModel
{
[Required]
public string Key { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string CollectionName { get; set; }
public OrganizationKeysRequestModel Keys { get; set; }
}
}

View File

@ -0,0 +1,103 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Business;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request.Organizations
{
public class OrganizationCreateRequestModel : IValidatableObject
{
[Required]
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
public string BusinessName { get; set; }
[Required]
[StringLength(256)]
[EmailAddress]
public string BillingEmail { get; set; }
public PlanType PlanType { get; set; }
[Required]
public string Key { get; set; }
public OrganizationKeysRequestModel Keys { get; set; }
public PaymentMethodType? PaymentMethodType { get; set; }
public string PaymentToken { get; set; }
[Range(0, int.MaxValue)]
public int AdditionalSeats { get; set; }
[Range(0, 99)]
public short? AdditionalStorageGb { get; set; }
public bool PremiumAccessAddon { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string CollectionName { get; set; }
public string TaxIdNumber { get; set; }
public string BillingAddressLine1 { get; set; }
public string BillingAddressLine2 { get; set; }
public string BillingAddressCity { get; set; }
public string BillingAddressState { get; set; }
public string BillingAddressPostalCode { get; set; }
[StringLength(2)]
public string BillingAddressCountry { get; set; }
public int? MaxAutoscaleSeats { get; set; }
public virtual OrganizationSignup ToOrganizationSignup(User user)
{
var orgSignup = new OrganizationSignup
{
Owner = user,
OwnerKey = Key,
Name = Name,
Plan = PlanType,
PaymentMethodType = PaymentMethodType,
PaymentToken = PaymentToken,
AdditionalSeats = AdditionalSeats,
MaxAutoscaleSeats = MaxAutoscaleSeats,
AdditionalStorageGb = AdditionalStorageGb.GetValueOrDefault(0),
PremiumAccessAddon = PremiumAccessAddon,
BillingEmail = BillingEmail,
BusinessName = BusinessName,
CollectionName = CollectionName,
TaxInfo = new TaxInfo
{
TaxIdNumber = TaxIdNumber,
BillingAddressLine1 = BillingAddressLine1,
BillingAddressLine2 = BillingAddressLine2,
BillingAddressCity = BillingAddressCity,
BillingAddressState = BillingAddressState,
BillingAddressPostalCode = BillingAddressPostalCode,
BillingAddressCountry = BillingAddressCountry,
},
};
Keys?.ToOrganizationSignup(orgSignup);
return orgSignup;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (PlanType != PlanType.Free && string.IsNullOrWhiteSpace(PaymentToken))
{
yield return new ValidationResult("Payment required.", new string[] { nameof(PaymentToken) });
}
if (PlanType != PlanType.Free && !PaymentMethodType.HasValue)
{
yield return new ValidationResult("Payment method type required.",
new string[] { nameof(PaymentMethodType) });
}
if (PlanType != PlanType.Free && string.IsNullOrWhiteSpace(BillingAddressCountry))
{
yield return new ValidationResult("Country required.",
new string[] { nameof(BillingAddressCountry) });
}
if (PlanType != PlanType.Free && BillingAddressCountry == "US" &&
string.IsNullOrWhiteSpace(BillingAddressPostalCode))
{
yield return new ValidationResult("Zip / postal code is required.",
new string[] { nameof(BillingAddressPostalCode) });
}
}
}
}

View File

@ -0,0 +1,59 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Business;
using Bit.Core.Models.Table;
namespace Bit.Api.Models.Request.Organizations
{
public class OrganizationKeysRequestModel
{
[Required]
public string PublicKey { get; set; }
[Required]
public string EncryptedPrivateKey { get; set; }
public OrganizationSignup ToOrganizationSignup(OrganizationSignup existingSignup)
{
if (string.IsNullOrWhiteSpace(existingSignup.PublicKey))
{
existingSignup.PublicKey = PublicKey;
}
if (string.IsNullOrWhiteSpace(existingSignup.PrivateKey))
{
existingSignup.PrivateKey = EncryptedPrivateKey;
}
return existingSignup;
}
public OrganizationUpgrade ToOrganizationUpgrade(OrganizationUpgrade existingUpgrade)
{
if (string.IsNullOrWhiteSpace(existingUpgrade.PublicKey))
{
existingUpgrade.PublicKey = PublicKey;
}
if (string.IsNullOrWhiteSpace(existingUpgrade.PrivateKey))
{
existingUpgrade.PrivateKey = EncryptedPrivateKey;
}
return existingUpgrade;
}
public Organization ToOrganization(Organization existingOrg)
{
if (string.IsNullOrWhiteSpace(existingOrg.PublicKey))
{
existingOrg.PublicKey = PublicKey;
}
if (string.IsNullOrWhiteSpace(existingOrg.PrivateKey))
{
existingOrg.PrivateKey = EncryptedPrivateKey;
}
return existingOrg;
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Organizations
{
public class OrganizationSeatRequestModel : IValidatableObject
{
[Required]
public int? SeatAdjustment { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (SeatAdjustment == 0)
{
yield return new ValidationResult("Seat adjustment cannot be 0.", new string[] { nameof(SeatAdjustment) });
}
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
namespace Bit.Api.Models.Request.Organizations
{
public class OrganizationSponsorshipRedeemRequestModel
{
[Required]
public PlanSponsorshipType PlanSponsorshipType { get; set; }
[Required]
public Guid SponsoredOrganizationId { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request.Organizations
{
public class OrganizationSponsorshipRequestModel
{
[Required]
public PlanSponsorshipType PlanSponsorshipType { get; set; }
[Required]
[StringLength(256)]
[StrictEmailAddress]
public string SponsoredEmail { get; set; }
[StringLength(256)]
public string FriendlyName { get; set; }
}
}

View File

@ -0,0 +1,239 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
using Bit.Core.Services;
using Bit.Core.Sso;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using U2F.Core.Utils;
namespace Bit.Api.Models.Request.Organizations
{
public class OrganizationSsoRequestModel
{
[Required]
public bool Enabled { get; set; }
[Required]
public SsoConfigurationDataRequest Data { get; set; }
public SsoConfig ToSsoConfig(Guid organizationId)
{
return ToSsoConfig(new SsoConfig { OrganizationId = organizationId });
}
public SsoConfig ToSsoConfig(SsoConfig existingConfig)
{
existingConfig.Enabled = Enabled;
var configurationData = Data.ToConfigurationData();
existingConfig.SetData(configurationData);
return existingConfig;
}
}
public class SsoConfigurationDataRequest : IValidatableObject
{
public SsoConfigurationDataRequest() {}
[Required]
public SsoType ConfigType { get; set; }
public bool KeyConnectorEnabled { get; set; }
public string KeyConnectorUrl { get; set; }
// OIDC
public string Authority { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string MetadataAddress { get; set; }
public OpenIdConnectRedirectBehavior RedirectBehavior { get; set; }
public bool? GetClaimsFromUserInfoEndpoint { get; set; }
public string AdditionalScopes { get; set; }
public string AdditionalUserIdClaimTypes { get; set; }
public string AdditionalEmailClaimTypes { get; set; }
public string AdditionalNameClaimTypes { get; set; }
public string AcrValues { get; set; }
public string ExpectedReturnAcrValue { get; set; }
// SAML2 SP
public Saml2NameIdFormat SpNameIdFormat { get; set; }
public string SpOutboundSigningAlgorithm { get; set; }
public Saml2SigningBehavior SpSigningBehavior { get; set; }
public bool? SpWantAssertionsSigned { get; set; }
public bool? SpValidateCertificates { get; set; }
public string SpMinIncomingSigningAlgorithm { get; set; }
// SAML2 IDP
public string IdpEntityId { get; set; }
public Saml2BindingType IdpBindingType { get; set; }
public string IdpSingleSignOnServiceUrl { get; set; }
public string IdpSingleLogoutServiceUrl { get; set; }
public string IdpArtifactResolutionServiceUrl { get; set; }
public string IdpX509PublicCert { get; set; }
public string IdpOutboundSigningAlgorithm { get; set; }
public bool? IdpAllowUnsolicitedAuthnResponse { get; set; }
public bool? IdpDisableOutboundLogoutRequests { get; set; }
public bool? IdpWantAuthnRequestsSigned { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
var i18nService = context.GetService(typeof(II18nService)) as I18nService;
if (ConfigType == SsoType.OpenIdConnect)
{
if (string.IsNullOrWhiteSpace(Authority))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("AuthorityValidationError"),
new[] { nameof(Authority) });
}
if (string.IsNullOrWhiteSpace(ClientId))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("ClientIdValidationError"),
new[] { nameof(ClientId) });
}
if (string.IsNullOrWhiteSpace(ClientSecret))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("ClientSecretValidationError"),
new[] { nameof(ClientSecret) });
}
}
else if (ConfigType == SsoType.Saml2)
{
if (string.IsNullOrWhiteSpace(IdpEntityId))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpEntityIdValidationError"),
new[] { nameof(IdpEntityId) });
}
if (IdpBindingType == Saml2BindingType.Artifact && string.IsNullOrWhiteSpace(IdpArtifactResolutionServiceUrl))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("Saml2BindingTypeValidationError"),
new[] { nameof(IdpArtifactResolutionServiceUrl) });
}
if (!Uri.IsWellFormedUriString(IdpEntityId, UriKind.Absolute) && string.IsNullOrWhiteSpace(IdpSingleSignOnServiceUrl))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpSingleSignOnServiceUrlValidationError"),
new[] { nameof(IdpSingleSignOnServiceUrl) });
}
if (InvalidServiceUrl(IdpSingleSignOnServiceUrl))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpSingleSignOnServiceUrlInvalid"),
new[] { nameof(IdpSingleSignOnServiceUrl) });
}
if (InvalidServiceUrl(IdpArtifactResolutionServiceUrl))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpArtifactResolutionServiceUrlInvalid"),
new[] { nameof(IdpArtifactResolutionServiceUrl) });
}
if (InvalidServiceUrl(IdpSingleLogoutServiceUrl))
{
yield return new ValidationResult(i18nService.GetLocalizedHtmlString("IdpSingleLogoutServiceUrlInvalid"),
new[] { nameof(IdpSingleLogoutServiceUrl) });
}
if (!string.IsNullOrWhiteSpace(IdpX509PublicCert))
{
// Validate the certificate is in a valid format
ValidationResult failedResult = null;
try
{
var certData = StripPemCertificateElements(IdpX509PublicCert).Base64StringToByteArray();
new X509Certificate2(certData);
}
catch (FormatException)
{
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertInvalidFormatValidationError"),
new[] { nameof(IdpX509PublicCert) });
}
catch (CryptographicException cryptoEx)
{
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertCryptographicExceptionValidationError", cryptoEx.Message),
new[] { nameof(IdpX509PublicCert) });
}
catch (Exception ex)
{
failedResult = new ValidationResult(i18nService.GetLocalizedHtmlString("IdpX509PublicCertValidationError", ex.Message),
new[] { nameof(IdpX509PublicCert) });
}
if (failedResult != null)
{
yield return failedResult;
}
}
}
}
public SsoConfigurationData ToConfigurationData()
{
return new SsoConfigurationData
{
ConfigType = ConfigType,
KeyConnectorEnabled = KeyConnectorEnabled,
KeyConnectorUrl = KeyConnectorUrl,
Authority = Authority,
ClientId = ClientId,
ClientSecret = ClientSecret,
MetadataAddress = MetadataAddress,
GetClaimsFromUserInfoEndpoint = GetClaimsFromUserInfoEndpoint.GetValueOrDefault(),
RedirectBehavior = RedirectBehavior,
IdpEntityId = IdpEntityId,
IdpBindingType = IdpBindingType,
IdpSingleSignOnServiceUrl = IdpSingleSignOnServiceUrl,
IdpSingleLogoutServiceUrl = IdpSingleLogoutServiceUrl,
IdpArtifactResolutionServiceUrl = IdpArtifactResolutionServiceUrl,
IdpX509PublicCert = StripPemCertificateElements(IdpX509PublicCert),
IdpOutboundSigningAlgorithm = IdpOutboundSigningAlgorithm,
IdpAllowUnsolicitedAuthnResponse = IdpAllowUnsolicitedAuthnResponse.GetValueOrDefault(),
IdpDisableOutboundLogoutRequests = IdpDisableOutboundLogoutRequests.GetValueOrDefault(),
IdpWantAuthnRequestsSigned = IdpWantAuthnRequestsSigned.GetValueOrDefault(),
SpNameIdFormat = SpNameIdFormat,
SpOutboundSigningAlgorithm = SpOutboundSigningAlgorithm ?? SamlSigningAlgorithms.Sha256,
SpSigningBehavior = SpSigningBehavior,
SpWantAssertionsSigned = SpWantAssertionsSigned.GetValueOrDefault(),
SpValidateCertificates = SpValidateCertificates.GetValueOrDefault(),
SpMinIncomingSigningAlgorithm = SpMinIncomingSigningAlgorithm,
AdditionalScopes = AdditionalScopes,
AdditionalUserIdClaimTypes = AdditionalUserIdClaimTypes,
AdditionalEmailClaimTypes = AdditionalEmailClaimTypes,
AdditionalNameClaimTypes = AdditionalNameClaimTypes,
AcrValues = AcrValues,
ExpectedReturnAcrValue = ExpectedReturnAcrValue,
};
}
private string StripPemCertificateElements(string certificateText)
{
if (string.IsNullOrWhiteSpace(certificateText))
{
return null;
}
return Regex.Replace(certificateText,
@"(((BEGIN|END) CERTIFICATE)|([\-\n\r\t\s\f]))",
string.Empty,
RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
private bool InvalidServiceUrl(string url)
{
if (string.IsNullOrWhiteSpace(url))
{
return false;
}
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
{
return true;
}
return Regex.IsMatch(url, "[<>\"]");
}
}
}

View File

@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Organizations
{
public class OrganizationSubscriptionUpdateRequestModel
{
[Required]
public int SeatAdjustment { get; set; }
public int? MaxAutoscaleSeats { get; set; }
}
}

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