mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 07:36:14 -05:00
[SM-380] Access checks for listing projects (#2496)
* Add project access checks for listing
This commit is contained in:
@ -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.Helpers;
|
||||
using Bit.Api.Models.Response;
|
||||
using Bit.Api.SecretManagerFeatures.Models.Request;
|
||||
using Bit.Api.SecretManagerFeatures.Models.Response;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.Helpers;
|
||||
using Xunit;
|
||||
@ -31,10 +33,19 @@ public class ProjectsControllerTest : IClassFixture<ApiApplicationFactory>, IAsy
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
var ownerEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com";
|
||||
var tokens = await _factory.LoginWithNewAccount(ownerEmail);
|
||||
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory, ownerEmail: ownerEmail, billingEmail: ownerEmail);
|
||||
await _factory.LoginWithNewAccount(ownerEmail);
|
||||
(_organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory, ownerEmail: ownerEmail, billingEmail: ownerEmail);
|
||||
var tokens = await _factory.LoginAsync(ownerEmail);
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token);
|
||||
}
|
||||
|
||||
public async Task LoginAsNewOrgUser(OrganizationUserType type = OrganizationUserType.User)
|
||||
{
|
||||
var email = $"integration-test{Guid.NewGuid()}@bitwarden.com";
|
||||
await _factory.LoginWithNewAccount(email);
|
||||
await OrganizationTestHelpers.CreateUserAsync(_factory, _organization.Id, email, type);
|
||||
var tokens = await _factory.LoginAsync(email);
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token);
|
||||
_organization = organization;
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
@ -44,12 +55,9 @@ public class ProjectsControllerTest : IClassFixture<ApiApplicationFactory>, IAsy
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateProject()
|
||||
public async Task CreateProject_Success()
|
||||
{
|
||||
var request = new ProjectCreateRequestModel()
|
||||
{
|
||||
Name = _mockEncryptedString
|
||||
};
|
||||
var request = new ProjectCreateRequestModel { Name = _mockEncryptedString };
|
||||
|
||||
var response = await _client.PostAsJsonAsync($"/organizations/{_organization.Id}/projects", request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
@ -69,7 +77,17 @@ public class ProjectsControllerTest : IClassFixture<ApiApplicationFactory>, IAsy
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateProject()
|
||||
public async Task CreateProject_NoPermission()
|
||||
{
|
||||
var request = new ProjectCreateRequestModel { Name = _mockEncryptedString };
|
||||
|
||||
var response = await _client.PostAsJsonAsync("/organizations/911d9106-7cf1-4d55-a3f9-f9abdeadecb3/projects", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateProject_Success()
|
||||
{
|
||||
var initialProject = await _projectRepository.CreateAsync(new Project
|
||||
{
|
||||
@ -101,6 +119,42 @@ public class ProjectsControllerTest : IClassFixture<ApiApplicationFactory>, IAsy
|
||||
Assert.NotEqual(initialProject.RevisionDate, updatedProject.RevisionDate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateProject_NotFound()
|
||||
{
|
||||
var request = new ProjectUpdateRequestModel()
|
||||
{
|
||||
Name = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=",
|
||||
};
|
||||
|
||||
var response = await _client.PutAsJsonAsync("/projects/c53de509-4581-402c-8cbd-f26d2c516fba", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateProject_MissingPermission()
|
||||
{
|
||||
// Create a new account as a user
|
||||
await LoginAsNewOrgUser();
|
||||
|
||||
var project = await _projectRepository.CreateAsync(new Project
|
||||
{
|
||||
OrganizationId = _organization.Id,
|
||||
Name = _mockEncryptedString
|
||||
});
|
||||
|
||||
|
||||
var request = new ProjectUpdateRequestModel()
|
||||
{
|
||||
Name = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98xy4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=",
|
||||
};
|
||||
|
||||
var response = await _client.PutAsJsonAsync($"/projects/{project.Id}", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetProject()
|
||||
{
|
||||
|
@ -45,4 +45,12 @@ public class ApiApplicationFactory : WebApplicationFactoryBase<Startup>
|
||||
|
||||
return await _identityApplicationFactory.TokenFromPasswordAsync(email, masterPasswordHash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for logging in to an account
|
||||
/// </summary>
|
||||
public async Task<(string Token, string RefreshToken)> LoginAsync(string email = "integration-test@bitwarden.com", string masterPasswordHash = "master_password_hash")
|
||||
{
|
||||
return await _identityApplicationFactory.TokenFromPasswordAsync(email, masterPasswordHash);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,8 @@ namespace Bit.Api.IntegrationTest.Helpers;
|
||||
|
||||
public static class OrganizationTestHelpers
|
||||
{
|
||||
public static async Task<Tuple<Organization, OrganizationUser>> SignUpAsync<T>(WebApplicationFactoryBase<T> factory,
|
||||
public static async Task<Tuple<Organization, OrganizationUser>> SignUpAsync<T>(
|
||||
WebApplicationFactoryBase<T> factory,
|
||||
PlanType plan = PlanType.Free,
|
||||
string ownerEmail = "integration-test@bitwarden.com",
|
||||
string name = "Integration Test Org",
|
||||
@ -30,4 +31,32 @@ public static class OrganizationTestHelpers
|
||||
Owner = owner,
|
||||
});
|
||||
}
|
||||
|
||||
public static async Task<OrganizationUser> CreateUserAsync<T>(
|
||||
WebApplicationFactoryBase<T> factory,
|
||||
Guid organizationId,
|
||||
string userEmail,
|
||||
OrganizationUserType type
|
||||
) where T : class
|
||||
{
|
||||
var userRepository = factory.GetService<IUserRepository>();
|
||||
var organizationUserRepository = factory.GetService<IOrganizationUserRepository>();
|
||||
|
||||
var user = await userRepository.GetByEmailAsync(userEmail);
|
||||
|
||||
var orgUser = new OrganizationUser
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
UserId = user.Id,
|
||||
Key = null,
|
||||
Type = type,
|
||||
Status = OrganizationUserStatusType.Invited,
|
||||
AccessAll = false,
|
||||
ExternalId = null,
|
||||
};
|
||||
|
||||
await organizationUserRepository.CreateAsync(orgUser);
|
||||
|
||||
return orgUser;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Bit.Api.Controllers;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.SecretManagerFeatures.Projects.Interfaces;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Test.AutoFixture.ProjectsFixture;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
@ -19,17 +20,18 @@ public class ProjectsControllerTests
|
||||
[BitAutoData]
|
||||
public async void BulkDeleteProjects_Success(SutProvider<ProjectsController> sutProvider, List<Project> data)
|
||||
{
|
||||
var ids = data.Select(project => project.Id).ToList();
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid());
|
||||
var ids = data.Select(project => project.Id)?.ToList();
|
||||
var mockResult = new List<Tuple<Project, string>>();
|
||||
foreach (var project in data)
|
||||
{
|
||||
mockResult.Add(new Tuple<Project, string>(project, ""));
|
||||
}
|
||||
sutProvider.GetDependency<IDeleteProjectCommand>().DeleteProjects(ids).ReturnsForAnyArgs(mockResult);
|
||||
sutProvider.GetDependency<IDeleteProjectCommand>().DeleteProjects(ids, default).ReturnsForAnyArgs(mockResult);
|
||||
|
||||
var results = await sutProvider.Sut.BulkDeleteProjectsAsync(ids);
|
||||
await sutProvider.GetDependency<IDeleteProjectCommand>().Received(1)
|
||||
.DeleteProjects(Arg.Is(ids));
|
||||
.DeleteProjects(Arg.Is(ids), Arg.Any<Guid>());
|
||||
Assert.Equal(data.Count, results.Data.Count());
|
||||
}
|
||||
|
||||
@ -37,6 +39,7 @@ public class ProjectsControllerTests
|
||||
[BitAutoData]
|
||||
public async void BulkDeleteProjects_NoGuids_ThrowsArgumentNullException(SutProvider<ProjectsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(Guid.NewGuid());
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(() => sutProvider.Sut.BulkDeleteProjectsAsync(new List<Guid>()));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user