/// <summary> /// We need a way to generate users on the fly. /// </summary> /// <param name="addRecipientRequest"></param> /// <returns></returns> private async Task <UserModel> GenerateUserForEDiscovery(AddRecipientRequest addRecipientRequest, string password) { await InitializePrivilegedConnectionAsync(); var folderIdentifier = addRecipientRequest.FolderIdentifier; var userModel = new UserModel { Identifier = ModuleUtility.GetFolderScopedUserIdentifier(folderIdentifier, addRecipientRequest.RecipientEmail), EmailAddress = addRecipientRequest.RecipientEmail, FirstName = addRecipientRequest.FirstName, LastName = addRecipientRequest.LastName, }; userModel = await privilegedConnection.User.PostAsync(userModel); await privilegedConnection.User.PasswordPutAsync(userModel.Identifier, password); var accessIdentifiers = new[] { $"o:{folderIdentifier.OrganizationKey}", $"r:eDiscovery{{{folderIdentifier.FolderKey.Replace(" ", "_")}}}", // used to actually control access $"r:eDiscovery", // used to test whether a given user is an eDiscovery user $"x:pcms", // disable PCMS rules $"x:leo", // disable LEO rules }; await privilegedConnection.User.AccessIdentifiersPutAsync(userModel.Identifier, accessIdentifiers); return(userModel); }
private async Task <UserModel> GenerateUser(RecipientRequestBase addRecipientRequest, string password) { await InitializePrivilegedConnectionAsync(); var folderIdentifier = addRecipientRequest.FolderIdentifier; var userModel = new UserModel { Identifier = ModuleUtility.GetFolderScopedUserIdentifier(folderIdentifier, addRecipientRequest.RecipientEmail, "leo"), EmailAddress = addRecipientRequest.RecipientEmail, FirstName = addRecipientRequest.FirstName, LastName = addRecipientRequest.LastName, }; userModel = await privilegedConnection.User.PostAsync(userModel); await privilegedConnection.User.PasswordPutAsync(userModel.Identifier, password); await privilegedConnection.User.AccessIdentifiersPutAsync(userModel.Identifier, new[] { "r:leoUpload", $"o:{folderIdentifier.OrganizationKey}", "x:pcms", "x:eDiscovery", $"u:{userModel.Identifier.UserKey.Replace(" ", "_")}" }); return(userModel); }
public async Task <RecipientResponse> RemoveRecipientAsync(RemoveRecipientRequest removeRequest) { var folder = await connection.Folder.GetAsync(removeRequest.FolderIdentifier); var recipients = folder.MetaEDiscoveryRecipientListRead(); folder.MetaEDiscoveryRecipientListRemove(removeRequest.RecipientEmail); await connection.Folder.PutAsync(folder); await InitializePrivilegedConnectionAsync(); // Using the special connection here to delete the user. await privilegedConnection.User.DeleteAsync(ModuleUtility.GetFolderScopedUserIdentifier(folder.Identifier, removeRequest.RecipientEmail)); await this.auditLogStore.AddEntry( new AuditLogEntry() { EntryType = AuditLogEntryType.eDiscoveryRecipientDeleted, Message = $"An eDiscovery User has been removed {removeRequest.RecipientEmail}", ModuleType = Modules.ModuleType.eDiscovery }, folder.Identifier ); return(new RecipientResponse() { Email = removeRequest.RecipientEmail, }); }
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 <ModelBase> RemoveOfficerAsync(RemoveOfficerRequest removeRequest) { FolderModel folder = null; await connection.ConcurrencyRetryBlock(async() => { folder = await connection.Folder.GetAsync(removeRequest.FolderIdentifier); var recipients = folder.MetaLEOUploadOfficerListRead(); var recipient = recipients.FirstOrDefault(f => f.Email == removeRequest.RecipientEmail); folder.MetaLEOUploadOfficerListRemove(removeRequest.RecipientEmail); var paths = folder.Read <List <string> >("_paths"); if (paths != null && recipient != null) { var officerPath = GetOfficerPath(folder.Identifier, recipient.FirstName, recipient.LastName); if (paths.Contains(officerPath.PathKey)) { paths = paths.Where(p => !p.StartsWith(officerPath.PathKey)).ToList(); } folder.Write("_paths", paths); } await connection.Folder.PutAsync(folder); }); await InitializePrivilegedConnectionAsync(); // Using the special connection here to delete the user. await privilegedConnection.User.DeleteAsync(ModuleUtility.GetFolderScopedUserIdentifier(folder.Identifier, removeRequest.RecipientEmail, "leo")); await this.auditLogStore.AddEntry( new AuditLogEntry() { EntryType = AuditLogEntryType.LEOUploadOfficerDeleted, Message = $"An officer has been removed {removeRequest.RecipientEmail}", ModuleType = Modules.ModuleType.LEOUpload }, folder.Identifier ); return(new RecipientResponse() { Email = removeRequest.RecipientEmail, }); }
public async Task <RecipientResponse> AddRecipientAsync(AddRecipientRequest addRecipientRequest, string landingLocation, string passphrase) { var folder = await connection.Folder.GetAsync(addRecipientRequest.FolderIdentifier); DateTime?expirationDate = ModuleUtility.GetLinkExpirationDate(folder, MetadataKeyConstants.E_DISCOVERY_EXPIRATION_LENGTH_SECONDS); var password = ModuleUtility.GeneratePassword(folder, MetadataKeyConstants.E_DISCOVERY_RND_PASSWORD_LENGTH, EDiscoveryUtility.E_DISCOVERY_DEFAULT_PASSWORD_LENGTH, MetadataKeyConstants.E_DISCOVERY_RND_PASSWORD_CHARS); // We're going to generate a user for eDicsovery. This user will have restricted priveleges. var user = await GenerateUserForEDiscovery(addRecipientRequest, password.Plain); string completeUrl = ModuleUtility.CreateMagicLink(addRecipientRequest, landingLocation, passphrase, folder.Identifier, expirationDate, user.Identifier); folder.MetaEDiscoveryRecipientListUpsert(new ExternalUser() { Email = addRecipientRequest.RecipientEmail, FirstName = addRecipientRequest.FirstName, LastName = addRecipientRequest.LastName, PasswordHash = password.Hashed, MagicLink = completeUrl, ExpirationDate = expirationDate.GetValueOrDefault() }); await connection.Folder.PutAsync(folder); await EnsureFolderSecurityConfiguration(folder.Identifier); await this.auditLogStore.AddEntry( new AuditLogEntry() { EntryType = AuditLogEntryType.eDiscoveryRecipientAdded, Message = $"An eDiscovery User has been added. {addRecipientRequest.RecipientEmail}", ModuleType = Modules.ModuleType.eDiscovery }, folder.Identifier ); // build up the response return(new RecipientResponse() { Email = addRecipientRequest.RecipientEmail, ExpirationDate = expirationDate.GetValueOrDefault(), MagicLink = completeUrl, Password = password.Plain, FirstName = addRecipientRequest.FirstName, LastName = addRecipientRequest.LastName, }); }
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, }); }
public async Task <UserAuthenticatedResponse> AuthenticateUserAsync(AuthenticateUserRequest authenticateUserRequest, string linkEncryptionKey) { if (String.IsNullOrEmpty(authenticateUserRequest.Token)) { return(new UserAuthenticatedResponse() { IsAuthenticated = false }); } // Get the magic link object back out from the token. var magicLink = ModuleUtility.DecryptMagicLink(authenticateUserRequest.Token, linkEncryptionKey); // Now we have a magic link object, we need to compare the email on the token, with the one that was passed in. if (magicLink.RecipientEmail.ToLower() != authenticateUserRequest.Email.ToLower() || magicLink.ExipirationDate < DateTime.UtcNow // next let's check that this link hasn't expired. ) // Next we check to make sure the folder keys are the same. { return(new UserAuthenticatedResponse() { IsAuthenticated = false }); } // Before we can operate, and get any folder details, we're also going to need to authenticate the user in the backend. This will properly set things on the connection // such that it can make a backed call. try { await connection.User.AuthenticateAsync(new TokenRequestModel { Identifier = ModuleUtility.GetFolderScopedUserIdentifier(magicLink.FolderIdentifier, authenticateUserRequest.Email, "leo"), Password = authenticateUserRequest.Password }); connection.AddCookieTokenToResponse(); // Now we can do the password check. We're going to check that the password in our database matches on hash // the password that came in on the request. var folder = await connection.Folder.GetAsync(magicLink.FolderIdentifier); // get the recipients, so we can find this particular recipient. var officers = folder.MetaLEOUploadOfficerListRead(); var recipient = officers.Where(rec => rec.Email.ToLower() == magicLink.RecipientEmail.ToLower()).FirstOrDefault(); // We check to make sure that there's a recipient on this folder that matches by email, and that the plain text // password that was passed in matches the password in our database. if (recipient != null && BCrypt.Verify(authenticateUserRequest.Password, recipient.PasswordHash)) { await this.auditLogStore.AddEntry( new AuditLogEntry() { EntryType = AuditLogEntryType.LEOUploadUserLogin, Message = "An officer Has Logged in.", ModuleType = Modules.ModuleType.LEOUpload }, folder.Identifier, connection ); return(new UserAuthenticatedResponse() { IsAuthenticated = true, FolderIdentifier = folder.Identifier, PathIdentifier = GetOfficerPath(folder.Identifier, recipient.FirstName, recipient.LastName) }); } else { return(new UserAuthenticatedResponse() { IsAuthenticated = false }); } } catch (Exception) { return(new UserAuthenticatedResponse() { IsAuthenticated = false }); } }