1
0
mirror of https://github.com/bitwarden/server synced 2025-12-26 21:23:39 +00:00

[PM-14406] Security Task Notifications (#5344)

* initial commit of `CipherOrganizationPermission_GetManyByUserId`

* create queries to get all of the security tasks that are actionable by a user

- A task is "actionable" when the user has manage permissions for that cipher

* rename query

* return the user's email from the query as well

* Add email notification for at-risk passwords

- Added email layouts for security tasks

* add push notification for security tasks

* update entity framework to match stored procedure plus testing

* update date of migration and remove orderby

* add push service to security task controller

* rename `SyncSecurityTasksCreated` to `SyncNotification`

* remove duplicate return

* remove unused directive

* remove unneeded new notification type

* use `createNotificationCommand` to alert all platforms

* return the cipher id that is associated with the security task and store the security task id on the notification entry

* Add `TaskId` to the output model of `GetUserSecurityTasksByCipherIdsAsync`

* move notification logic to command

* use TaskId from `_getSecurityTasksNotificationDetailsQuery`

* add service

* only push last notification for each user

* formatting

* refactor `CreateNotificationCommand` parameter to `sendPush`

* flip boolean in test

* update interface to match usage

* do not push any of the security related notifications to the user

* add `PendingSecurityTasks` push type

* add push notification for pending security tasks
This commit is contained in:
Nick Krantz
2025-02-27 08:34:42 -06:00
committed by GitHub
parent a2e665cb96
commit 1267332b5b
35 changed files with 893 additions and 8 deletions

View File

@@ -348,6 +348,51 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
}
}
public async Task<ICollection<UserSecurityTaskCipher>> GetUserSecurityTasksByCipherIdsAsync(Guid organizationId, IEnumerable<Core.Vault.Entities.SecurityTask> tasks)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var cipherIds = tasks.Where(t => t.CipherId.HasValue).Select(t => t.CipherId.Value);
var dbContext = GetDatabaseContext(scope);
var query = new UserSecurityTasksByCipherIdsQuery(organizationId, cipherIds).Run(dbContext);
ICollection<UserSecurityTaskCipher> userTaskCiphers;
// SQLite does not support the GROUP BY clause
if (dbContext.Database.IsSqlite())
{
userTaskCiphers = (await query.ToListAsync())
.GroupBy(c => new { c.UserId, c.Email, c.CipherId })
.Select(g => new UserSecurityTaskCipher
{
UserId = g.Key.UserId,
Email = g.Key.Email,
CipherId = g.Key.CipherId,
}).ToList();
}
else
{
var groupByQuery = from p in query
group p by new { p.UserId, p.Email, p.CipherId }
into g
select new UserSecurityTaskCipher
{
UserId = g.Key.UserId,
CipherId = g.Key.CipherId,
Email = g.Key.Email,
};
userTaskCiphers = await groupByQuery.ToListAsync();
}
foreach (var userTaskCipher in userTaskCiphers)
{
userTaskCipher.TaskId = tasks.First(t => t.CipherId == userTaskCipher.CipherId).Id;
}
return userTaskCiphers;
}
}
public async Task<CipherDetails> GetByIdAsync(Guid id, Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())