|
|
|
@ -1,10 +1,12 @@ |
|
|
|
using System.Net.Http.Headers; |
|
|
|
using System.Net; |
|
|
|
|
|
|
|
using System.Net.Http.Headers; |
|
|
|
using Bit.Api.IntegrationTest.Factories; |
|
|
|
using Bit.Api.IntegrationTest.Factories; |
|
|
|
using Bit.Api.IntegrationTest.Helpers; |
|
|
|
using Bit.Api.IntegrationTest.Helpers; |
|
|
|
using Bit.Api.Models.Response; |
|
|
|
using Bit.Api.Models.Response; |
|
|
|
using Bit.Api.SecretManagerFeatures.Models.Request; |
|
|
|
using Bit.Api.SecretManagerFeatures.Models.Request; |
|
|
|
using Bit.Api.SecretManagerFeatures.Models.Response; |
|
|
|
using Bit.Api.SecretManagerFeatures.Models.Response; |
|
|
|
using Bit.Core.Entities; |
|
|
|
using Bit.Core.Entities; |
|
|
|
|
|
|
|
using Bit.Core.Enums; |
|
|
|
using Bit.Core.Repositories; |
|
|
|
using Bit.Core.Repositories; |
|
|
|
using Bit.Test.Common.Helpers; |
|
|
|
using Bit.Test.Common.Helpers; |
|
|
|
using Xunit; |
|
|
|
using Xunit; |
|
|
|
@ -13,49 +15,73 @@ namespace Bit.Api.IntegrationTest.Controllers; |
|
|
|
|
|
|
|
|
|
|
|
public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory>, IAsyncLifetime |
|
|
|
public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory>, IAsyncLifetime |
|
|
|
{ |
|
|
|
{ |
|
|
|
private readonly string _mockEncryptedString = |
|
|
|
private const string _mockEncryptedString = |
|
|
|
"2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; |
|
|
|
"2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private const string _mockNewName = |
|
|
|
|
|
|
|
"2.3AZ+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private readonly IAccessPolicyRepository _accessPolicyRepository; |
|
|
|
private readonly HttpClient _client; |
|
|
|
private readonly HttpClient _client; |
|
|
|
private readonly ApiApplicationFactory _factory; |
|
|
|
private readonly ApiApplicationFactory _factory; |
|
|
|
private readonly IServiceAccountRepository _serviceAccountRepository; |
|
|
|
private readonly IServiceAccountRepository _serviceAccountRepository; |
|
|
|
private Organization _organization = null!; |
|
|
|
private Organization _organization = null!; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ServiceAccountsControllerTest(ApiApplicationFactory factory) |
|
|
|
public ServiceAccountsControllerTest(ApiApplicationFactory factory) |
|
|
|
{ |
|
|
|
{ |
|
|
|
_factory = factory; |
|
|
|
_factory = factory; |
|
|
|
_client = _factory.CreateClient(); |
|
|
|
_client = _factory.CreateClient(); |
|
|
|
_serviceAccountRepository = _factory.GetService<IServiceAccountRepository>(); |
|
|
|
_serviceAccountRepository = _factory.GetService<IServiceAccountRepository>(); |
|
|
|
|
|
|
|
_accessPolicyRepository = _factory.GetService<IAccessPolicyRepository>(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public async Task InitializeAsync() |
|
|
|
public async Task InitializeAsync() |
|
|
|
{ |
|
|
|
{ |
|
|
|
var ownerEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com"; |
|
|
|
var ownerEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com"; |
|
|
|
var tokens = await _factory.LoginWithNewAccount(ownerEmail); |
|
|
|
await _factory.LoginWithNewAccount(ownerEmail); |
|
|
|
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory, ownerEmail: ownerEmail, billingEmail: ownerEmail); |
|
|
|
(_organization, _) = |
|
|
|
|
|
|
|
await OrganizationTestHelpers.SignUpAsync(_factory, ownerEmail: ownerEmail, billingEmail: ownerEmail); |
|
|
|
|
|
|
|
var tokens = await _factory.LoginAsync(ownerEmail); |
|
|
|
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token); |
|
|
|
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token); |
|
|
|
_organization = organization; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Task DisposeAsync() |
|
|
|
public Task DisposeAsync() => Task.CompletedTask; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
|
|
public async Task GetServiceAccountsByOrganization_Admin() |
|
|
|
{ |
|
|
|
{ |
|
|
|
return Task.CompletedTask; |
|
|
|
var serviceAccountIds = await SetupGetServiceAccountsByOrganizationAsync(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.GetAsync($"/organizations/{_organization.Id}/service-accounts"); |
|
|
|
|
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<ListResponseModel<ServiceAccountResponseModel>>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Assert.NotNull(result); |
|
|
|
|
|
|
|
Assert.NotEmpty(result!.Data); |
|
|
|
|
|
|
|
Assert.Equal(serviceAccountIds.Count, result.Data.Count()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
[Fact] |
|
|
|
public async Task GetServiceAccountsByOrganization() |
|
|
|
public async Task GetServiceAccountsByOrganization_User_Success() |
|
|
|
{ |
|
|
|
{ |
|
|
|
var serviceAccountsToCreate = 3; |
|
|
|
// Create a new account as a user |
|
|
|
var serviceAccountIds = new List<Guid>(); |
|
|
|
var user = await LoginAsNewOrgUserAsync(); |
|
|
|
for (var i = 0; i < serviceAccountsToCreate; i++) |
|
|
|
|
|
|
|
{ |
|
|
|
var serviceAccountIds = await SetupGetServiceAccountsByOrganizationAsync(); |
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
|
|
|
|
|
|
|
|
var accessPolicies = serviceAccountIds.Select( |
|
|
|
|
|
|
|
id => new UserServiceAccountAccessPolicy |
|
|
|
{ |
|
|
|
{ |
|
|
|
OrganizationId = _organization.Id, |
|
|
|
OrganizationUserId = user.Id, |
|
|
|
Name = _mockEncryptedString, |
|
|
|
GrantedServiceAccountId = id, |
|
|
|
}); |
|
|
|
Read = true, |
|
|
|
serviceAccountIds.Add(serviceAccount.Id); |
|
|
|
Write = false, |
|
|
|
} |
|
|
|
}).Cast<BaseAccessPolicy>().ToList(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await _accessPolicyRepository.CreateManyAsync(accessPolicies); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.GetAsync($"/organizations/{_organization.Id}/service-accounts"); |
|
|
|
var response = await _client.GetAsync($"/organizations/{_organization.Id}/service-accounts"); |
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
@ -67,12 +93,24 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
[Fact] |
|
|
|
public async Task CreateServiceAccount() |
|
|
|
public async Task GetServiceAccountsByOrganization_User_NoPermission() |
|
|
|
{ |
|
|
|
{ |
|
|
|
var request = new ServiceAccountCreateRequestModel() |
|
|
|
// Create a new account as a user |
|
|
|
{ |
|
|
|
await LoginAsNewOrgUserAsync(); |
|
|
|
Name = _mockEncryptedString, |
|
|
|
await SetupGetServiceAccountsByOrganizationAsync(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.GetAsync($"/organizations/{_organization.Id}/service-accounts"); |
|
|
|
|
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<ListResponseModel<ServiceAccountResponseModel>>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Assert.NotNull(result); |
|
|
|
|
|
|
|
Assert.Empty(result!.Data); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
|
|
public async Task CreateServiceAccount_Admin() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var request = new ServiceAccountCreateRequestModel { Name = _mockEncryptedString }; |
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PostAsJsonAsync($"/organizations/{_organization.Id}/service-accounts", request); |
|
|
|
var response = await _client.PostAsJsonAsync($"/organizations/{_organization.Id}/service-accounts", request); |
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
@ -91,7 +129,19 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
[Fact] |
|
|
|
public async Task UpdateServiceAccount() |
|
|
|
public async Task CreateServiceAccount_User_NoPermissions() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Create a new account as a user |
|
|
|
|
|
|
|
await LoginAsNewOrgUserAsync(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var request = new ServiceAccountCreateRequestModel { Name = _mockEncryptedString }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PostAsJsonAsync($"/organizations/{_organization.Id}/service-accounts", request); |
|
|
|
|
|
|
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
|
|
public async Task UpdateServiceAccount_Admin() |
|
|
|
{ |
|
|
|
{ |
|
|
|
var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -99,10 +149,41 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory |
|
|
|
Name = _mockEncryptedString, |
|
|
|
Name = _mockEncryptedString, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
var request = new ServiceAccountUpdateRequestModel() |
|
|
|
var request = new ServiceAccountUpdateRequestModel { Name = _mockNewName }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PutAsJsonAsync($"/service-accounts/{initialServiceAccount.Id}", request); |
|
|
|
|
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<ServiceAccountResponseModel>(); |
|
|
|
|
|
|
|
Assert.NotNull(result); |
|
|
|
|
|
|
|
Assert.Equal(request.Name, result!.Name); |
|
|
|
|
|
|
|
Assert.NotEqual(initialServiceAccount.Name, result.Name); |
|
|
|
|
|
|
|
AssertHelper.AssertRecent(result.RevisionDate); |
|
|
|
|
|
|
|
Assert.NotEqual(initialServiceAccount.RevisionDate, result.RevisionDate); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var updatedServiceAccount = await _serviceAccountRepository.GetByIdAsync(initialServiceAccount.Id); |
|
|
|
|
|
|
|
Assert.NotNull(result); |
|
|
|
|
|
|
|
Assert.Equal(request.Name, updatedServiceAccount.Name); |
|
|
|
|
|
|
|
AssertHelper.AssertRecent(updatedServiceAccount.RevisionDate); |
|
|
|
|
|
|
|
AssertHelper.AssertRecent(updatedServiceAccount.CreationDate); |
|
|
|
|
|
|
|
Assert.NotEqual(initialServiceAccount.Name, updatedServiceAccount.Name); |
|
|
|
|
|
|
|
Assert.NotEqual(initialServiceAccount.RevisionDate, updatedServiceAccount.RevisionDate); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
|
|
public async Task UpdateServiceAccount_User_WithPermission() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Create a new account as a user |
|
|
|
|
|
|
|
var user = await LoginAsNewOrgUserAsync(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
{ |
|
|
|
{ |
|
|
|
Name = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=", |
|
|
|
OrganizationId = _organization.Id, |
|
|
|
}; |
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await CreateUserServiceAccountAccessPolicyAsync(user.Id, initialServiceAccount.Id, true, true); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var request = new ServiceAccountUpdateRequestModel { Name = _mockNewName }; |
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PutAsJsonAsync($"/service-accounts/{initialServiceAccount.Id}", request); |
|
|
|
var response = await _client.PutAsJsonAsync($"/service-accounts/{initialServiceAccount.Id}", request); |
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
@ -123,21 +204,74 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
[Fact] |
|
|
|
public async Task CreateServiceAccountAccessToken() |
|
|
|
public async Task UpdateServiceAccount_User_NoPermissions() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Create a new account as a user |
|
|
|
|
|
|
|
await LoginAsNewOrgUserAsync(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
OrganizationId = _organization.Id, |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var request = new ServiceAccountUpdateRequestModel { Name = _mockNewName }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PutAsJsonAsync($"/service-accounts/{initialServiceAccount.Id}", request); |
|
|
|
|
|
|
|
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
|
|
public async Task CreateServiceAccountAccessToken_Admin() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
OrganizationId = _organization.Id, |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var mockExpiresAt = DateTime.UtcNow.AddDays(30); |
|
|
|
|
|
|
|
var request = new AccessTokenCreateRequestModel |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
EncryptedPayload = _mockEncryptedString, |
|
|
|
|
|
|
|
Key = _mockEncryptedString, |
|
|
|
|
|
|
|
ExpireAt = mockExpiresAt, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens", request); |
|
|
|
|
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<AccessTokenCreationResponseModel>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Assert.NotNull(result); |
|
|
|
|
|
|
|
Assert.Equal(request.Name, result!.Name); |
|
|
|
|
|
|
|
Assert.NotNull(result.ClientSecret); |
|
|
|
|
|
|
|
Assert.Equal(mockExpiresAt, result.ExpireAt); |
|
|
|
|
|
|
|
AssertHelper.AssertRecent(result.RevisionDate); |
|
|
|
|
|
|
|
AssertHelper.AssertRecent(result.CreationDate); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
|
|
public async Task CreateServiceAccountAccessToken_User_WithPermission() |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
// Create a new account as a user |
|
|
|
|
|
|
|
var user = await LoginAsNewOrgUserAsync(); |
|
|
|
|
|
|
|
|
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
{ |
|
|
|
{ |
|
|
|
OrganizationId = _organization.Id, |
|
|
|
OrganizationId = _organization.Id, |
|
|
|
Name = _mockEncryptedString, |
|
|
|
Name = _mockEncryptedString, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await CreateUserServiceAccountAccessPolicyAsync(user.Id, serviceAccount.Id, true, true); |
|
|
|
|
|
|
|
|
|
|
|
var mockExpiresAt = DateTime.UtcNow.AddDays(30); |
|
|
|
var mockExpiresAt = DateTime.UtcNow.AddDays(30); |
|
|
|
var request = new AccessTokenCreateRequestModel() |
|
|
|
var request = new AccessTokenCreateRequestModel |
|
|
|
{ |
|
|
|
{ |
|
|
|
Name = _mockEncryptedString, |
|
|
|
Name = _mockEncryptedString, |
|
|
|
EncryptedPayload = _mockEncryptedString, |
|
|
|
EncryptedPayload = _mockEncryptedString, |
|
|
|
Key = _mockEncryptedString, |
|
|
|
Key = _mockEncryptedString, |
|
|
|
ExpireAt = mockExpiresAt |
|
|
|
ExpireAt = mockExpiresAt, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens", request); |
|
|
|
var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens", request); |
|
|
|
@ -153,7 +287,32 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
[Fact] |
|
|
|
public async Task CreateServiceAccountAccessTokenExpireAtNullAsync() |
|
|
|
public async Task CreateServiceAccountAccessToken_User_NoPermission() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Create a new account as a user |
|
|
|
|
|
|
|
await LoginAsNewOrgUserAsync(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
OrganizationId = _organization.Id, |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var mockExpiresAt = DateTime.UtcNow.AddDays(30); |
|
|
|
|
|
|
|
var request = new AccessTokenCreateRequestModel |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
EncryptedPayload = _mockEncryptedString, |
|
|
|
|
|
|
|
Key = _mockEncryptedString, |
|
|
|
|
|
|
|
ExpireAt = mockExpiresAt, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens", request); |
|
|
|
|
|
|
|
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
|
|
public async Task CreateServiceAccountAccessTokenExpireAtNullAsync_Admin() |
|
|
|
{ |
|
|
|
{ |
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -161,12 +320,12 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory |
|
|
|
Name = _mockEncryptedString, |
|
|
|
Name = _mockEncryptedString, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
var request = new AccessTokenCreateRequestModel() |
|
|
|
var request = new AccessTokenCreateRequestModel |
|
|
|
{ |
|
|
|
{ |
|
|
|
Name = _mockEncryptedString, |
|
|
|
Name = _mockEncryptedString, |
|
|
|
EncryptedPayload = _mockEncryptedString, |
|
|
|
EncryptedPayload = _mockEncryptedString, |
|
|
|
Key = _mockEncryptedString, |
|
|
|
Key = _mockEncryptedString, |
|
|
|
ExpireAt = null |
|
|
|
ExpireAt = null, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens", request); |
|
|
|
var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens", request); |
|
|
|
@ -180,4 +339,105 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory |
|
|
|
AssertHelper.AssertRecent(result.RevisionDate); |
|
|
|
AssertHelper.AssertRecent(result.RevisionDate); |
|
|
|
AssertHelper.AssertRecent(result.CreationDate); |
|
|
|
AssertHelper.AssertRecent(result.CreationDate); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
|
|
public async Task CreateServiceAccountAccessTokenExpireAtNullAsync_User_WithPermission() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Create a new account as a user |
|
|
|
|
|
|
|
var user = await LoginAsNewOrgUserAsync(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
OrganizationId = _organization.Id, |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await CreateUserServiceAccountAccessPolicyAsync(user.Id, serviceAccount.Id, true, true); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var request = new AccessTokenCreateRequestModel |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
EncryptedPayload = _mockEncryptedString, |
|
|
|
|
|
|
|
Key = _mockEncryptedString, |
|
|
|
|
|
|
|
ExpireAt = null, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens", request); |
|
|
|
|
|
|
|
response.EnsureSuccessStatusCode(); |
|
|
|
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<AccessTokenCreationResponseModel>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Assert.NotNull(result); |
|
|
|
|
|
|
|
Assert.Equal(request.Name, result!.Name); |
|
|
|
|
|
|
|
Assert.NotNull(result.ClientSecret); |
|
|
|
|
|
|
|
Assert.Null(result.ExpireAt); |
|
|
|
|
|
|
|
AssertHelper.AssertRecent(result.RevisionDate); |
|
|
|
|
|
|
|
AssertHelper.AssertRecent(result.CreationDate); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
|
|
public async Task CreateServiceAccountAccessTokenExpireAtNullAsync_User_NoPermission() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Create a new account as a user |
|
|
|
|
|
|
|
await LoginAsNewOrgUserAsync(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
OrganizationId = _organization.Id, |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var request = new AccessTokenCreateRequestModel |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
EncryptedPayload = _mockEncryptedString, |
|
|
|
|
|
|
|
Key = _mockEncryptedString, |
|
|
|
|
|
|
|
ExpireAt = null, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var response = await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-tokens", request); |
|
|
|
|
|
|
|
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private async Task<List<Guid>> SetupGetServiceAccountsByOrganizationAsync() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
const int serviceAccountsToCreate = 3; |
|
|
|
|
|
|
|
var serviceAccountIds = new List<Guid>(); |
|
|
|
|
|
|
|
for (var i = 0; i < serviceAccountsToCreate; i++) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
OrganizationId = _organization.Id, |
|
|
|
|
|
|
|
Name = _mockEncryptedString, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
serviceAccountIds.Add(serviceAccount.Id); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return serviceAccountIds; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private async Task CreateUserServiceAccountAccessPolicyAsync(Guid userId, Guid serviceAccountId, bool read, |
|
|
|
|
|
|
|
bool write) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var accessPolicies = new List<BaseAccessPolicy> |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
new UserServiceAccountAccessPolicy |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
OrganizationUserId = userId, |
|
|
|
|
|
|
|
GrantedServiceAccountId = serviceAccountId, |
|
|
|
|
|
|
|
Read = read, |
|
|
|
|
|
|
|
Write = write, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
await _accessPolicyRepository.CreateManyAsync(accessPolicies); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private async Task<OrganizationUser> LoginAsNewOrgUserAsync(OrganizationUserType type = OrganizationUserType.User) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var email = $"integration-test{Guid.NewGuid()}@bitwarden.com"; |
|
|
|
|
|
|
|
await _factory.LoginWithNewAccount(email); |
|
|
|
|
|
|
|
var orgUser = await OrganizationTestHelpers.CreateUserAsync(_factory, _organization.Id, email, type); |
|
|
|
|
|
|
|
var tokens = await _factory.LoginAsync(email); |
|
|
|
|
|
|
|
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token); |
|
|
|
|
|
|
|
return orgUser; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|