diff --git a/src/Cryptie.Server/DatabaseUpdater.cs b/src/Cryptie.Server/DatabaseUpdater.cs index 9515303..6ed5a8d 100644 --- a/src/Cryptie.Server/DatabaseUpdater.cs +++ b/src/Cryptie.Server/DatabaseUpdater.cs @@ -12,6 +12,10 @@ public DatabaseUpdater(IServiceProvider serviceProvider) _serviceProvider = serviceProvider; } + /// + /// Applies pending Entity Framework migrations on application startup. + /// + /// A task representing the asynchronous operation. public async Task PerformDatabaseUpdate() { using var scope = _serviceProvider.CreateScope(); diff --git a/src/Cryptie.Server/DockerStarter.cs b/src/Cryptie.Server/DockerStarter.cs index e658c74..8f01d79 100644 --- a/src/Cryptie.Server/DockerStarter.cs +++ b/src/Cryptie.Server/DockerStarter.cs @@ -5,6 +5,10 @@ namespace Cryptie.Server; public class DockerStarter { + /// + /// Starts a local PostgreSQL container if it is not running. + /// + /// A task representing the asynchronous startup process. public async Task StartPostgresAsync() { var client = new DockerClientConfiguration().CreateClient(); diff --git a/src/Cryptie.Server/Features/Authentication/Services/AuthenticationService.cs b/src/Cryptie.Server/Features/Authentication/Services/AuthenticationService.cs index e1abdfd..53d60c5 100644 --- a/src/Cryptie.Server/Features/Authentication/Services/AuthenticationService.cs +++ b/src/Cryptie.Server/Features/Authentication/Services/AuthenticationService.cs @@ -15,6 +15,15 @@ public class AuthenticationService( IDatabaseService databaseService) : ControllerBase, IAuthenticationService { + /// + /// Handles the first phase of login by verifying provided credentials + /// and issuing a temporary TOTP token when successful. + /// + /// Login credentials. + /// + /// containing a TOTP token when + /// credentials are valid; otherwise an error result. + /// public IActionResult LoginHandler(LoginRequestDto loginRequest) { var user = appDbContext.Users @@ -40,6 +49,12 @@ public IActionResult LoginHandler(LoginRequestDto loginRequest) return Ok(new LoginResponseDto { TotpToken = totpToken }); } + /// + /// Validates the provided TOTP code and issues a session token when + /// the code is correct. + /// + /// Request containing TOTP code and token. + /// Session token on success or an error result. public IActionResult TotpHandler(TotpRequestDto totpRequest) { var now = DateTime.UtcNow; @@ -72,6 +87,12 @@ public IActionResult TotpHandler(TotpRequestDto totpRequest) }); } + /// + /// Registers a new user and returns the initial TOTP configuration + /// along with a TOTP token for verification. + /// + /// Information required to create the user. + /// Registration details including the TOTP secret. public IActionResult RegisterHandler(RegisterRequestDto registerRequest) { if (databaseService.FindUserByLogin(registerRequest.Login) != null) return BadRequest(); diff --git a/src/Cryptie.Server/Features/Authentication/Services/DelayService.cs b/src/Cryptie.Server/Features/Authentication/Services/DelayService.cs index a977c3d..3942fa9 100644 --- a/src/Cryptie.Server/Features/Authentication/Services/DelayService.cs +++ b/src/Cryptie.Server/Features/Authentication/Services/DelayService.cs @@ -7,6 +7,12 @@ public class DelayService : IDelayService { private const int TargetMilliseconds = 100; + /// + /// Executes the given action and ensures that the call takes at least a + /// predefined amount of time. Useful to mitigate timing attacks. + /// + /// Action returning an to execute. + /// Result of after the artificial delay. public async Task FakeDelay(Func func) { var stopwatch = Stopwatch.StartNew(); diff --git a/src/Cryptie.Server/Features/Authentication/Services/LockoutService.cs b/src/Cryptie.Server/Features/Authentication/Services/LockoutService.cs index dc72f65..beaf518 100644 --- a/src/Cryptie.Server/Features/Authentication/Services/LockoutService.cs +++ b/src/Cryptie.Server/Features/Authentication/Services/LockoutService.cs @@ -5,6 +5,13 @@ namespace Cryptie.Server.Features.Authentication.Services; public class LockoutService(IAppDbContext appDbContext) : ILockoutService { + /// + /// Determines whether a user or honeypot account should be locked out + /// based on failed login attempts. + /// + /// Real user instance if available. + /// Login of a honeypot account when no real user exists. + /// true if the account is locked out; otherwise false. public bool IsUserLockedOut(User? user, string honeypotLogin = "") { var referenceLockTimestamp = DateTime.UtcNow.AddMinutes(-60); @@ -28,28 +35,56 @@ public bool IsUserLockedOut(User? user, string honeypotLogin = "") return true; } + /// + /// Checks if the specified user has an active lockout entry. + /// + /// User entity to check. + /// Timestamp defining the lockout window. + /// true when the user has an active lock. public bool IsUserAccountHasLock(User user, DateTime referenceLockTimestamp) { return appDbContext.UserAccountLocks.Any(l => l.User == user && l.Until > referenceLockTimestamp); } + /// + /// Checks if the honeypot account has an active lockout entry. + /// + /// Honeypot login to check. + /// Timestamp defining the lockout window. + /// true when the account has an active lock. public bool IsUserAccountHasLock(string user, DateTime referenceLockTimestamp) { return appDbContext.HoneypotAccountLocks.Any(l => l.Username == user && l.Until > referenceLockTimestamp); } + /// + /// Evaluates if the user has exceeded the allowed login attempts. + /// + /// User entity to check. + /// Timestamp defining the attempts window. + /// true if there are too many attempts. public bool IsUserAccountHasTooManyAttempts(User user, DateTime referenceAttemptTimestamp) { return appDbContext.UserLoginAttempts.Count(a => a.User == user && a.Timestamp > referenceAttemptTimestamp) < 2; } + /// + /// Evaluates if the honeypot account has exceeded the allowed login attempts. + /// + /// Honeypot login to check. + /// Timestamp defining the attempts window. + /// true if there are too many attempts. public bool IsUserAccountHasTooManyAttempts(string user, DateTime referenceAttemptTimestamp) { return appDbContext.HoneypotLoginAttempts.Count(a => a.Username == user && a.Timestamp > referenceAttemptTimestamp) < 2; } + /// + /// Creates a lockout entry for the specified user. + /// + /// User entity to lock. public void LockUserAccount(User user) { appDbContext.UserAccountLocks.Add(new UserAccountLock @@ -62,6 +97,10 @@ public void LockUserAccount(User user) appDbContext.SaveChanges(); } + /// + /// Creates a lockout entry for the specified honeypot login. + /// + /// Honeypot login to lock. public void LockUserAccount(string user) { appDbContext.HoneypotAccountLocks.Add(new HoneypotAccountLock diff --git a/src/Cryptie.Server/Features/GroupManagement/Services/GroupManagementService.cs b/src/Cryptie.Server/Features/GroupManagement/Services/GroupManagementService.cs index 6686cc1..a76feac 100644 --- a/src/Cryptie.Server/Features/GroupManagement/Services/GroupManagementService.cs +++ b/src/Cryptie.Server/Features/GroupManagement/Services/GroupManagementService.cs @@ -9,6 +9,11 @@ public class GroupManagementService( IDatabaseService databaseService ) : ControllerBase, IGroupManagementService { + /// + /// Determines privacy status for a collection of groups. + /// + /// List of group identifiers. + /// Dictionary with group ids and their privacy status. public IActionResult IsGroupsPrivate(IsGroupsPrivateRequestDto isGroupsPrivateRequest) { var result = new Dictionary(); @@ -22,6 +27,12 @@ public IActionResult IsGroupsPrivate(IsGroupsPrivateRequestDto isGroupsPrivateRe return Ok(new IsGroupsPrivateResponseDto { GroupStatuses = result }); } + /// + /// Returns display names for all groups the user is a member of. + /// Private groups are represented by the other member's display name. + /// + /// Request containing user session token. + /// Mapping of group ids to their display names. public IActionResult GetGroupsNames([FromBody] GetGroupsNamesRequestDto getGroupsNamesRequest) { var user = databaseService.GetUserFromToken(getGroupsNamesRequest.SessionToken); diff --git a/src/Cryptie.Server/Features/KeysManagement/Services/KeysManagementService.cs b/src/Cryptie.Server/Features/KeysManagement/Services/KeysManagementService.cs index 7cf98ec..283661b 100644 --- a/src/Cryptie.Server/Features/KeysManagement/Services/KeysManagementService.cs +++ b/src/Cryptie.Server/Features/KeysManagement/Services/KeysManagementService.cs @@ -6,6 +6,11 @@ namespace Cryptie.Server.Features.KeysManagement.Services; public class KeysManagementService(IDatabaseService databaseService) : ControllerBase, IKeysManagementService { + /// + /// Retrieves public key of specified user. + /// + /// Request containing the user identifier. + /// DTO with the public key string. public IActionResult getUserKey([FromBody] GetUserKeyRequestDto getUserKeyRequest) { var key = databaseService.GetUserPublicKey(getUserKeyRequest.UserId); @@ -15,6 +20,11 @@ public IActionResult getUserKey([FromBody] GetUserKeyRequestDto getUserKeyReques }); } + /// + /// Retrieves AES keys for all groups the current user belongs to. + /// + /// Request containing the session token. + /// Dictionary mapping group ids to their AES keys. public IActionResult getGroupsKey([FromBody] GetGroupsKeyRequestDto getGroupsKeyRequest) { var user = databaseService.GetUserFromToken(getGroupsKeyRequest.SessionToken); diff --git a/src/Cryptie.Server/Features/Messages/Hubs/MessageHub.cs b/src/Cryptie.Server/Features/Messages/Hubs/MessageHub.cs index 8f18975..4349a38 100644 --- a/src/Cryptie.Server/Features/Messages/Hubs/MessageHub.cs +++ b/src/Cryptie.Server/Features/Messages/Hubs/MessageHub.cs @@ -7,11 +7,20 @@ public class MessageHub : Hub, IMessageHub { private static readonly ConcurrentDictionary Users = new(); + /// + /// Broadcasts a plain text message to all connected clients. + /// + /// Message to broadcast. public async Task SendMessage(string message) { await Clients.All.SendAsync("ReceiveMessage", message); } + /// + /// Adds the current connection to a SignalR group and notifies other members. + /// + /// Identifier of the joining user. + /// Group identifier. public async Task JoinGroup(Guid user, Guid group) { Users.TryAdd(Context.ConnectionId, user); @@ -19,6 +28,12 @@ public async Task JoinGroup(Guid user, Guid group) await Clients.Group(group.ToString()).SendAsync("UserJoinedGroup", user, group); } + /// + /// Sends a message to all members of a group. + /// + /// Group identifier. + /// Sender identifier. + /// Encrypted message payload. public async Task SendMessageToGroup(Guid group, Guid senderId, string message) { await Clients diff --git a/src/Cryptie.Server/Features/Messages/Services/MessageHubService.cs b/src/Cryptie.Server/Features/Messages/Services/MessageHubService.cs index e4c48ba..f12e08d 100644 --- a/src/Cryptie.Server/Features/Messages/Services/MessageHubService.cs +++ b/src/Cryptie.Server/Features/Messages/Services/MessageHubService.cs @@ -12,6 +12,12 @@ public MessageHubService(IHubContext hubContext) _hubContext = hubContext; } + /// + /// Sends a real-time notification to all clients connected to the specified group. + /// + /// Group identifier. + /// Identifier of the message sender. + /// Encrypted message body. public void SendMessageToGroup(Guid group, Guid senderId, string message) { _hubContext.Clients.Group(group.ToString()) diff --git a/src/Cryptie.Server/Features/Messages/Services/MessagesService.cs b/src/Cryptie.Server/Features/Messages/Services/MessagesService.cs index b7001cb..08d0814 100644 --- a/src/Cryptie.Server/Features/Messages/Services/MessagesService.cs +++ b/src/Cryptie.Server/Features/Messages/Services/MessagesService.cs @@ -7,6 +7,11 @@ namespace Cryptie.Server.Features.Messages.Services; public class MessagesService(IDatabaseService databaseService, IMessageHubService messageHubService) : ControllerBase, IMessagesService { + /// + /// Sends a message to a specified group on behalf of the authenticated user. + /// + /// Request containing message and authentication data. + /// when the message is sent. public IActionResult SendMessage([FromBody] SendMessageRequestDto sendMessageRequest) { var user = databaseService.GetUserFromToken(sendMessageRequest.SenderToken); @@ -24,6 +29,11 @@ public IActionResult SendMessage([FromBody] SendMessageRequestDto sendMessageReq return Ok(); } + /// + /// Retrieves all messages for a particular group. + /// + /// Request containing user token and group identifier. + /// List of messages. public IActionResult GetGroupMessages([FromBody] GetGroupMessagesRequestDto request) { var user = databaseService.GetUserFromToken(request.UserToken); @@ -49,6 +59,11 @@ public IActionResult GetGroupMessages([FromBody] GetGroupMessagesRequestDto requ return Ok(response); } + /// + /// Retrieves messages for a group that were sent after the specified timestamp. + /// + /// Request containing group id, token and timestamp. + /// List of messages sent since the provided date. public IActionResult GetGroupMessagesSince([FromBody] GetGroupMessagesSinceRequestDto request) { var user = databaseService.GetUserFromToken(request.UserToken); diff --git a/src/Cryptie.Server/Features/ServerStatus/Services/ServerStatusService.cs b/src/Cryptie.Server/Features/ServerStatus/Services/ServerStatusService.cs index c3173d0..e84c2f5 100644 --- a/src/Cryptie.Server/Features/ServerStatus/Services/ServerStatusService.cs +++ b/src/Cryptie.Server/Features/ServerStatus/Services/ServerStatusService.cs @@ -4,6 +4,10 @@ namespace Cryptie.Server.Features.ServerStatus.Services; public class ServerStatusService : ControllerBase, IServerStatusService { + /// + /// Simple health-check endpoint. + /// + /// when the server is running. public IActionResult GetServerStatus() { return Ok(); diff --git a/src/Cryptie.Server/Features/UserManagement/Services/UserManagementService.cs b/src/Cryptie.Server/Features/UserManagement/Services/UserManagementService.cs index ea0615d..379f35c 100644 --- a/src/Cryptie.Server/Features/UserManagement/Services/UserManagementService.cs +++ b/src/Cryptie.Server/Features/UserManagement/Services/UserManagementService.cs @@ -6,6 +6,11 @@ namespace Cryptie.Server.Features.UserManagement.Services; public class UserManagementService(IDatabaseService databaseService) : ControllerBase, IUserManagementService { + /// + /// Returns user GUID associated with a session token. + /// + /// Request containing session token. + /// User identifier on success. public IActionResult UserGuidFromToken([FromBody] UserGuidFromTokenRequestDto userGuidFromTokenRequest) { var user = databaseService.GetUserFromToken(userGuidFromTokenRequest.SessionToken); @@ -17,6 +22,11 @@ public IActionResult UserGuidFromToken([FromBody] UserGuidFromTokenRequestDto us }); } + /// + /// Adds a user to the current user's friend list and creates a private group for them. + /// + /// Request containing friend login and encryption keys. + /// when the friend was added. public IActionResult AddFriend([FromBody] AddFriendRequestDto addFriendRequest) { var friend = databaseService.FindUserByLogin(addFriendRequest.Friend); @@ -44,6 +54,11 @@ public IActionResult AddFriend([FromBody] AddFriendRequestDto addFriendRequest) return Ok(); } + /// + /// Retrieves the display name of a user by their GUID. + /// + /// Request with the user's id. + /// Display name of the user. public IActionResult NameFromGuid([FromBody] NameFromGuidRequestDto nameFromGuidRequest) { var user = databaseService.FindUserById(nameFromGuidRequest.Id); @@ -55,6 +70,11 @@ public IActionResult NameFromGuid([FromBody] NameFromGuidRequestDto nameFromGuid }); } + /// + /// Returns identifiers of groups the user belongs to. + /// + /// Request containing session token. + /// List of group identifiers. public IActionResult UserGroups([FromBody] UserGroupsRequestDto userGroupsRequest) { var user = databaseService.GetUserFromToken(userGroupsRequest.SessionToken); @@ -68,6 +88,11 @@ public IActionResult UserGroups([FromBody] UserGroupsRequestDto userGroupsReques }); } + /// + /// Changes the display name of the authenticated user. + /// + /// Request containing token and new name. + /// when the change was applied. public IActionResult UserDisplayName([FromBody] UserDisplayNameRequestDto userDisplayNameRequest) { var user = databaseService.GetUserFromToken(userDisplayNameRequest.Token); @@ -78,6 +103,11 @@ public IActionResult UserDisplayName([FromBody] UserDisplayNameRequestDto userDi return Ok(); } + /// + /// Returns the user's private key used for end-to-end encryption. + /// + /// Request containing session token. + /// Private key and associated control value. public IActionResult UserPrivateKey([FromBody] UserPrivateKeyRequestDto userPrivateKeyRequest) { var user = databaseService.GetUserFromToken(userPrivateKeyRequest.SessionToken); @@ -90,6 +120,11 @@ public IActionResult UserPrivateKey([FromBody] UserPrivateKeyRequestDto userPriv }); } + /// + /// Retrieves the unique identifier of a user from their login name. + /// + /// Request containing user login. + /// User identifier when found. public IActionResult GuidFromLogin([FromBody] GuidFromLoginRequestDto guidFromLoginRequest) { var user = databaseService.FindUserByLogin(guidFromLoginRequest.Login); diff --git a/src/Cryptie.Server/Services/DatabaseService.cs b/src/Cryptie.Server/Services/DatabaseService.cs index 05a06fe..720ecb4 100644 --- a/src/Cryptie.Server/Services/DatabaseService.cs +++ b/src/Cryptie.Server/Services/DatabaseService.cs @@ -6,6 +6,11 @@ namespace Cryptie.Server.Services; public class DatabaseService(IAppDbContext appDbContext) : IDatabaseService { + /// + /// Retrieves a user entity from a session token. + /// + /// Token identifier. + /// User entity or null when not found. public User? GetUserFromToken(Guid guid) { return appDbContext.UserTokens @@ -17,18 +22,33 @@ public class DatabaseService(IAppDbContext appDbContext) : IDatabaseService .User; } + /// + /// Finds a user by its unique identifier. + /// + /// User identifier. + /// User entity or null. public User? FindUserById(Guid id) { var user = appDbContext.Users.Find(id); return user; } + /// + /// Locates a user by login name. + /// + /// Login name. + /// User entity or null. public User? FindUserByLogin(string login) { var user = appDbContext.Users.FirstOrDefault(u => u.Login == login); return user; } + /// + /// Retrieves a group by its identifier including member list. + /// + /// Group identifier. + /// Group entity or null. public Group? FindGroupById(Guid id) { return appDbContext.Groups @@ -36,6 +56,12 @@ public class DatabaseService(IAppDbContext appDbContext) : IDatabaseService .SingleOrDefault(g => g.Id == id); } + /// + /// Creates a new group and assigns the specified user as a member. + /// + /// Initial member. + /// Name of the group. + /// The newly created group. public Group CreateNewGroup(User user, string name) { var newGroup = new Group @@ -51,6 +77,11 @@ public Group CreateNewGroup(User user, string name) return createdGroup.Entity; } + /// + /// Adds an existing user to an existing group. + /// + /// User identifier. + /// Group identifier. public void AddUserToGroup(Guid user, Guid group) { var userToAdd = appDbContext.Users.SingleOrDefault(u => u.Id == user); @@ -59,6 +90,11 @@ public void AddUserToGroup(Guid user, Guid group) appDbContext.SaveChanges(); } + /// + /// Removes a user from a group. + /// + /// User identifier. + /// Group identifier. public void RemoveUserFromGroup(Guid user, Guid group) { var userToRemove = appDbContext.Users.SingleOrDefault(u => u.Id == user); @@ -67,6 +103,11 @@ public void RemoveUserFromGroup(Guid user, Guid group) appDbContext.SaveChanges(); } + /// + /// Deletes a group from the database. + /// + /// Group identifier. + /// true if removed; otherwise false. public bool DeleteGroup(Guid groupGuid) { var group = appDbContext.Groups.SingleOrDefault(g => g.Id == groupGuid); @@ -76,6 +117,12 @@ public bool DeleteGroup(Guid groupGuid) return true; } + /// + /// Changes the name of a group. + /// + /// Group identifier. + /// New name. + /// true if the group exists and was updated. public bool ChangeGroupName(Guid groupGuid, string name) { var group = appDbContext.Groups.SingleOrDefault(g => g.Id == groupGuid); @@ -87,6 +134,11 @@ public bool ChangeGroupName(Guid groupGuid, string name) return true; } + /// + /// Creates a TOTP token entity for the user. + /// + /// User to associate the token with. + /// Identifier of the created token. public Guid CreateTotpToken(User user) { var totpToken = appDbContext.TotpTokens.Add(new TotpToken @@ -101,6 +153,10 @@ public Guid CreateTotpToken(User user) return totpToken.Entity.Id; } + /// + /// Logs a failed login attempt for a real user. + /// + /// User entity. public void LogLoginAttempt(User user) { appDbContext.UserLoginAttempts.Add(new UserLoginAttempt @@ -113,6 +169,10 @@ public void LogLoginAttempt(User user) appDbContext.SaveChanges(); } + /// + /// Logs a failed login attempt for a honeypot account. + /// + /// Honeypot login name. public void LogLoginAttempt(string user) { appDbContext.HoneypotLoginAttempts.Add(new HoneypotLoginAttempt @@ -125,6 +185,11 @@ public void LogLoginAttempt(string user) appDbContext.SaveChanges(); } + /// + /// Generates and persists a new session token for the user. + /// + /// User entity. + /// Identifier of the new token. public Guid GenerateUserToken(User user) { var token = appDbContext.UserTokens.Add(new UserToken @@ -138,12 +203,23 @@ public Guid GenerateUserToken(User user) return token.Entity.Id; } + /// + /// Adds a friend relationship between two users. + /// + /// User initiating the friendship. + /// Friend user. public void AddFriend(User user, User friend) { user.Friends.Add(friend); appDbContext.SaveChanges(); } + /// + /// Creates a new group without assigning members. + /// + /// Name of the group. + /// Whether the group is private. + /// The created group. public Group CreateGroup(string name, bool isPrivate = false) { var group = appDbContext.Groups.Add(new Group @@ -157,6 +233,11 @@ public Group CreateGroup(string name, bool isPrivate = false) return group.Entity; } + /// + /// Updates the display name of the specified user. + /// + /// User entity. + /// New display name. public void ChangeUserDisplayName(User user, string name) { user.DisplayName = name; @@ -164,6 +245,11 @@ public void ChangeUserDisplayName(User user, string name) appDbContext.SaveChanges(); } + /// + /// Retrieves stored public key of a user. + /// + /// User identifier. + /// Public key string or empty when not found. public string GetUserPublicKey(Guid userId) { var user = appDbContext.Users @@ -173,6 +259,12 @@ public string GetUserPublicKey(Guid userId) return user?.PublicKey ?? string.Empty; } + /// + /// Stores new key pair for the user. + /// + /// User entity. + /// Private key string. + /// Public key string. public void SaveUserKeys(User user, string privateKey, string publicKey) { user.PrivateKey = privateKey; @@ -181,6 +273,13 @@ public void SaveUserKeys(User user, string privateKey, string publicKey) appDbContext.SaveChanges(); } + /// + /// Persists a new message in the specified group. + /// + /// Target group. + /// Sender user. + /// Message content. + /// The created entity. public GroupMessage SendGroupMessage(Group group, User sender, string message) { var groupMessage = new GroupMessage @@ -198,6 +297,12 @@ public GroupMessage SendGroupMessage(Group group, User sender, string message) return groupMessage; } + /// + /// Retrieves a single message by id from a group. + /// + /// Message identifier. + /// Group identifier. + /// Message entity or null. public GroupMessage? GetGroupMessage(Guid messageId, Guid groupId) { return appDbContext.GroupMessages @@ -207,6 +312,11 @@ public GroupMessage SendGroupMessage(Group group, User sender, string message) .FirstOrDefault(m => m.Id == messageId && m.GroupId == groupId); } + /// + /// Retrieves all messages for the specified group ordered by date. + /// + /// Group identifier. + /// List of messages. public List GetGroupMessages(Guid groupId) { return appDbContext.GroupMessages @@ -216,6 +326,12 @@ public List GetGroupMessages(Guid groupId) .ToList(); } + /// + /// Retrieves all messages for a group after the specified timestamp. + /// + /// Group identifier. + /// Earliest timestamp. + /// List of messages. public List GetGroupMessagesSince(Guid groupId, DateTime since) { return appDbContext.GroupMessages @@ -225,6 +341,12 @@ public List GetGroupMessagesSince(Guid groupId, DateTime since) .ToList(); } + /// + /// Adds an AES encryption key for a user-group pair. + /// + /// User identifier. + /// Group identifier. + /// Encryption key. public void AddGroupEncryptionKey(Guid userId, Guid groupId, string key) { var user = appDbContext.Users @@ -250,6 +372,12 @@ public void AddGroupEncryptionKey(Guid userId, Guid groupId, string key) appDbContext.SaveChanges(); } + /// + /// Retrieves the AES encryption key for a user-group pair. + /// + /// User identifier. + /// Group identifier. + /// Encryption key string or empty when not set. public string getGroupEncryptionKey(Guid userId, Guid groupId) { var key = appDbContext.GroupEncryptionKeys