Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-11162] Assign to Collection Permission Update #4844

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions src/Api/Vault/Controllers/CiphersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,59 @@
return false;
}

/// <summary>
/// TODO: Move this to its own authorization handler or equivalent service - AC-2062
/// </summary>
private async Task<bool> CanModifyCipherCollectionsAsync(Guid organizationId, IEnumerable<Guid> cipherIds)
{

Check warning on line 428 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L428

Added line #L428 was not covered by tests
// If the user can edit all ciphers for the organization, just check they all belong to the org
if (await CanEditAllCiphersAsync(organizationId))
{

Check warning on line 431 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L431

Added line #L431 was not covered by tests
// TODO: This can likely be optimized to only query the requested ciphers and then checking they belong to the org
var orgCiphers = (await _cipherRepository.GetManyByOrganizationIdAsync(organizationId)).ToDictionary(c => c.Id);

Check warning on line 433 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L433

Added line #L433 was not covered by tests

// Ensure all requested ciphers are in orgCiphers
if (cipherIds.Any(c => !orgCiphers.ContainsKey(c)))
{
return false;

Check warning on line 438 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L436-L438

Added lines #L436 - L438 were not covered by tests
}

return true;

Check warning on line 441 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L441

Added line #L441 was not covered by tests
}

// The user cannot access any ciphers for the organization, we're done
if (!await CanAccessOrganizationCiphersAsync(organizationId))
{
return false;

Check warning on line 447 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L446-L447

Added lines #L446 - L447 were not covered by tests
}

var userId = _userService.GetProperUserId(User).Value;

Check warning on line 450 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L450

Added line #L450 was not covered by tests
// Select all editable ciphers for this user belonging to the organization
var editableOrgCipherList = (await _cipherRepository.GetManyByUserIdAsync(userId, true))

Check warning on line 452 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L452

Added line #L452 was not covered by tests
.Where(c => c.OrganizationId == organizationId && c.UserId == null && c.Edit && c.ViewPassword).ToList();

// Special case for unassigned ciphers
if (await CanAccessUnassignedCiphersAsync(organizationId))
{
var unassignedCiphers =
(await _cipherRepository.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(
organizationId));

Check warning on line 460 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L457-L460

Added lines #L457 - L460 were not covered by tests

// Users that can access unassigned ciphers can also edit them
editableOrgCipherList.AddRange(unassignedCiphers.Select(c => new CipherDetails(c) { Edit = true }));
}

Check warning on line 464 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L463-L464

Added lines #L463 - L464 were not covered by tests

var editableOrgCiphers = editableOrgCipherList
.ToDictionary(c => c.Id);

Check warning on line 467 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L466-L467

Added lines #L466 - L467 were not covered by tests

if (cipherIds.Any(c => !editableOrgCiphers.ContainsKey(c)))
{
return false;

Check warning on line 471 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L469-L471

Added lines #L469 - L471 were not covered by tests
}

return true;
}

Check warning on line 475 in src/Api/Vault/Controllers/CiphersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/Vault/Controllers/CiphersController.cs#L474-L475

Added lines #L474 - L475 were not covered by tests

/// <summary>
/// TODO: Move this to its own authorization handler or equivalent service - AC-2062
/// </summary>
Expand Down Expand Up @@ -576,7 +629,7 @@
var userId = _userService.GetProperUserId(User).Value;
var cipher = await GetByIdAsync(id, userId);
if (cipher == null || !cipher.OrganizationId.HasValue ||
!await _currentContext.OrganizationUser(cipher.OrganizationId.Value))
!await _currentContext.OrganizationUser(cipher.OrganizationId.Value) || !cipher.ViewPassword)
{
throw new NotFoundException();
}
Expand Down Expand Up @@ -626,7 +679,7 @@
[HttpPost("bulk-collections")]
public async Task PostBulkCollections([FromBody] CipherBulkUpdateCollectionsRequestModel model)
{
if (!await CanEditCiphersAsync(model.OrganizationId, model.CipherIds) ||
if (!await CanModifyCipherCollectionsAsync(model.OrganizationId, model.CipherIds) ||
!await CanEditItemsInCollections(model.OrganizationId, model.CollectionIds))
{
throw new NotFoundException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,41 @@ AS
BEGIN
SET NOCOUNT ON

SELECT TOP 1
*
SELECT
[Id],
[UserId],
[OrganizationId],
[Type],
[Data],
[Attachments],
[CreationDate],
[RevisionDate],
[Favorite],
[FolderId],
[DeletedDate],
[Reprompt],
[Key],
[OrganizationUseTotp]
, MAX ([Edit]) AS [Edit]
, MAX ([ViewPassword]) AS [ViewPassword]
FROM
[dbo].[UserCipherDetails](@UserId)
WHERE
[Id] = @Id
ORDER BY
[Edit] DESC
GROUP BY
[Id],
[UserId],
[OrganizationId],
[Type],
[Data],
[Attachments],
[CreationDate],
[RevisionDate],
[Favorite],
[FolderId],
[DeletedDate],
[Reprompt],
[Key],
[OrganizationUseTotp]

END
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ private CipherDetails CreateCipherDetailsMock(Guid id, Guid userId)
UserId = userId,
OrganizationId = Guid.NewGuid(),
Type = CipherType.Login,
ViewPassword = true,
Data = @"
{
""Uris"": [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
CREATE OR ALTER PROCEDURE [dbo].[CipherDetails_ReadByIdUserId]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON

SELECT
[Id],
[UserId],
[OrganizationId],
[Type],
[Data],
[Attachments],
[CreationDate],
[RevisionDate],
[Favorite],
[FolderId],
[DeletedDate],
[Reprompt],
[Key],
[OrganizationUseTotp],
MAX ([Edit]) AS [Edit],
MAX ([ViewPassword]) AS [ViewPassword]
FROM
[dbo].[UserCipherDetails](@UserId)
WHERE
[Id] = @Id
GROUP BY
[Id],
[UserId],
[OrganizationId],
[Type],
[Data],
[Attachments],
[CreationDate],
[RevisionDate],
[Favorite],
[FolderId],
[DeletedDate],
[Reprompt],
[Key],
[OrganizationUseTotp]
END
GO
Loading