using System.Net; using Bit.Api.Models.Public.Request; using Bit.Api.Models.Public.Response; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.Public.Controllers; [Route("public/collections")] [Authorize("Organization")] public class CollectionsController : Controller { private readonly ICollectionRepository _collectionRepository; private readonly IUpdateCollectionCommand _updateCollectionCommand; private readonly ICurrentContext _currentContext; private readonly IApplicationCacheService _applicationCacheService; public CollectionsController( ICollectionRepository collectionRepository, IUpdateCollectionCommand updateCollectionCommand, ICurrentContext currentContext, IApplicationCacheService applicationCacheService) { _collectionRepository = collectionRepository; _updateCollectionCommand = updateCollectionCommand; _currentContext = currentContext; _applicationCacheService = applicationCacheService; } /// /// Retrieve a collection. /// /// /// Retrieves the details of an existing collection. You need only supply the unique collection identifier /// that was returned upon collection creation. /// /// The identifier of the collection to be retrieved. [HttpGet("{id}")] [ProducesResponseType(typeof(CollectionResponseModel), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task Get(Guid id) { (var collection, var access) = await _collectionRepository.GetByIdWithAccessAsync(id); if (collection == null || collection.OrganizationId != _currentContext.OrganizationId) { return new NotFoundResult(); } var response = new CollectionResponseModel(collection, access.Groups); return new JsonResult(response); } /// /// List all collections. /// /// /// Returns a list of your organization's collections. /// Collection objects listed in this call do not include information about their associated groups. /// [HttpGet] [ProducesResponseType(typeof(ListResponseModel), (int)HttpStatusCode.OK)] public async Task List() { var collections = await _collectionRepository.GetManyByOrganizationIdAsync( _currentContext.OrganizationId.Value); // TODO: Get all CollectionGroup associations for the organization and marry them up here for the response. var collectionResponses = collections.Select(c => new CollectionResponseModel(c, null)); var response = new ListResponseModel(collectionResponses); return new JsonResult(response); } /// /// Update a collection. /// /// /// Updates the specified collection object. If a property is not provided, /// the value of the existing property will be reset. /// /// The identifier of the collection to be updated. /// The request model. [HttpPut("{id}")] [ProducesResponseType(typeof(CollectionResponseModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task Put(Guid id, [FromBody] CollectionUpdateRequestModel model) { var existingCollection = await _collectionRepository.GetByIdAsync(id); if (existingCollection == null || existingCollection.OrganizationId != _currentContext.OrganizationId) { return new NotFoundResult(); } var updatedCollection = model.ToCollection(existingCollection); var associations = model.Groups?.Select(c => c.ToCollectionAccessSelection()).ToList(); await _updateCollectionCommand.UpdateAsync(updatedCollection, associations, null); var response = new CollectionResponseModel(updatedCollection, associations); return new JsonResult(response); } /// /// Delete a collection. /// /// /// Permanently deletes a collection. This cannot be undone. /// /// The identifier of the collection to be deleted. [HttpDelete("{id}")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task Delete(Guid id) { var collection = await _collectionRepository.GetByIdAsync(id); if (collection == null || collection.OrganizationId != _currentContext.OrganizationId) { return new NotFoundResult(); } if (collection.Type == CollectionType.DefaultUserCollection) { return new BadRequestObjectResult(new ErrorResponseModel("You cannot delete a collection with the type as DefaultUserCollection.")); } await _collectionRepository.DeleteAsync(collection); return new OkResult(); } }