public async Task <RecipientResponse> RegenerateRecipientPasswordAsync(RegenerateRecipientPasswordRequest regenRequest) { var folder = await connection.Folder.GetAsync(regenRequest.FolderIdentifier); var recipients = folder.MetaEDiscoveryRecipientListRead(); var password = ModuleUtility.GeneratePassword(folder, MetadataKeyConstants.E_DISCOVERY_RND_PASSWORD_LENGTH, EDiscoveryUtility.E_DISCOVERY_DEFAULT_PASSWORD_LENGTH, MetadataKeyConstants.E_DISCOVERY_RND_PASSWORD_CHARS); var recipient = recipients.Where(rec => rec.Email.ToLower() == regenRequest.RecipientEmail.ToLower()).FirstOrDefault(); if (recipient != null) { // Using the special connection here to update their password. var userIdentifier = ModuleUtility.GetFolderScopedUserIdentifier(folder.Identifier, regenRequest.RecipientEmail); await InitializePrivilegedConnectionAsync(); await privilegedConnection.User.PasswordPutAsync(userIdentifier, password.Plain); await this.auditLogStore.AddEntry( new AuditLogEntry() { EntryType = AuditLogEntryType.eDiscoveryRecipientRegenerated, Message = $"An eDiscovery has had their password regenerated {recipient.Email}", ModuleType = Modules.ModuleType.eDiscovery }, folder.Identifier ); recipient.PasswordHash = password.Hashed; recipient.ExpirationDate = ModuleUtility.GetLinkExpirationDate(folder, MetadataKeyConstants.E_DISCOVERY_EXPIRATION_LENGTH_SECONDS).Value; recipient.MagicLink = ModuleUtility.CreateMagicLink(regenRequest, managerConfiguration.EDiscoveryLandingLocation, managerConfiguration.EDiscoveryLinkEncryptionKey, regenRequest.FolderIdentifier, recipient.ExpirationDate, userIdentifier); await connection.ConcurrencyRetryBlock(async() => { folder = await connection.Folder.GetAsync(regenRequest.FolderIdentifier); folder.MetaEDiscoveryRecipientListUpsert(recipient); await connection.Folder.PutAsync(folder); }); // now we also want to send back the magic link that we generated before. return(new RecipientResponse() { Email = regenRequest.RecipientEmail, FirstName = recipient.FirstName, LastName = recipient.LastName, Password = password.Plain, MagicLink = recipient.MagicLink, ExpirationDate = recipient.ExpirationDate, }); } return(new RecipientResponse()); }
private async Task EnsureFolderSecurityConfiguration(FolderIdentifier folderIdentifier) { await privilegedConnection.ConcurrencyRetryBlock(async() => { var folder = await privilegedConnection.Folder.GetAsync(folderIdentifier); if (!folder.Privilege("read")?.Any(a => a.OverrideKey == "edisc") ?? false) { folder.WriteACLs("read", new[] { new ACLModel { OverrideKey = "edisc", RequiredIdentifiers = new List <string> { "u:system", "x:eDiscovery", $"r:eDiscovery{{{folderIdentifier.FolderKey.Replace(" ", "_")}}}" } } }); await privilegedConnection.Folder.PutAsync(folder); } }); }
public async Task <RecipientResponse> AddRecipientAsync(AddOfficerRequest addOfficerRequest, string landingLocation, string passphrase) { var folder = await connection.Folder.GetAsync(addOfficerRequest.FolderIdentifier); DateTime?expirationDate = ModuleUtility.GetLinkExpirationDate(folder, MetadataKeyConstants.LEO_UPLOAD_EXPIRATION_LENGTH_SECONDS); var password = ModuleUtility.GeneratePassword( folder, MetadataKeyConstants.LEO_UPLOAD_RND_PASSWORD_LENGTH, LEOUploadUtility.LEO_UPLOAD_DEFAULT_PASSWORD_LENGTH, MetadataKeyConstants.LEO_UPLOAD_RND_PASSWORD_CHARS); // We're going to generate a user for eDicsovery. This user will have restricted priveleges. var user = await GenerateUser(addOfficerRequest, password.Plain); string completeUrl = ModuleUtility.CreateMagicLink(addOfficerRequest, landingLocation, passphrase, folder.Identifier, expirationDate, user.Identifier); await connection.ConcurrencyRetryBlock(async() => { folder = await connection.Folder.GetAsync(addOfficerRequest.FolderIdentifier); folder.MetaLEOUploadOfficerListUpsert(new ExternalUser() { Email = addOfficerRequest.RecipientEmail, FirstName = addOfficerRequest.FirstName, LastName = addOfficerRequest.LastName, PasswordHash = password.Hashed, MagicLink = completeUrl, ExpirationDate = expirationDate.GetValueOrDefault() }); var childPath = GetOfficerPath(addOfficerRequest.FolderIdentifier, addOfficerRequest.FirstName, addOfficerRequest.LastName); var allPaths = folder.Read("_paths", defaultValue: new List <string>()); allPaths.Add(childPath.FullName); folder.Write("_paths", allPaths); await connection.Folder.PutAsync(folder); }); await this.auditLogStore.AddEntry( new AuditLogEntry() { EntryType = AuditLogEntryType.LEOUploadOfficerAdded, Message = $"A LEO officer has been added. {addOfficerRequest.RecipientEmail}", ModuleType = Modules.ModuleType.LEOUpload }, folder.Identifier ); // build up the response return(new RecipientResponse() { Email = addOfficerRequest.RecipientEmail, ExpirationDate = expirationDate.GetValueOrDefault(), MagicLink = completeUrl, Password = password.Plain, FirstName = addOfficerRequest.FirstName, LastName = addOfficerRequest.LastName, }); }
/// <summary> /// In nearly all cases except very specific ones, the connection override should remain null. /// </summary> /// <param name="auditLogEntry"></param> /// <param name="identifier"></param> /// <param name="connectionOverride"></param> /// <returns></returns> public async Task <AuditLogEntry> AddEntry(AuditLogEntry auditLogEntry, FolderIdentifier identifier, APIConnection connectionOverride = null) { // In some cases we need to be able to pass a special connection in, such is the case for // an eDiscovery logged in user. This connection needs to come in, because we want to use their credentials, and piggy back off their account. // The connection in that case as it's passed in through DI won't have a logged in user, and calls to things like 'open folder' will fail. if (connectionOverride != null) { this.connection = connectionOverride; } var currentUserModel = await connection.User.GetAsync(connection.UserIdentifier); // This will save us having to set this everywhere it's created. if (!auditLogEntry.Created.HasValue) { auditLogEntry.Created = DateTime.UtcNow; } if (String.IsNullOrEmpty(auditLogEntry.UserKey)) { auditLogEntry.UserKey = currentUserModel.Identifier.UserKey; } if (String.IsNullOrEmpty(auditLogEntry.UserName)) { auditLogEntry.UserName = currentUserModel.EmailAddress; } // Check to see if there's already an audit log started. This will give me an empty list, if one // doesn't exist, so I'm garunteed this won't result in null. var auditLogMetadataEntries = await this.GetAllEntries(identifier); var lastEntry = new AuditLogEntry(); if (auditLogMetadataEntries.Count > 0) { lastEntry = auditLogMetadataEntries[auditLogMetadataEntries.Count - 1]; } // we're only going to add an audit log entry if there's something different in this entry // and the last entry. The created date will always be different, so we're checking these properties individualy. if (lastEntry.Message != auditLogEntry.Message || lastEntry.EntryType != auditLogEntry.EntryType || lastEntry.UserKey != auditLogEntry.UserKey || lastEntry.UserName != auditLogEntry.UserName || lastEntry.ModuleType != auditLogEntry.ModuleType ) { auditLogMetadataEntries.Add(auditLogEntry); await connection.ConcurrencyRetryBlock(async() => { var folder = await connection.Folder.GetAsync(identifier); folder.Write(AUDIT_LOG_LOCATION, auditLogMetadataEntries); await connection.Folder.PutAsync(folder); }); return(auditLogEntry); } return(null); }