Ported everything except signalR and thick client, structured codebase throughout
#1
Merged
Krishna
merged 1 commits from PAT-375-Migration
into master
7 months ago
22 changed files with 2077 additions and 219 deletions
-
2CDP-LTS-80.sln
-
0CDP-LTS-80/Audit/AuditDB.cs
-
0CDP-LTS-80/Audit/AuditDocument.cs
-
263CDP-LTS-80/Audit/AuditFunctions.cs
-
6CDP-LTS-80/CDP-LTS-80.csproj
-
0CDP-LTS-80/CDPCore/CDPBlobStorage.cs
-
0CDP-LTS-80/CDPCore/CDPDB.cs
-
344CDP-LTS-80/CDPCore/CDPLite.cs
-
84CDP-LTS-80/CDPCore/FileRevision.cs
-
0CDP-LTS-80/CDPCore/MailProcessor.cs
-
111CDP-LTS-80/CDPCore/SignalRFunctions.cs
-
207CDP-LTS-80/CDPLite.cs
-
81CDP-LTS-80/Contacts/ContactFunctions.cs
-
327CDP-LTS-80/Contacts/ContactsDB.cs
-
0CDP-LTS-80/Contacts/ContactsDocument.cs
-
5CDP-LTS-80/EventBased/EventProcessor.cs
-
311CDP-LTS-80/EventBased/MetaProcessor.cs
-
388CDP-LTS-80/Groups/GroupsDB.cs
-
0CDP-LTS-80/Groups/GroupsDocument.cs
-
107CDP-LTS-80/Groups/GroupsFunctions.cs
-
39CDP-LTS-80/Helpers.cs
-
21CDP-LTS-80/host.json
@ -0,0 +1,263 @@ |
|||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Azure.Functions.Worker; |
||||
|
using Microsoft.Azure.Functions.Worker.Http; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace CDP |
||||
|
{ |
||||
|
public class AuditFunctions |
||||
|
{ |
||||
|
private readonly ILogger<AuditFunctions> _logger; |
||||
|
public static string FileAuditContainer = "FileAudits"; |
||||
|
public static string UserAuditContainer = "UserAudits"; |
||||
|
public static string GroupAuditContainer = "GroupAudits"; |
||||
|
public static string TenantAuditContainer = "TenantAudits"; |
||||
|
public AuditFunctions(ILogger<AuditFunctions> logger) |
||||
|
{ |
||||
|
_logger = logger; |
||||
|
} |
||||
|
|
||||
|
[Function("GetAuditLogForFile")] |
||||
|
public async Task<IActionResult> GetAuditLogForFile([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("GetAuditLogForFile invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
if (string.IsNullOrEmpty(requestBody)) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "The body is empty" }); |
||||
|
|
||||
|
_logger.LogInformation(requestBody); |
||||
|
|
||||
|
GetAuditLogForFileDto dto = JsonConvert.DeserializeObject<GetAuditLogForFileDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
List<AuditRecord> ad = await AuditDB.GetAuditRecordsBetweenDates(dto.FileId, DateTime.MinValue, DateTime.MaxValue, CDPLite.FileAuditContainer); |
||||
|
|
||||
|
return new OkObjectResult(ad); |
||||
|
} |
||||
|
|
||||
|
[Function("GetAuditLogForUser")] |
||||
|
public async Task<IActionResult> GetAuditLogForUser([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("GetAuditLogForUser invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
if (string.IsNullOrEmpty(requestBody)) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "The body is empty" }); |
||||
|
|
||||
|
_logger.LogInformation(requestBody); |
||||
|
|
||||
|
GetAuditLogForUserDto dto = JsonConvert.DeserializeObject<GetAuditLogForUserDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(dto.Email.ToLower()); |
||||
|
List<AuditRecord> ad = await AuditDB.GetAuditRecordsBetweenDates(userId, DateTime.MinValue, DateTime.MaxValue, UserAuditContainer); |
||||
|
return new OkObjectResult(ad); |
||||
|
} |
||||
|
|
||||
|
[Function("GetAuditLogForGroup")] |
||||
|
public async Task<IActionResult> GetAuditLogForGroup([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("GetAuditLogForGroup invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
if (string.IsNullOrEmpty(requestBody)) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "The body is empty" }); |
||||
|
|
||||
|
_logger.LogInformation(requestBody); |
||||
|
|
||||
|
GetAuditLogForGroupDto dto = JsonConvert.DeserializeObject<GetAuditLogForGroupDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
List<AuditRecord> ad = await AuditDB.GetAuditRecordsBetweenDates(dto.GroupId, DateTime.MinValue, DateTime.MaxValue, GroupAuditContainer); |
||||
|
return new OkObjectResult(ad); |
||||
|
} |
||||
|
|
||||
|
[Function("GetAuditLogForTenant")] |
||||
|
public async Task<IActionResult> GetAuditLogForTenant([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("GetAuditLogForGroup invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
if (string.IsNullOrEmpty(requestBody)) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "The body is empty" }); |
||||
|
|
||||
|
_logger.LogInformation(requestBody); |
||||
|
|
||||
|
GetAuditLogForTenantDto dto = JsonConvert.DeserializeObject<GetAuditLogForTenantDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
List<AuditRecord> ad = await AuditDB.GetAuditRecordsBetweenDates(dto.AppKey, DateTime.MinValue, DateTime.MaxValue, TenantAuditContainer); |
||||
|
return new OkObjectResult(ad); |
||||
|
} |
||||
|
|
||||
|
[Function("AddAccessViolation")] |
||||
|
public async Task<Boolean> AddAccessViolation([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("AddAccessViolation invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
if (string.IsNullOrEmpty(requestBody)) |
||||
|
return false; |
||||
|
|
||||
|
_logger.LogInformation(requestBody); |
||||
|
|
||||
|
AddAccessViolationDto dto = JsonConvert.DeserializeObject<AddAccessViolationDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return false; |
||||
|
|
||||
|
string message = string.Format($"Access violation recorded for file {dto.FileName}"); |
||||
|
string action = "Access Violation"; |
||||
|
await AddAudits(dto.AppKey, dto.FileId, dto.FileName, "", "", action, message); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public static async Task<string> AddAuditsEvent(string appKey, string fileId, string fileName, string userId, string groupid, string action, string message) |
||||
|
{ |
||||
|
using (var mt = new MethodTimer("AddAuditsEventMessage")) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(appKey) || string.IsNullOrEmpty(fileId) || string.IsNullOrEmpty(action) || string.IsNullOrEmpty(message)) |
||||
|
return ""; |
||||
|
AuditEventMetadata auditEvent = new AuditEventMetadata |
||||
|
{ |
||||
|
FileId = fileId, |
||||
|
FileName = fileName, |
||||
|
UserId = userId, |
||||
|
GroupId = groupid, |
||||
|
Action = action, |
||||
|
Message = message |
||||
|
}; |
||||
|
string jobId = Guid.NewGuid().ToString(); |
||||
|
string jobMeta = JsonConvert.SerializeObject(auditEvent); |
||||
|
Job job = new Job { AppKey = appKey, EventType = JobType.AddAudits, Id = jobId, JobMetadata = jobMeta }; |
||||
|
await MetaProcessor.PublishJob(job); |
||||
|
return jobId; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static async Task AddAudits(string appKey, string fileId, string fileName, string userId, string groupid, string action, string message) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(appKey) || string.IsNullOrEmpty(fileId) || string.IsNullOrEmpty(action) || string.IsNullOrEmpty(message)) |
||||
|
{ |
||||
|
Console.WriteLine(string.Format("something weird? appKey, fileId, action, message: {0} {1} {2} {3}", appKey, fileId, action, message)); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
AuditRecord faRec = new FileAuditRecord() |
||||
|
{ |
||||
|
AppKey = appKey, |
||||
|
FileId = fileId, |
||||
|
FileName = fileName, |
||||
|
UserId = userId, |
||||
|
GroupId = groupid, |
||||
|
Action = action, |
||||
|
Message = message, |
||||
|
EventTime = DateTime.UtcNow, |
||||
|
}; |
||||
|
Console.WriteLine("Adding File Audit Record"); |
||||
|
await AuditDB.AppendRecord(faRec.id, faRec, FileAuditContainer); |
||||
|
|
||||
|
AuditRecord faRecTenant = new TenantAuditRecord() |
||||
|
{ |
||||
|
AppKey = appKey, |
||||
|
FileId = fileId, |
||||
|
FileName = fileName, |
||||
|
UserId = userId, |
||||
|
GroupId = groupid, |
||||
|
Action = action, |
||||
|
Message = message, |
||||
|
EventTime = DateTime.UtcNow, |
||||
|
}; |
||||
|
|
||||
|
await AuditDB.AppendRecord(faRecTenant.id, faRecTenant, TenantAuditContainer); |
||||
|
|
||||
|
if (!string.IsNullOrEmpty(groupid)) |
||||
|
{ |
||||
|
AuditRecord faRecGroup = new GroupAuditRecord() |
||||
|
{ |
||||
|
AppKey = appKey, |
||||
|
FileId = fileId, |
||||
|
FileName = fileName, |
||||
|
UserId = userId, |
||||
|
GroupId = groupid, |
||||
|
Action = action, |
||||
|
Message = message, |
||||
|
EventTime = DateTime.UtcNow, |
||||
|
}; |
||||
|
|
||||
|
await AuditDB.AppendRecord(faRecGroup.id, faRecGroup, GroupAuditContainer); |
||||
|
} |
||||
|
|
||||
|
AuditRecord faRecUser = new UserAuditRecord() |
||||
|
{ |
||||
|
AppKey = appKey, |
||||
|
FileId = fileId, |
||||
|
FileName = fileName, |
||||
|
UserId = userId, |
||||
|
GroupId = groupid, |
||||
|
Action = action, |
||||
|
Message = message, |
||||
|
EventTime = DateTime.UtcNow, |
||||
|
}; |
||||
|
|
||||
|
await AuditDB.AppendRecord(faRecUser.id, faRecUser, UserAuditContainer); |
||||
|
|
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// Adds the audit record on a background thread.
|
||||
|
/// </summary>
|
||||
|
private static async Task AddFileAudit(AuditRecord far) |
||||
|
{ |
||||
|
await AuditDB.AppendRecord(far.id, far, FileAuditContainer); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
private static async Task AddUserAudit(AuditRecord far) |
||||
|
{ |
||||
|
await AuditDB.AppendRecord(far.id, far, UserAuditContainer); |
||||
|
} |
||||
|
|
||||
|
private static async Task AddTenantAudit(AuditRecord far) |
||||
|
{ |
||||
|
await Task.Run(async () => |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await AuditDB.AppendRecord(far.id, far, TenantAuditContainer); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
private static async Task AddGroupAudit(AuditRecord far) |
||||
|
{ |
||||
|
await Task.Run(async () => |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await AuditDB.AppendRecord(far.id, far, GroupAuditContainer); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,344 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Azure.Cosmos; |
||||
|
using Microsoft.Azure.Functions.Worker.Http; |
||||
|
using Microsoft.Azure.Functions.Worker; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Newtonsoft.Json; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CDP |
||||
|
{ |
||||
|
public class CDPLite |
||||
|
{ |
||||
|
private readonly ILogger<CDPLite> _logger; |
||||
|
public static string FileAuditContainer = "FileAudits"; |
||||
|
public static string UserAuditContainer = "UserAudits"; |
||||
|
public static string GroupAuditContainer = "GroupAudits"; |
||||
|
public static string TenantAuditContainer = "TenantAudits"; |
||||
|
|
||||
|
public CDPLite(ILogger<CDPLite> log) |
||||
|
{ |
||||
|
_logger = log; |
||||
|
} |
||||
|
|
||||
|
/*internal async Task<IActionResult> AddFilesBatchedInternal(AddFileBatchedDto dto) |
||||
|
{ |
||||
|
string userId = Helpers.HashAndShortenText(dto.Email.ToLower()); |
||||
|
string jobId = Guid.NewGuid().ToString(); |
||||
|
List<KeyVaultEvent> vaultEvents = new List<KeyVaultEvent>(); |
||||
|
//List<AuditEventMetadata> auditEvents = new List<AuditEventMetadata>();
|
||||
|
List<FileRecord> fileRecords = new List<FileRecord>(); |
||||
|
for (int i = 0; i < dto.Count; i++) |
||||
|
{ |
||||
|
string fileId = Guid.NewGuid().ToString(); |
||||
|
string fileName = dto.FileNames[i]; |
||||
|
string aesKey = Helpers.GenerateAES256KeyToBase64(); |
||||
|
//string message = string.Format($"{dto.Email} protected {fileName} file having {fileId} id.");
|
||||
|
KeyVaultEvent vaultEvent = new KeyVaultEvent { AESKey = aesKey, FileId = fileId }; |
||||
|
//AuditEventMetadata auditEvent = new AuditEventMetadata { Action = "Addded", FileId = fileId, FileName = dto.FileNames[i], Message = message, UserId = userId };
|
||||
|
//auditEvents.Add(auditEvent);
|
||||
|
vaultEvents.Add(vaultEvent); |
||||
|
|
||||
|
AccessPolicy ac = new AccessPolicy() |
||||
|
{ |
||||
|
Access = "Owner", |
||||
|
Key = aesKey, |
||||
|
Email = dto.Email |
||||
|
}; |
||||
|
FileRecord fr = await CDPDB.UpsertFile(dto.AppKey, fileId, fileName, userId, "", ac); |
||||
|
fileRecords.Add(fr); |
||||
|
} |
||||
|
List<Job> jobs = new List<Job>(); |
||||
|
Job vaultJob = await AddKeyVaultBatchedEvent(vaultEvents, dto.AppKey); |
||||
|
//Job auditJob = await AddAuditsBatchedEvent(auditEvents, dto.AppKey);
|
||||
|
//jobs.Add(auditJob);
|
||||
|
jobs.Add(vaultJob); |
||||
|
await MetaProcessor.PublishBatchJobs(jobs); |
||||
|
return new OkObjectResult(fileRecords); |
||||
|
|
||||
|
}*/ |
||||
|
|
||||
|
|
||||
|
internal async Task<IActionResult> AddFileInternal(AddFileDto dto, bool useKeyVaultEvent = false) |
||||
|
{ |
||||
|
string fileId = Guid.NewGuid().ToString(); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(dto.Email.ToLower()); |
||||
|
string fileName = dto.FileName; |
||||
|
string aesKey = Helpers.GenerateAES256KeyToBase64(); |
||||
|
|
||||
|
// the KeyVault is slow for some reason and while it's dangerous to return a key
|
||||
|
// before we're sure it got added to the database...I'm going to do it anyway.
|
||||
|
if (useKeyVaultEvent) |
||||
|
{ |
||||
|
await AddKeyVaultEvent(fileId, aesKey, dto.AppKey); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
await Task.Run(async () => |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
KeyVaultService kvs = new KeyVaultService(Constants.KeyVaultURI); |
||||
|
await kvs.SetSecretAsync(fileId, aesKey); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Console.WriteLine(e); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// when you add a file, you have rights to manage access to it.
|
||||
|
AccessPolicy ac = new AccessPolicy() |
||||
|
{ |
||||
|
Access = "Owner", |
||||
|
Key = aesKey, |
||||
|
Email = dto.Email |
||||
|
}; |
||||
|
|
||||
|
// since we're generating a new file id, a new entry will always be created.
|
||||
|
FileRecord fr = await CDPDB.UpsertFile(dto.AppKey, fileId, fileName, userId, "", ac); |
||||
|
|
||||
|
string message = string.Format($"{dto.Email} protected {fileName} file having {fileId} id."); |
||||
|
string action = "Added"; |
||||
|
|
||||
|
await AuditFunctions.AddAuditsEvent(dto.AppKey, fileId, fileName, userId, dto.GroupId, action, message); //commenting for speed test
|
||||
|
return new OkObjectResult(fr); |
||||
|
} |
||||
|
|
||||
|
internal static async Task<IActionResult> AddFileUserInternal(AddFileUserDto dto) |
||||
|
{ |
||||
|
// check to see if the email has the power to add a user
|
||||
|
string userId = Helpers.HashAndShortenText(dto.Email.ToLower()); |
||||
|
|
||||
|
FileRecord fr = await CDPDB.GetFile(dto.AppKey, dto.FileId, userId); |
||||
|
if (fr == null) |
||||
|
{ |
||||
|
string message = string.Format($"{dto.Email} attempted to add/change access policy for {dto.EmailToAdd} on {dto.FileName} file having {dto.FileId} id, but didn't have ANY access"); |
||||
|
Console.WriteLine(message); |
||||
|
string action = "Policy change failed"; |
||||
|
await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, dto.FileName, userId, "", action, message); |
||||
|
|
||||
|
return new BadRequestObjectResult(new { error = true, message = "File not found for user " + dto.Email }); |
||||
|
} |
||||
|
|
||||
|
if ((!fr.Policy.CheckAccess("Manage")) && (!fr.Policy.CheckAccess("Owner"))) |
||||
|
{ |
||||
|
string message = string.Format($"{dto.Email} attempted to add/change access policy for {dto.EmailToAdd} on {dto.FileName} file having {dto.FileId} id, but didn't have manage access"); |
||||
|
Console.WriteLine(message); |
||||
|
string action = "Policy change failed"; |
||||
|
await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, dto.FileName, userId, "", action, message); |
||||
|
return new BadRequestObjectResult(new { error = true, message = $"{dto.Email} doesn't have the rights to add a user." }); |
||||
|
} |
||||
|
|
||||
|
string fileId = dto.FileId; |
||||
|
string fileName = dto.FileName; |
||||
|
string userIdToAdd = ""; |
||||
|
|
||||
|
if (dto.EmailToAdd != "") |
||||
|
{ |
||||
|
userIdToAdd = Helpers.HashAndShortenText(dto.EmailToAdd.ToLower()); |
||||
|
} |
||||
|
else if (dto.Group != null) |
||||
|
{ |
||||
|
userIdToAdd = dto.GroupId; |
||||
|
} |
||||
|
else if (dto.Group != null) |
||||
|
{ |
||||
|
userIdToAdd = dto.GroupId; |
||||
|
} |
||||
|
|
||||
|
AccessPolicy ac = new AccessPolicy() |
||||
|
{ |
||||
|
Access = dto.Policy, |
||||
|
Email = dto.EmailToAdd.ToLower(), |
||||
|
Group = dto.Group, |
||||
|
GroupId = dto.GroupId, |
||||
|
Key = "" |
||||
|
}; |
||||
|
|
||||
|
fr = await CDPDB.UpsertFile(dto.AppKey, fileId, fileName, userIdToAdd, "", ac); |
||||
|
|
||||
|
if (dto.EmailToAdd != "") |
||||
|
{ |
||||
|
string message = string.Format($"{dto.Email} added/changed the access policy for User : {dto.EmailToAdd} to {dto.Policy} on {fileName} file having {fileId} id"); |
||||
|
string action = "Policy change"; |
||||
|
await AuditFunctions.AddAuditsEvent(dto.AppKey, fileId, fileName, userId, "", action, message); |
||||
|
} |
||||
|
|
||||
|
if (dto.Group != null) |
||||
|
{ |
||||
|
string message = string.Format($"{dto.Email} added/changed the access policy for Group : {dto.Group} to {dto.Policy} on {fileName} file having {fileId} id"); |
||||
|
string action = "Policy change"; |
||||
|
await AuditFunctions.AddAuditsEvent(dto.AppKey, fileId, fileName, "", dto.Group.id, action, message); |
||||
|
} |
||||
|
return new OkObjectResult(fr); |
||||
|
} |
||||
|
|
||||
|
#region CDP File Functions
|
||||
|
|
||||
|
[Function("AddFile")] |
||||
|
public async Task<IActionResult> AddFile([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("AddFile invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
AddFileDto dto = JsonConvert.DeserializeObject<AddFileDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
return await AddFileInternal(dto, true); |
||||
|
} |
||||
|
|
||||
|
[Function("AddFileUser")] |
||||
|
public async Task<IActionResult> AddFileUser([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("AddFileUser invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
AddFileUserDto dto = JsonConvert.DeserializeObject<AddFileUserDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
return await AddFileUserInternal(dto); |
||||
|
} |
||||
|
|
||||
|
[Function("GetFileForUser")] |
||||
|
public async Task<IActionResult> GetFileForUser([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("GetFile invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
AddFileUserDto dto = JsonConvert.DeserializeObject<AddFileUserDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
// check to see if the email has the power to add a user
|
||||
|
// string a = Helpers.HashAndShortenText(dto.Email.ToLower());
|
||||
|
// string b = Helpers.HashToHex(dto.Email.ToLower());
|
||||
|
// string c = Helpers.ConvertShortHashToHex(a);
|
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(dto.Email.ToLower()); |
||||
|
// userId = "user-" + Helpers.HashToHex(dto.Email.ToLower());
|
||||
|
|
||||
|
FileRecord fr = await CDPDB.GetFile(dto.AppKey, dto.FileId, userId); |
||||
|
if (fr == null) |
||||
|
{ |
||||
|
string AuditMessage = string.Format($"File not found for user {dto.Email} having fileId {dto.FileId}"); |
||||
|
string AuditAction = "Decrypt failed"; |
||||
|
await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, dto.FileName, userId, "", AuditAction, AuditMessage); |
||||
|
return new BadRequestObjectResult(new { error = true, message = "File not found for user " + dto.Email }); |
||||
|
} |
||||
|
|
||||
|
if (fr.Policy.CheckAccess("None")) |
||||
|
{ |
||||
|
string AuditMessage = string.Format($"{dto.Email} don't have the rights to decrypt {fr.FileName} file having {dto.FileId} id"); |
||||
|
string AuditAction = "Decrypt failed"; |
||||
|
await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, fr.FileName, userId, "", AuditAction, AuditMessage); |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Access is denied for user " + dto.Email }); |
||||
|
} |
||||
|
|
||||
|
KeyVaultService kvs = new KeyVaultService(Constants.KeyVaultURI); |
||||
|
fr.Policy.Key = await kvs.GetSecretAsync(fr.FileId); |
||||
|
|
||||
|
string message = string.Format($"{dto.Email} decrypted {fr.FileName} file having {dto.FileId} id"); |
||||
|
string action = "Decrypted"; |
||||
|
await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, dto.FileName, userId, "", action, message); |
||||
|
|
||||
|
return new OkObjectResult(fr); |
||||
|
} |
||||
|
|
||||
|
[Function("GetPoliciesForFile")] |
||||
|
public async Task<IActionResult> GetPoliciesForFile([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("GetFile invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
GetPoliciesForFileDto dto = JsonConvert.DeserializeObject<GetPoliciesForFileDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
List<FileRecord> fr = await CDPDB.GetPoliciesForFile(dto.AppKey, dto.FileId); |
||||
|
if (fr == null) |
||||
|
{ |
||||
|
return new BadRequestObjectResult(new { error = true, message = "File not found " + dto.FileId }); |
||||
|
} |
||||
|
KeyVaultService kvs = new KeyVaultService(Constants.KeyVaultURI); |
||||
|
string aesKey = await kvs.GetSecretAsync(dto.FileId); |
||||
|
foreach (var f in fr) |
||||
|
{ |
||||
|
f.Policy.Key = aesKey; |
||||
|
} |
||||
|
|
||||
|
return new OkObjectResult(fr); |
||||
|
} |
||||
|
|
||||
|
[Function("DeleteRegisteredUserPolicies")] |
||||
|
public async Task<IActionResult> DeleteRegisteredUserPolicies([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("Deleting Registered User invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
DeleteRegisteredUserDto deleteContactdto = JsonConvert.DeserializeObject<DeleteRegisteredUserDto>(requestBody); |
||||
|
if (deleteContactdto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(deleteContactdto.UserEmail.ToLower()); |
||||
|
await CDPDB.revokeRegisteredUserPolicies(deleteContactdto.AppKey, deleteContactdto.UserEmail.ToLower(), deleteContactdto.ContactEmail.ToLower(), deleteContactdto.AdminEmail.ToLower()); |
||||
|
|
||||
|
return new OkObjectResult(true); |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
#region Background Processing Support
|
||||
|
|
||||
|
public static async Task<Job> AddAuditsBatchedEvent(List<AuditEventMetadata> auditEvents, string appKey) |
||||
|
{ |
||||
|
using (var mt = new MethodTimer("AddAuditsBatchedEvent")) |
||||
|
{ |
||||
|
string jobMeta = JsonConvert.SerializeObject(auditEvents); |
||||
|
string jobId = Guid.NewGuid().ToString(); |
||||
|
Job job = new Job { AppKey = appKey, EventType = JobType.AddAuditsBatch, Id = jobId, JobMetadata = jobMeta }; |
||||
|
return job; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static async Task<Job> AddKeyVaultBatchedEvent(List<KeyVaultEvent> vaultEvents, string appKey) |
||||
|
{ |
||||
|
using (var mt = new MethodTimer("AddKeyVaultBatchedEvent")) |
||||
|
{ |
||||
|
string jobMeta = JsonConvert.SerializeObject(vaultEvents); |
||||
|
string jobId = Guid.NewGuid().ToString(); |
||||
|
Job job = new Job { AppKey = appKey, EventType = JobType.KeyVaultInsertionBatch, Id = jobId, JobMetadata = jobMeta }; |
||||
|
//await MetaProcessor.PublishJob(job);
|
||||
|
return job; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static async Task<string> AddKeyVaultEvent(string fileId, string aesKey, string appKey) |
||||
|
{ |
||||
|
using (var mt = new MethodTimer("AddKeyVaultEvent")) |
||||
|
{ |
||||
|
KeyVaultEvent vaultEvent = new KeyVaultEvent { AESKey = aesKey, FileId = fileId }; |
||||
|
string jobMeta = JsonConvert.SerializeObject(vaultEvent); |
||||
|
string jobId = Guid.NewGuid().ToString(); |
||||
|
Job keyVaultJob = new Job { AppKey = appKey, EventType = JobType.KeyVaultInsertion, Id = jobId, JobMetadata = jobMeta }; |
||||
|
await MetaProcessor.PublishJob(keyVaultJob); |
||||
|
return jobId; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
} |
||||
|
} |
@ -0,0 +1,111 @@ |
|||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Azure.Functions.Worker; |
||||
|
using Microsoft.Azure.Functions.Worker.Http; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Newtonsoft.Json; |
||||
|
using System.Net; |
||||
|
|
||||
|
namespace CDP.CDPCore |
||||
|
{ |
||||
|
/*public class SignalRFunctions |
||||
|
{ |
||||
|
private readonly ILogger<SignalRFunctions> _logger; |
||||
|
|
||||
|
private static readonly HttpClient HttpClient = new(); |
||||
|
private static string Etag = string.Empty; |
||||
|
private static int StarCount = 0; |
||||
|
|
||||
|
public SignalRFunctions(ILogger<SignalRFunctions> log) |
||||
|
{ |
||||
|
_logger = log; |
||||
|
} |
||||
|
|
||||
|
[Function("index")] |
||||
|
public static HttpResponseData GetHomePage([HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequestData req) |
||||
|
{ |
||||
|
var response = req.CreateResponse(HttpStatusCode.OK); |
||||
|
response.WriteString(File.ReadAllText("content/index.html")); |
||||
|
response.Headers.Add("Content-Type", "text/html"); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
[Function("negotiate")] |
||||
|
public static HttpResponseData Negotiate([HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequestData req, |
||||
|
[SignalRConnectionInfoInput(HubName = "cdp")] string connectionInfo) |
||||
|
{ |
||||
|
var response = req.CreateResponse(HttpStatusCode.OK); |
||||
|
response.Headers.Add("Content-Type", "application/json"); |
||||
|
response.WriteString(connectionInfo); |
||||
|
return response; |
||||
|
} |
||||
|
|
||||
|
[Function(nameof(SendMessage))] |
||||
|
[SignalROutput(HubName = "cdp")] |
||||
|
public static async Task<SignalRMessageAction> SendMessage([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req) |
||||
|
{ |
||||
|
string requestBody = new StreamReader(req.Body).ReadToEnd(); |
||||
|
SendMessagePayload pl = JsonConvert.DeserializeObject<SendMessagePayload>(requestBody); |
||||
|
if (pl == null) |
||||
|
return null; |
||||
|
|
||||
|
return await SendMessageInternal(pl); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static async Task<SignalRMessageAction> SendMessageInternal(SendMessagePayload pl) |
||||
|
{ |
||||
|
if (pl == null) |
||||
|
return null; |
||||
|
|
||||
|
if (string.IsNullOrEmpty(pl.ConnectionId) && string.IsNullOrEmpty(pl.UserId)) |
||||
|
return null; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
Console.WriteLine($"Sending message to {pl.ConnectionId}"); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Console.WriteLine(ex.Message); |
||||
|
} |
||||
|
|
||||
|
return new SignalRMessageAction(pl.Target) |
||||
|
{ |
||||
|
Arguments = new[] { pl.Message }, |
||||
|
ConnectionId = pl.ConnectionId.Trim() |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
//[Function("broadcast")]
|
||||
|
//[SignalROutput(HubName = "cdp")]
|
||||
|
//public static async Task<SignalRMessageAction> Broadcast([TimerTrigger("5 * * * * *] TimerInfo timerInfo)
|
||||
|
//{
|
||||
|
// var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/repos/azure/azure-signalr");
|
||||
|
// request.Headers.UserAgent.ParseAdd("Serverless");
|
||||
|
// request.Headers.Add("If-None-Match", Etag);
|
||||
|
// var response = await HttpClient.SendAsync(request);
|
||||
|
// if (response.Headers.Contains("Etag"))
|
||||
|
// {
|
||||
|
// Etag = response.Headers.GetValues("Etag").First();
|
||||
|
// }
|
||||
|
// if (response.StatusCode == HttpStatusCode.OK)
|
||||
|
// {
|
||||
|
// var result = await response.Content.ReadFromJsonAsync<GitResult>();
|
||||
|
// if (result != null)
|
||||
|
// {
|
||||
|
// StarCount = result.StarCount;
|
||||
|
// }
|
||||
|
// }
|
||||
|
// return new SignalRMessageAction("newMessage", new object[] { $"Current star count of https://github.com/Azure/azure-signalr is: {StarCount}" });
|
||||
|
//}
|
||||
|
}*/ |
||||
|
|
||||
|
public class SendMessagePayload |
||||
|
{ |
||||
|
public string ConnectionId { get; set; } |
||||
|
public string UserId { get; set; } |
||||
|
public string Message { get; set; } |
||||
|
public string Target { get; set; } // this will default to 'newMessage'
|
||||
|
} |
||||
|
} |
@ -1,207 +0,0 @@ |
|||||
using Microsoft.AspNetCore.Mvc; |
|
||||
using Microsoft.Azure.Cosmos; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Text; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace CDP |
|
||||
{ |
|
||||
public class CDPLite |
|
||||
{ |
|
||||
private readonly ILogger<CDPLite> _logger; |
|
||||
private static string FileAuditContainer = "FileAudits"; |
|
||||
private static string UserAuditContainer = "UserAudits"; |
|
||||
private static string GroupAuditContainer = "GroupAudits"; |
|
||||
private static string TenantAuditContainer = "TenantAudits"; |
|
||||
|
|
||||
public CDPLite(ILogger<CDPLite> log) |
|
||||
{ |
|
||||
_logger = log; |
|
||||
} |
|
||||
|
|
||||
internal static async Task<IActionResult> AddFileUserInternal(AddFileUserDto dto) |
|
||||
{ |
|
||||
// check to see if the email has the power to add a user
|
|
||||
string userId = Helpers.HashAndShortenText(dto.Email.ToLower()); |
|
||||
|
|
||||
FileRecord fr = await CDPDB.GetFile(dto.AppKey, dto.FileId, userId); |
|
||||
if (fr == null) |
|
||||
{ |
|
||||
string message = string.Format($"{dto.Email} attempted to add/change access policy for {dto.EmailToAdd} on {dto.FileName} file having {dto.FileId} id, but didn't have ANY access"); |
|
||||
Console.WriteLine(message); |
|
||||
string action = "Policy change failed"; |
|
||||
await AddAudits(dto.AppKey, dto.FileId, dto.FileName, userId, "", action, message); |
|
||||
|
|
||||
return new BadRequestObjectResult(new { error = true, message = "File not found for user " + dto.Email }); |
|
||||
} |
|
||||
|
|
||||
if ((!fr.Policy.CheckAccess("Manage")) && (!fr.Policy.CheckAccess("Owner"))) |
|
||||
{ |
|
||||
string message = string.Format($"{dto.Email} attempted to add/change access policy for {dto.EmailToAdd} on {dto.FileName} file having {dto.FileId} id, but didn't have manage access"); |
|
||||
Console.WriteLine(message); |
|
||||
string action = "Policy change failed"; |
|
||||
await AddAudits(dto.AppKey, dto.FileId, dto.FileName, userId, "", action, message); |
|
||||
return new BadRequestObjectResult(new { error = true, message = $"{dto.Email} doesn't have the rights to add a user." }); |
|
||||
} |
|
||||
|
|
||||
string fileId = dto.FileId; |
|
||||
string fileName = dto.FileName; |
|
||||
string userIdToAdd = ""; |
|
||||
|
|
||||
if (dto.EmailToAdd != "") |
|
||||
{ |
|
||||
userIdToAdd = Helpers.HashAndShortenText(dto.EmailToAdd.ToLower()); |
|
||||
} |
|
||||
else if (dto.Group != null) |
|
||||
{ |
|
||||
userIdToAdd = dto.GroupId; |
|
||||
} |
|
||||
else if (dto.Group != null) |
|
||||
{ |
|
||||
userIdToAdd = dto.GroupId; |
|
||||
} |
|
||||
|
|
||||
AccessPolicy ac = new AccessPolicy() |
|
||||
{ |
|
||||
Access = dto.Policy, |
|
||||
Email = dto.EmailToAdd.ToLower(), |
|
||||
Group = dto.Group, |
|
||||
GroupId = dto.GroupId, |
|
||||
Key = "" |
|
||||
}; |
|
||||
|
|
||||
fr = await CDPDB.UpsertFile(dto.AppKey, fileId, fileName, userIdToAdd, "", ac); |
|
||||
|
|
||||
if (dto.EmailToAdd != "") |
|
||||
{ |
|
||||
string message = string.Format($"{dto.Email} added/changed the access policy for User : {dto.EmailToAdd} to {dto.Policy} on {fileName} file having {fileId} id"); |
|
||||
string action = "Policy change"; |
|
||||
await AddAudits(dto.AppKey, fileId, fileName, userId, "", action, message); |
|
||||
} |
|
||||
|
|
||||
if (dto.Group != null) |
|
||||
{ |
|
||||
string message = string.Format($"{dto.Email} added/changed the access policy for Group : {dto.Group} to {dto.Policy} on {fileName} file having {fileId} id"); |
|
||||
string action = "Policy change"; |
|
||||
await AddAudits(dto.AppKey, fileId, fileName, "", dto.Group.id, action, message); |
|
||||
} |
|
||||
return new OkObjectResult(fr); |
|
||||
} |
|
||||
|
|
||||
public static async Task AddAudits(string appKey, string fileId, string fileName, string userId, string groupid, string action, string message) |
|
||||
{ |
|
||||
if (string.IsNullOrEmpty(appKey) || string.IsNullOrEmpty(fileId) || string.IsNullOrEmpty(action) || string.IsNullOrEmpty(message)) |
|
||||
{ |
|
||||
Console.WriteLine(string.Format("something weird? appKey, fileId, action, message: {0} {1} {2} {3}", appKey, fileId, action, message)); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
AuditRecord faRec = new FileAuditRecord() |
|
||||
{ |
|
||||
AppKey = appKey, |
|
||||
FileId = fileId, |
|
||||
FileName = fileName, |
|
||||
UserId = userId, |
|
||||
GroupId = groupid, |
|
||||
Action = action, |
|
||||
Message = message, |
|
||||
EventTime = DateTime.UtcNow, |
|
||||
}; |
|
||||
Console.WriteLine("Adding File Audit Record"); |
|
||||
await AuditDB.AppendRecord(faRec.id, faRec, FileAuditContainer); |
|
||||
|
|
||||
AuditRecord faRecTenant = new TenantAuditRecord() |
|
||||
{ |
|
||||
AppKey = appKey, |
|
||||
FileId = fileId, |
|
||||
FileName = fileName, |
|
||||
UserId = userId, |
|
||||
GroupId = groupid, |
|
||||
Action = action, |
|
||||
Message = message, |
|
||||
EventTime = DateTime.UtcNow, |
|
||||
}; |
|
||||
|
|
||||
await AuditDB.AppendRecord(faRecTenant.id, faRecTenant, TenantAuditContainer); |
|
||||
|
|
||||
if (!string.IsNullOrEmpty(groupid)) |
|
||||
{ |
|
||||
AuditRecord faRecGroup = new GroupAuditRecord() |
|
||||
{ |
|
||||
AppKey = appKey, |
|
||||
FileId = fileId, |
|
||||
FileName = fileName, |
|
||||
UserId = userId, |
|
||||
GroupId = groupid, |
|
||||
Action = action, |
|
||||
Message = message, |
|
||||
EventTime = DateTime.UtcNow, |
|
||||
}; |
|
||||
|
|
||||
await AuditDB.AppendRecord(faRecGroup.id, faRecGroup, GroupAuditContainer); |
|
||||
} |
|
||||
|
|
||||
AuditRecord faRecUser = new UserAuditRecord() |
|
||||
{ |
|
||||
AppKey = appKey, |
|
||||
FileId = fileId, |
|
||||
FileName = fileName, |
|
||||
UserId = userId, |
|
||||
GroupId = groupid, |
|
||||
Action = action, |
|
||||
Message = message, |
|
||||
EventTime = DateTime.UtcNow, |
|
||||
}; |
|
||||
|
|
||||
await AuditDB.AppendRecord(faRecUser.id, faRecUser, UserAuditContainer); |
|
||||
|
|
||||
} |
|
||||
/// <summary>
|
|
||||
/// Adds the audit record on a background thread.
|
|
||||
/// </summary>
|
|
||||
private static async Task AddFileAudit(AuditRecord far) |
|
||||
{ |
|
||||
await AuditDB.AppendRecord(far.id, far, FileAuditContainer); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
private static async Task AddUserAudit(AuditRecord far) |
|
||||
{ |
|
||||
await AuditDB.AppendRecord(far.id, far, UserAuditContainer); |
|
||||
} |
|
||||
|
|
||||
private static async Task AddTenantAudit(AuditRecord far) |
|
||||
{ |
|
||||
await Task.Run(async () => |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
await AuditDB.AppendRecord(far.id, far, TenantAuditContainer); |
|
||||
} |
|
||||
catch (Exception e) |
|
||||
{ |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
private static async Task AddGroupAudit(AuditRecord far) |
|
||||
{ |
|
||||
await Task.Run(async () => |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
await AuditDB.AppendRecord(far.id, far, GroupAuditContainer); |
|
||||
} |
|
||||
catch (Exception e) |
|
||||
{ |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,81 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Azure.Messaging.ServiceBus; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Azure.Functions.Worker; |
||||
|
using Microsoft.Azure.Functions.Worker.Http; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace CDP |
||||
|
{ |
||||
|
public class ContactFunctions |
||||
|
{ |
||||
|
private readonly ILogger<ContactFunctions> _logger; |
||||
|
|
||||
|
public ContactFunctions(ILogger<ContactFunctions> logger) |
||||
|
{ |
||||
|
_logger = logger; |
||||
|
} |
||||
|
|
||||
|
[Function("GetContacts")] |
||||
|
public async Task<IActionResult> GetContacts([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("Get Contacts invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
GetContactsDto dto = JsonConvert.DeserializeObject<GetContactsDto>(requestBody); |
||||
|
if (dto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(dto.Email.ToLower()); |
||||
|
// userId = "user-" + Helpers.HashToHex(dto.Email.ToLower());
|
||||
|
|
||||
|
List<ContactRecord> fr = await ContactsDB.GetUserContacts(dto.AppKey, userId); |
||||
|
if (fr == null) |
||||
|
{ |
||||
|
return new BadRequestObjectResult(new { error = true, message = "File not found " + dto.Email }); |
||||
|
} |
||||
|
|
||||
|
return new OkObjectResult(fr); |
||||
|
} |
||||
|
|
||||
|
[Function("AddContact")] |
||||
|
public async Task<IActionResult> AddContacts([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("Add Contacts invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
AddUserContactsDto addContactdto = JsonConvert.DeserializeObject<AddUserContactsDto>(requestBody); |
||||
|
if (addContactdto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(addContactdto.EmailId.ToLower()); |
||||
|
ContactRecord cr = new ContactRecord(addContactdto.ContactName, addContactdto.ContactEmail, addContactdto.ContactPhone, addContactdto.ContactAddress, addContactdto.ContactCompany); |
||||
|
|
||||
|
Boolean resp = await ContactsDB.AppendRecord(addContactdto.AppKey, userId, cr); |
||||
|
|
||||
|
return new OkObjectResult(resp); |
||||
|
} |
||||
|
|
||||
|
[Function("DeleteContact")] |
||||
|
public async Task<IActionResult> DeleteContact([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("Deleting Contact invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
DeleteUserContactsDto deleteContactdto = JsonConvert.DeserializeObject<DeleteUserContactsDto>(requestBody); |
||||
|
if (deleteContactdto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(deleteContactdto.UserEmail.ToLower()); |
||||
|
Boolean resp = await ContactsDB.RemoveRecord(deleteContactdto.AppKey, userId, deleteContactdto.ContactEmail.ToLower()); |
||||
|
await CDPDB.revokePolicies(deleteContactdto.AppKey, deleteContactdto.UserEmail.ToLower(), deleteContactdto.ContactEmail.ToLower()); |
||||
|
|
||||
|
return new OkObjectResult(resp); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,327 @@ |
|||||
|
using Microsoft.Azure.Cosmos; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CDP |
||||
|
{ |
||||
|
public class ContactsDB |
||||
|
{ |
||||
|
private static Lazy<CosmosClient> lazyClient = new Lazy<CosmosClient>(InitializeCosmosClient); |
||||
|
private static CosmosClient cosmosClient => lazyClient.Value; |
||||
|
private static string DatabaseName = "CDP"; |
||||
|
private static string ContainerName = "Contacts"; |
||||
|
|
||||
|
private static CosmosClient InitializeCosmosClient() |
||||
|
{ |
||||
|
// Perform any initialization here
|
||||
|
var uri = "https://cdplite.documents.azure.com:443/"; |
||||
|
var authKey = "VPbg8RpzyI3XwhC2o0dIUtYFs33ghxORCqZeNAyg8vg4HWUBjM41BUxP0qLFXEvFh6ewQY1uKv52ACDbsEN1AQ=="; |
||||
|
|
||||
|
return new CosmosClient(uri, authKey); |
||||
|
} |
||||
|
|
||||
|
public static async Task<List<ContactRecord>> GetUserContacts(string AppKey, string UserId) |
||||
|
{ |
||||
|
var results = new List<ContactRecord>(); |
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
|
||||
|
// Fetch the metadata document for the customer ID
|
||||
|
var metadataDocumentId = GetMetaDocumentKey(AppKey, UserId); |
||||
|
MetadataDocumentContact metadataDocument = null; |
||||
|
try |
||||
|
{ |
||||
|
var metadataDocumentResponse = |
||||
|
await container.ReadItemAsync<MetadataDocumentContact>(metadataDocumentId, new PartitionKey(metadataDocumentId)); |
||||
|
metadataDocument = metadataDocumentResponse.Resource; |
||||
|
} |
||||
|
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) |
||||
|
{ |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
} |
||||
|
|
||||
|
if (metadataDocument == null) |
||||
|
return results; |
||||
|
|
||||
|
// Determine the partition keys within the date range
|
||||
|
var partitionKeysInDocument = metadataDocument.PartitionKeys; |
||||
|
// Fetch the audit records for each partition key within the date range
|
||||
|
foreach (var partitionKey in partitionKeysInDocument) |
||||
|
{ |
||||
|
|
||||
|
ItemResponse<ContactsDocument> response = await container.ReadItemAsync<ContactsDocument>(partitionKey, new PartitionKey(partitionKey)); |
||||
|
if (response == null) |
||||
|
continue; |
||||
|
|
||||
|
ContactsDocument t = response.Resource; |
||||
|
results.AddRange(t.Records); |
||||
|
} |
||||
|
|
||||
|
return results; |
||||
|
} |
||||
|
|
||||
|
public static async Task<bool> AppendRecord(string appKey, string userId, ContactRecord rec) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var metadataDocument = await GetMetadataDocument(appKey, userId); |
||||
|
if (metadataDocument == null) |
||||
|
return false; |
||||
|
|
||||
|
string dayKey = metadataDocument.GetLatestKey(appKey, userId); |
||||
|
ContactsDocument al = await GetContactDocument(dayKey); |
||||
|
if (al == null) |
||||
|
{ |
||||
|
al = new ContactsDocument(); |
||||
|
al.AppKey = appKey; |
||||
|
al.UserId = userId; |
||||
|
} |
||||
|
al.Records.Add(rec); |
||||
|
await UpdateContactsDocument(al); |
||||
|
return true; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static async Task<bool> RemoveRecord(string appKey, string userId, string EmailId) |
||||
|
{ |
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
|
||||
|
// Fetch the metadata document for the customer ID
|
||||
|
var metadataDocumentId = GetMetaDocumentKey(appKey, userId); |
||||
|
MetadataDocumentContact metadataDocument = null; |
||||
|
try |
||||
|
{ |
||||
|
var metadataDocumentResponse = await container.ReadItemAsync<MetadataDocumentContact>(metadataDocumentId, new PartitionKey(metadataDocumentId)); |
||||
|
metadataDocument = metadataDocumentResponse.Resource; |
||||
|
} |
||||
|
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) |
||||
|
{ |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
} |
||||
|
|
||||
|
if (metadataDocument == null) |
||||
|
return false; |
||||
|
|
||||
|
// Determine the partition keys within the date range
|
||||
|
var partitionKeysInDocument = metadataDocument.PartitionKeys; |
||||
|
// Fetch the audit records for each partition key within the date range
|
||||
|
foreach (var partitionKey in partitionKeysInDocument) |
||||
|
{ |
||||
|
|
||||
|
ItemResponse<ContactsDocument> response = await container.ReadItemAsync<ContactsDocument>(partitionKey, new PartitionKey(partitionKey)); |
||||
|
if (response == null) |
||||
|
continue; |
||||
|
|
||||
|
ContactsDocument t = response.Resource; |
||||
|
var originalCount = t.Records.Count; |
||||
|
t.Records = t.Records.Where(item => item.Email != EmailId).ToList(); |
||||
|
var afterFilterCount = t.Records.Count; |
||||
|
|
||||
|
if (originalCount != afterFilterCount) |
||||
|
{ |
||||
|
ItemResponse<ContactsDocument> updateResponse = await container.ReplaceItemAsync( |
||||
|
item: t, |
||||
|
id: t.id, |
||||
|
partitionKey: new PartitionKey(partitionKey)); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static async Task UpdateContactsDocument(ContactsDocument al) |
||||
|
{ |
||||
|
if (al.Records.Count == 0) |
||||
|
return; |
||||
|
|
||||
|
List<ContactsDocument> lal = await SplitAuditlog(al); |
||||
|
|
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
|
||||
|
foreach (ContactsDocument ial in lal) |
||||
|
{ |
||||
|
ItemResponse<ContactsDocument> r = await container.UpsertItemAsync(ial, new PartitionKey(ial.id)); |
||||
|
} |
||||
|
|
||||
|
await UpdateMetadata(container, lal); |
||||
|
} |
||||
|
|
||||
|
static async Task UpdateMetadata(Container container, List<ContactsDocument> lal) |
||||
|
{ |
||||
|
bool update = false; |
||||
|
string pKey = GetMetaDocumentKey(lal[0].AppKey, lal[0].UserId); |
||||
|
MetadataDocument md = null; |
||||
|
try |
||||
|
{ |
||||
|
ItemResponse<MetadataDocument> response = |
||||
|
await container.ReadItemAsync<MetadataDocument>(pKey, new PartitionKey(pKey)); |
||||
|
md = response.Resource; |
||||
|
} |
||||
|
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) |
||||
|
{ |
||||
|
md = new MetadataDocument() |
||||
|
{ |
||||
|
id = GetMetaDocumentKey(lal[0].AppKey, lal[0].UserId), |
||||
|
PartitionKeys = new List<string>() |
||||
|
}; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (md == null) |
||||
|
{ |
||||
|
// Helpers.LogIt("Something ugly happened!");
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (ContactsDocument log in lal) |
||||
|
{ |
||||
|
if (md.PartitionKeys.Contains(log.id)) |
||||
|
continue; |
||||
|
md.PartitionKeys.Add(log.id); |
||||
|
update = true; |
||||
|
} |
||||
|
|
||||
|
if (update) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
ItemResponse<MetadataDocument> r = await container.UpsertItemAsync(md, new PartitionKey(pKey)); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static async Task<List<ContactsDocument>> SplitAuditlog(ContactsDocument al) |
||||
|
{ |
||||
|
List<ContactsDocument> lal = new List<ContactsDocument>(); |
||||
|
var sortedRecords = al.Records.OrderBy(record => record.EventTime).ToList(); |
||||
|
|
||||
|
var currentGroup = new List<ContactRecord>(); |
||||
|
var currentGroupSize = 0; |
||||
|
|
||||
|
int MaxDocumentSizeInBytes = 2 * 1024 * 1024; // 2MB
|
||||
|
|
||||
|
int index = al.Index; // start for index passed in
|
||||
|
foreach (var record in sortedRecords) |
||||
|
{ |
||||
|
var recordSize = record.CalculateRecordSize(); |
||||
|
|
||||
|
if (currentGroupSize + recordSize > MaxDocumentSizeInBytes) |
||||
|
{ |
||||
|
ContactsDocument i = new ContactsDocument(); |
||||
|
i.Index = index++; |
||||
|
i.Records = currentGroup; |
||||
|
i.AppKey = al.AppKey; |
||||
|
i.UserId = al.UserId; |
||||
|
lal.Add(i); |
||||
|
|
||||
|
currentGroup = new List<ContactRecord>(); |
||||
|
currentGroupSize = 0; |
||||
|
} |
||||
|
|
||||
|
currentGroup.Add(record); |
||||
|
currentGroupSize += recordSize; |
||||
|
} |
||||
|
|
||||
|
if (currentGroup.Any()) |
||||
|
{ |
||||
|
ContactsDocument i = new ContactsDocument(); |
||||
|
i.Index = index++; |
||||
|
i.Records = currentGroup; |
||||
|
i.AppKey = al.AppKey; |
||||
|
i.UserId = al.UserId; |
||||
|
lal.Add(i); |
||||
|
} |
||||
|
|
||||
|
return lal; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static async Task<ContactsDocument> GetContactDocument(string key) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
ItemResponse<ContactsDocument> response = await container.ReadItemAsync<ContactsDocument>(key, new PartitionKey(key)); |
||||
|
if (response == null) |
||||
|
return null; |
||||
|
|
||||
|
ContactsDocument t = response.Resource; |
||||
|
return t; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static async Task<MetadataDocumentContact> GetMetadataDocument(string appKey, string userId) |
||||
|
{ |
||||
|
MetadataDocumentContact md = null; |
||||
|
string pKey = GetMetaDocumentKey(appKey, userId); |
||||
|
string id = GetDocumentId(appKey, userId); |
||||
|
|
||||
|
PartitionKey partitionKey = new PartitionKeyBuilder() |
||||
|
.Add(pKey) |
||||
|
.Build(); |
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
try |
||||
|
{ |
||||
|
ItemResponse<MetadataDocumentContact> response = |
||||
|
await container.ReadItemAsync<MetadataDocumentContact>(pKey, partitionKey); |
||||
|
md = response.Resource; |
||||
|
} |
||||
|
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) |
||||
|
{ |
||||
|
md = new MetadataDocumentContact() |
||||
|
{ |
||||
|
id = id, |
||||
|
PartitionKeys = new List<string>() |
||||
|
}; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (md == null) |
||||
|
{ |
||||
|
// Helpers.LogIt("Something ugly happened!");
|
||||
|
return null; |
||||
|
} |
||||
|
return md; |
||||
|
} |
||||
|
|
||||
|
static string GetMetaDocumentKey(string appKey, string userId) |
||||
|
{ |
||||
|
return $"{appKey}-{userId}-meta"; |
||||
|
} |
||||
|
|
||||
|
static string GetDocumentId(string appKey, string userId) |
||||
|
{ |
||||
|
return $"{appKey}-{userId}"; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,311 @@ |
|||||
|
using Azure.Messaging.ServiceBus; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Text.Json; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Azure.Storage.Blobs; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Azure.Functions.Worker.Http; |
||||
|
using Microsoft.Azure.Functions.Worker; |
||||
|
using System.Net; |
||||
|
using Microsoft.DurableTask; |
||||
|
using Microsoft.DurableTask.Client; |
||||
|
using FromBodyAttribute = Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute; |
||||
|
|
||||
|
namespace CDP |
||||
|
{ |
||||
|
public class MetaProcessor |
||||
|
{ |
||||
|
private readonly ILogger<MetaProcessor> _logger; |
||||
|
private static Lazy<ServiceBusClient> lazyBusClient = new Lazy<ServiceBusClient>(InitializeServiceBusClient); |
||||
|
private static ServiceBusClient _serviceBusClient = lazyBusClient.Value; |
||||
|
|
||||
|
public MetaProcessor(ILogger<MetaProcessor> logger) |
||||
|
{ |
||||
|
_logger = logger; |
||||
|
} |
||||
|
|
||||
|
private static ServiceBusClient InitializeServiceBusClient() |
||||
|
{ |
||||
|
return new ServiceBusClient(Constants.SvcBusConnectionString); |
||||
|
} |
||||
|
|
||||
|
public static async Task PublishBatchJobs(List<Job> jobs) |
||||
|
{ |
||||
|
//Job[] jobsCopy = jobs.ToArray();
|
||||
|
var sender = _serviceBusClient.CreateSender("mail-events-queue"); |
||||
|
ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync(); |
||||
|
jobs.ForEach(job => |
||||
|
{ |
||||
|
var msg = new ServiceBusMessage(JsonSerializer.Serialize(job)); |
||||
|
bool isAdded = batch.TryAddMessage(msg); |
||||
|
}); |
||||
|
await sender.SendMessagesAsync(batch); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static async Task PublishJob(Job job) |
||||
|
{ |
||||
|
var sender = _serviceBusClient.CreateSender("mail-events-queue"); |
||||
|
ServiceBusMessage msg = new ServiceBusMessage(JsonSerializer.Serialize(job)); |
||||
|
await sender.SendMessageAsync(msg); |
||||
|
} |
||||
|
[Function("MailMetaProcessor")] |
||||
|
public async Task<Job> MailMetaProcessor([ActivityTrigger] MailRecord mailRecord, FunctionContext functionContext) |
||||
|
{ |
||||
|
Job job = await TriggerMailProcessor(mailRecord); |
||||
|
return job; |
||||
|
} |
||||
|
|
||||
|
[Function("MailMetaOrchestrator")] |
||||
|
public async Task<Job> MailMetaOrchestrator([OrchestrationTrigger] TaskOrchestrationContext ctx) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
MailRecord record = ctx.GetInput<MailRecord>(); |
||||
|
string jobId = await ctx.CallActivityAsync<string>(nameof(AddProtectionAudits), record); |
||||
|
Job job = await ctx.CallActivityAsync<Job>(nameof(MailMetaProcessor), record); |
||||
|
string revId = await ctx.CallActivityAsync<string>(nameof(AddMailCompundRevision), record); |
||||
|
return job; |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex.Message); |
||||
|
return new Job() { AppKey = "", EventType = JobType.MailMetaProcessing, Id = "failed" }; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Function("AddMailMetaDurable")] |
||||
|
public async Task<HttpResponseData> AddMailMetaDurable([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req, |
||||
|
[FromBody] MailRecord recordMail, |
||||
|
[DurableClient] DurableTaskClient client) |
||||
|
{ |
||||
|
bool isValid = true; |
||||
|
//bool isValid = await CDPLite.CheckJwtRequest(req);
|
||||
|
|
||||
|
if (!isValid) |
||||
|
{ |
||||
|
HttpResponseData res = req.CreateResponse(HttpStatusCode.Unauthorized); |
||||
|
return res; |
||||
|
} |
||||
|
try |
||||
|
{ |
||||
|
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(nameof(MailMetaOrchestrator), recordMail); |
||||
|
_logger.LogInformation("created instance: {0}", instanceId); |
||||
|
return client.CreateCheckStatusResponse(req, instanceId); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex.ToString()); |
||||
|
HttpResponseData response = req.CreateResponse(HttpStatusCode.InternalServerError); |
||||
|
return response; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Function("AddMailMeta")] |
||||
|
public async Task<ActionResult> AddMailMeta([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req, [FromBody] MailRecord recordMail) |
||||
|
{ |
||||
|
bool isValid = true; |
||||
|
//bool isValid = await CDPLite.CheckJwtRequest(req);
|
||||
|
if (!isValid) |
||||
|
{ |
||||
|
return new UnauthorizedObjectResult("Jwt has expired please validate"); |
||||
|
} |
||||
|
try |
||||
|
{ |
||||
|
MailRecord mailRecord = recordMail; |
||||
|
//_logger.LogInformation(JsonSerializer.Serialize(recordMail));
|
||||
|
string res = await TriggerProtectionAuditsJob(mailRecord); |
||||
|
res = await AddMailCompoundRevisionInternal(mailRecord); |
||||
|
_logger.LogInformation($"protection audits? {res}"); |
||||
|
Job job = await TriggerMailProcessor(mailRecord); |
||||
|
return new OkObjectResult(job); |
||||
|
|
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex.ToString()); |
||||
|
return new StatusCodeResult(500); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
[Function("AddMailForwardingRequest")] |
||||
|
public async Task<IActionResult> AddMailForwardingRequest([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
ForwardMailDto forwardMail; |
||||
|
try |
||||
|
{ |
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
_logger.LogInformation("requestbody: {0}", requestBody); |
||||
|
forwardMail = JsonSerializer.Deserialize<ForwardMailDto>(requestBody); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogInformation(ex.ToString()); |
||||
|
return new StatusCodeResult(500); |
||||
|
} |
||||
|
string appKey = forwardMail.AppKey; |
||||
|
var userId = Helpers.HashAndShortenText(forwardMail.SenderEmail.ToLower()); |
||||
|
var fr = await CDPDB.GetFile(appKey, forwardMail.MailId, userId); |
||||
|
if ((!fr.Policy.CheckAccess("Manage")) && (!fr.Policy.CheckAccess("Owner"))) |
||||
|
{ |
||||
|
return new UnauthorizedObjectResult("User doesn't have rights to forward this email"); |
||||
|
} |
||||
|
RevisionEntry entry = await FileRevision.GetFileRevisionInternal(forwardMail.AppKey, forwardMail.MailId); |
||||
|
if (entry.CompoundDocument == null) |
||||
|
{ |
||||
|
return new BadRequestObjectResult("IncorrectMailId"); |
||||
|
} |
||||
|
MailMetaRecord mailRecord = JsonSerializer.Deserialize<MailMetaRecord>(entry.CompoundDocument.Contents); |
||||
|
|
||||
|
mailRecord.AttachmentDetails.ForEach(async (att) => |
||||
|
{ |
||||
|
forwardMail.ReceiverEmails.ForEach(async (receiver) => |
||||
|
{ |
||||
|
await AddMailForwardPolicy(appKey, att.FileId, att.FileName, forwardMail.SenderEmail, receiver, "Read"); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
forwardMail.ReceiverEmails.ForEach(async (receiver) => |
||||
|
{ |
||||
|
await AddMailForwardPolicy(appKey, mailRecord.BodyId, string.Concat(mailRecord.BodyId, "-", "MailBody"), forwardMail.SenderEmail, receiver, "Read"); |
||||
|
}); |
||||
|
|
||||
|
return new OkObjectResult("Done"); |
||||
|
} |
||||
|
|
||||
|
internal async Task AddMailForwardPolicy(string appKey, string fileId, string fileName, string sender, string receiver, string policy = "Read") |
||||
|
{ |
||||
|
AddFileUserDto addFileUserDto = new AddFileUserDto |
||||
|
{ |
||||
|
AppKey = appKey, |
||||
|
FileId = fileId, |
||||
|
FileName = fileName, |
||||
|
Email = sender, |
||||
|
EmailToAdd = receiver, |
||||
|
Policy = policy |
||||
|
}; |
||||
|
await CDPLite.AddFileUserInternal(addFileUserDto); |
||||
|
} |
||||
|
|
||||
|
[Function("AddMailCompundRevision")] |
||||
|
public async Task<string> AddMailCompundRevision([ActivityTrigger] MailRecord record, FunctionContext context) |
||||
|
{ |
||||
|
|
||||
|
return await AddMailCompoundRevisionInternal(record); |
||||
|
} |
||||
|
|
||||
|
internal async Task<string> AddMailCompoundRevisionInternal(MailRecord record) |
||||
|
{ |
||||
|
RevisionDocument doc = new RevisionDocument |
||||
|
{ |
||||
|
RevisionId = Guid.NewGuid().ToString(), |
||||
|
RevisionDate = DateTime.UtcNow, |
||||
|
EditedBy = record.SenderEmail, |
||||
|
Comments = "MailBody" |
||||
|
}; |
||||
|
MailMetaRecord mailMeta = new MailMetaRecord |
||||
|
{ |
||||
|
AppKey = record.AppKey, |
||||
|
SenderEmail = record.SenderEmail, |
||||
|
BodyContent = record.BodyContent, |
||||
|
BodyId = record.MailId, |
||||
|
AttachmentDetails = record.AttachmentDetails |
||||
|
}; |
||||
|
CompoundDocument compound = new CompoundDocument |
||||
|
{ |
||||
|
Id = Guid.NewGuid().ToString(), |
||||
|
AppKey = record.AppKey, |
||||
|
Contents = JsonSerializer.Serialize(mailMeta), |
||||
|
DocumentType = "MailRecord" |
||||
|
}; |
||||
|
|
||||
|
string revisionId = await FileRevision.AddMailBodyRevision(doc, compound, record.AppKey, record.MailId); |
||||
|
return revisionId; |
||||
|
} |
||||
|
|
||||
|
[Function("AddProtectionAudits")] |
||||
|
public async Task<string> AddProtectionAudits([ActivityTrigger] MailRecord record, FunctionContext functionContext) |
||||
|
{ |
||||
|
var res = await TriggerProtectionAuditsJob(record); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
internal async Task<string> TriggerProtectionAuditsJob(MailRecord record) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
string userId = Helpers.HashAndShortenText(record.SenderEmail); |
||||
|
string message = string.Format($"{record.SenderEmail} protected an email body with id: {record.MailId}."); |
||||
|
List<AuditEventMetadata> auditEvents = new List<AuditEventMetadata>(); |
||||
|
AuditEventMetadata auditEvent = new AuditEventMetadata { Action = "Addded", FileId = record.MailId, FileName = "Outlook-Mail-Body", Message = message, UserId = userId }; |
||||
|
auditEvents.Add(auditEvent); |
||||
|
record.AttachmentDetails.ForEach((att) => |
||||
|
{ |
||||
|
message = string.Format($"{record.SenderEmail} protected the email attachment {att.FileName} with id: {att.FileId}."); |
||||
|
AuditEventMetadata attachmentEvent = new AuditEventMetadata { Action = "Added", FileId = att.FileId, FileName = att.FileName, Message = message, UserId = userId }; |
||||
|
auditEvents.Add(attachmentEvent); |
||||
|
}); |
||||
|
Job job = await CDPLite.AddAuditsBatchedEvent(auditEvents, record.AppKey); |
||||
|
await PublishJob(job); |
||||
|
_logger.LogInformation(JsonSerializer.Serialize(job)); |
||||
|
return "Done"; |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex.ToString()); |
||||
|
throw ex; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal async Task<Job> TriggerMailProcessor(MailRecord mailRecord) |
||||
|
{ |
||||
|
string Connection = Constants.StorageConnString; |
||||
|
var blobSvc = new BlobServiceClient(Connection); |
||||
|
var blobClient = blobSvc.GetBlobContainerClient(mailRecord.AppKey.ToLower()); |
||||
|
|
||||
|
if (!blobClient.Exists()) |
||||
|
{ |
||||
|
await blobClient.CreateAsync(); |
||||
|
} |
||||
|
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(JsonSerializer.Serialize(mailRecord)); |
||||
|
string jobId = Guid.NewGuid().ToString(); |
||||
|
var blob = blobClient.GetBlobClient(jobId); |
||||
|
Stream fs = new MemoryStream(fileBytes); |
||||
|
try |
||||
|
{ |
||||
|
await blob.UploadAsync(fs); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex.ToString()); |
||||
|
throw ex; |
||||
|
} |
||||
|
|
||||
|
Job job = new Job { AppKey = mailRecord.AppKey, EventType = JobType.MailMetaProcessing, Id = jobId }; |
||||
|
await PublishJob(job); |
||||
|
return job; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MailMetaRecord |
||||
|
{ |
||||
|
public required string AppKey { get; set; } |
||||
|
public required string SenderEmail { get; set; } |
||||
|
public required string BodyId { get; set; } |
||||
|
public required string BodyContent { get; set; } //encrypted body
|
||||
|
public required List<AttachmentDetails> AttachmentDetails { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class ForwardMailDto |
||||
|
{ |
||||
|
public string SenderEmail { get; set; } |
||||
|
public List<string> ReceiverEmails { get; set; } |
||||
|
public string MailId { get; set; } |
||||
|
public string AppKey { get; set; } |
||||
|
} |
||||
|
} |
@ -0,0 +1,388 @@ |
|||||
|
using Microsoft.Azure.Cosmos; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace CDP |
||||
|
{ |
||||
|
public class GroupsDB |
||||
|
{ |
||||
|
private static Lazy<CosmosClient> lazyClient = new Lazy<CosmosClient>(InitializeCosmosClient); |
||||
|
private static CosmosClient cosmosClient => lazyClient.Value; |
||||
|
private static string DatabaseName = "CDP"; |
||||
|
private static string ContainerName = "Groups"; |
||||
|
|
||||
|
private static CosmosClient InitializeCosmosClient() |
||||
|
{ |
||||
|
// Perform any initialization here
|
||||
|
var uri = "https://cdplite.documents.azure.com:443/"; |
||||
|
var authKey = "VPbg8RpzyI3XwhC2o0dIUtYFs33ghxORCqZeNAyg8vg4HWUBjM41BUxP0qLFXEvFh6ewQY1uKv52ACDbsEN1AQ=="; |
||||
|
|
||||
|
return new CosmosClient(uri, authKey); |
||||
|
} |
||||
|
|
||||
|
public static async Task<List<GroupsRecord>> GetUserGroups(string AppKey, string UserId) |
||||
|
{ |
||||
|
var results = new List<GroupsRecord>(); |
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
|
||||
|
// Fetch the metadata document for the customer ID
|
||||
|
var metadataDocumentId = GetMetaDocumentKey(AppKey, UserId); |
||||
|
MetadataDocumentGroups metadataDocument = null; |
||||
|
try |
||||
|
{ |
||||
|
var metadataDocumentResponse = |
||||
|
await container.ReadItemAsync<MetadataDocumentGroups>(metadataDocumentId, new PartitionKey(metadataDocumentId)); |
||||
|
metadataDocument = metadataDocumentResponse.Resource; |
||||
|
} |
||||
|
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) |
||||
|
{ |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
} |
||||
|
|
||||
|
if (metadataDocument == null) |
||||
|
return results; |
||||
|
|
||||
|
// Determine the partition keys within the date range
|
||||
|
var partitionKeysInDocument = metadataDocument.PartitionKeys; |
||||
|
// Fetch the audit records for each partition key within the date range
|
||||
|
foreach (var partitionKey in partitionKeysInDocument) |
||||
|
{ |
||||
|
|
||||
|
ItemResponse<GroupsDocument> response = await container.ReadItemAsync<GroupsDocument>(partitionKey, new PartitionKey(partitionKey)); |
||||
|
if (response == null) |
||||
|
continue; |
||||
|
|
||||
|
GroupsDocument t = response.Resource; |
||||
|
results.AddRange(t.Records); |
||||
|
} |
||||
|
|
||||
|
return results; |
||||
|
} |
||||
|
|
||||
|
public static async Task<bool> RemoveGroup(string appKey, string userId, string groupId) |
||||
|
{ |
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
|
||||
|
// Fetch the metadata document for the customer ID
|
||||
|
var metadataDocumentId = GetMetaDocumentKey(appKey, userId); |
||||
|
MetadataDocumentContact metadataDocument = null; |
||||
|
try |
||||
|
{ |
||||
|
var metadataDocumentResponse = await container.ReadItemAsync<MetadataDocumentContact>(metadataDocumentId, new PartitionKey(metadataDocumentId)); |
||||
|
metadataDocument = metadataDocumentResponse.Resource; |
||||
|
} |
||||
|
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) |
||||
|
{ |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
} |
||||
|
|
||||
|
if (metadataDocument == null) |
||||
|
return false; |
||||
|
|
||||
|
// Determine the partition keys within the date range
|
||||
|
var partitionKeysInDocument = metadataDocument.PartitionKeys; |
||||
|
// Fetch the audit records for each partition key within the date range
|
||||
|
foreach (var partitionKey in partitionKeysInDocument) |
||||
|
{ |
||||
|
|
||||
|
ItemResponse<GroupsDocument> response = await container.ReadItemAsync<GroupsDocument>(partitionKey, new PartitionKey(partitionKey)); |
||||
|
if (response == null) |
||||
|
continue; |
||||
|
|
||||
|
GroupsDocument t = response.Resource; |
||||
|
var originalCount = t.Records.Count; |
||||
|
t.Records = t.Records.Where(item => item.id != groupId).ToList(); |
||||
|
var afterFilterCount = t.Records.Count; |
||||
|
|
||||
|
if (originalCount != afterFilterCount) |
||||
|
{ |
||||
|
ItemResponse<GroupsDocument> updateResponse = await container.ReplaceItemAsync( |
||||
|
item: t, |
||||
|
id: t.id, |
||||
|
partitionKey: new PartitionKey(partitionKey)); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static async Task<bool> AppendGroup(string appKey, string userId, String grpName, string grpDescription, List<ContactRecord> rec) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var metadataDocument = await GetMetadataDocument(appKey, userId); |
||||
|
if (metadataDocument == null) |
||||
|
return false; |
||||
|
|
||||
|
string dayKey = metadataDocument.GetLatestKey(appKey, userId); |
||||
|
GroupsDocument al = await GetGroupDocument(dayKey); |
||||
|
if (al == null) |
||||
|
{ |
||||
|
al = new GroupsDocument(); |
||||
|
al.AppKey = appKey; |
||||
|
al.UserId = userId; |
||||
|
} |
||||
|
GroupsRecord gr = new GroupsRecord(grpName, grpDescription, rec); |
||||
|
al.Records.Add(gr); |
||||
|
await UpdateGroupsDocument(al); |
||||
|
return true; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static async Task<bool> UpdateGroup(string appKey, string userId, string grpId, string grpName, string grpDescription, List<ContactRecord> contacts) |
||||
|
{ |
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
|
||||
|
// Fetch the metadata document for the customer ID
|
||||
|
var metadataDocumentId = GetMetaDocumentKey(appKey, userId); |
||||
|
MetadataDocumentContact metadataDocument = null; |
||||
|
try |
||||
|
{ |
||||
|
var metadataDocumentResponse = await container.ReadItemAsync<MetadataDocumentContact>(metadataDocumentId, new PartitionKey(metadataDocumentId)); |
||||
|
metadataDocument = metadataDocumentResponse.Resource; |
||||
|
} |
||||
|
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) |
||||
|
{ |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
} |
||||
|
|
||||
|
if (metadataDocument == null) |
||||
|
return false; |
||||
|
|
||||
|
// Determine the partition keys within the date range
|
||||
|
var partitionKeysInDocument = metadataDocument.PartitionKeys; |
||||
|
// Fetch the audit records for each partition key within the date range
|
||||
|
foreach (var partitionKey in partitionKeysInDocument) |
||||
|
{ |
||||
|
|
||||
|
ItemResponse<GroupsDocument> response = await container.ReadItemAsync<GroupsDocument>(partitionKey, new PartitionKey(partitionKey)); |
||||
|
if (response == null) |
||||
|
continue; |
||||
|
|
||||
|
GroupsDocument t = response.Resource; |
||||
|
bool groupFound = false; |
||||
|
foreach (GroupsRecord gr in t.Records) |
||||
|
{ |
||||
|
if (gr.id == grpId) |
||||
|
{ |
||||
|
gr.Name = grpName != "" ? grpName : gr.Name; |
||||
|
gr.Description = grpDescription != "" ? grpDescription : gr.Description; |
||||
|
gr.Contacts = contacts; |
||||
|
groupFound = true; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (groupFound) |
||||
|
{ |
||||
|
ItemResponse<GroupsDocument> updateResponse = await container.ReplaceItemAsync( |
||||
|
item: t, |
||||
|
id: t.id, |
||||
|
partitionKey: new PartitionKey(partitionKey)); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static async Task UpdateGroupsDocument(GroupsDocument al) |
||||
|
{ |
||||
|
if (al.Records.Count == 0) |
||||
|
return; |
||||
|
|
||||
|
List<GroupsDocument> lal = await SplitUserGroups(al); |
||||
|
|
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
|
||||
|
foreach (GroupsDocument ial in lal) |
||||
|
{ |
||||
|
ItemResponse<GroupsDocument> r = await container.UpsertItemAsync(ial, new PartitionKey(ial.id)); |
||||
|
} |
||||
|
|
||||
|
await UpdateMetadata(container, lal); |
||||
|
} |
||||
|
|
||||
|
static async Task UpdateMetadata(Container container, List<GroupsDocument> lal) |
||||
|
{ |
||||
|
bool update = false; |
||||
|
string pKey = GetMetaDocumentKey(lal[0].AppKey, lal[0].UserId); |
||||
|
MetadataDocument md = null; |
||||
|
try |
||||
|
{ |
||||
|
ItemResponse<MetadataDocument> response = |
||||
|
await container.ReadItemAsync<MetadataDocument>(pKey, new PartitionKey(pKey)); |
||||
|
md = response.Resource; |
||||
|
} |
||||
|
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) |
||||
|
{ |
||||
|
md = new MetadataDocument() |
||||
|
{ |
||||
|
id = GetMetaDocumentKey(lal[0].AppKey, lal[0].UserId), |
||||
|
PartitionKeys = new List<string>() |
||||
|
}; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (md == null) |
||||
|
{ |
||||
|
// Helpers.LogIt("Something ugly happened!");
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (GroupsDocument log in lal) |
||||
|
{ |
||||
|
if (md.PartitionKeys.Contains(log.id)) |
||||
|
continue; |
||||
|
md.PartitionKeys.Add(log.id); |
||||
|
update = true; |
||||
|
} |
||||
|
|
||||
|
if (update) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
ItemResponse<MetadataDocument> r = await container.UpsertItemAsync(md, new PartitionKey(pKey)); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static async Task<List<GroupsDocument>> SplitUserGroups(GroupsDocument al) |
||||
|
{ |
||||
|
List<GroupsDocument> lal = new List<GroupsDocument>(); |
||||
|
var sortedRecords = al.Records.OrderBy(record => record.EventTime).ToList(); |
||||
|
|
||||
|
var currentGroup = new List<GroupsRecord>(); |
||||
|
var currentGroupSize = 0; |
||||
|
|
||||
|
int MaxDocumentSizeInBytes = 2 * 1024 * 1024; // 2MB
|
||||
|
|
||||
|
int index = al.Index; // start for index passed in
|
||||
|
foreach (var record in sortedRecords) |
||||
|
{ |
||||
|
var recordSize = record.CalculateRecordSize(); |
||||
|
|
||||
|
if (currentGroupSize + recordSize > MaxDocumentSizeInBytes) |
||||
|
{ |
||||
|
GroupsDocument i = new GroupsDocument(); |
||||
|
i.Index = index++; |
||||
|
i.Records = currentGroup; |
||||
|
i.AppKey = al.AppKey; |
||||
|
i.UserId = al.UserId; |
||||
|
lal.Add(i); |
||||
|
|
||||
|
currentGroup = new List<GroupsRecord>(); |
||||
|
currentGroupSize = 0; |
||||
|
} |
||||
|
|
||||
|
currentGroup.Add(record); |
||||
|
currentGroupSize += recordSize; |
||||
|
} |
||||
|
|
||||
|
if (currentGroup.Any()) |
||||
|
{ |
||||
|
GroupsDocument i = new GroupsDocument(); |
||||
|
i.Index = index++; |
||||
|
i.Records = currentGroup; |
||||
|
i.AppKey = al.AppKey; |
||||
|
i.UserId = al.UserId; |
||||
|
lal.Add(i); |
||||
|
} |
||||
|
|
||||
|
return lal; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static async Task<GroupsDocument> GetGroupDocument(string key) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
ItemResponse<GroupsDocument> response = await container.ReadItemAsync<GroupsDocument>(key, new PartitionKey(key)); |
||||
|
if (response == null) |
||||
|
return null; |
||||
|
|
||||
|
GroupsDocument t = response.Resource; |
||||
|
return t; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static async Task<MetadataDocumentGroups> GetMetadataDocument(string appKey, string userId) |
||||
|
{ |
||||
|
MetadataDocumentGroups md = null; |
||||
|
string pKey = GetMetaDocumentKey(appKey, userId); |
||||
|
string id = GetDocumentId(appKey, userId); |
||||
|
|
||||
|
PartitionKey partitionKey = new PartitionKeyBuilder() |
||||
|
.Add(pKey) |
||||
|
.Build(); |
||||
|
Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); |
||||
|
try |
||||
|
{ |
||||
|
ItemResponse<MetadataDocumentGroups> response = |
||||
|
await container.ReadItemAsync<MetadataDocumentGroups>(pKey, partitionKey); |
||||
|
md = response.Resource; |
||||
|
} |
||||
|
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) |
||||
|
{ |
||||
|
md = new MetadataDocumentGroups() |
||||
|
{ |
||||
|
id = id, |
||||
|
PartitionKeys = new List<string>() |
||||
|
}; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
// Helpers.LogIt(e.Message);
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (md == null) |
||||
|
{ |
||||
|
// Helpers.LogIt("Something ugly happened!");
|
||||
|
return null; |
||||
|
} |
||||
|
return md; |
||||
|
} |
||||
|
|
||||
|
static string GetMetaDocumentKey(string appKey, string userId) |
||||
|
{ |
||||
|
return $"{appKey}-{userId}-meta"; |
||||
|
} |
||||
|
|
||||
|
static string GetDocumentId(string appKey, string userId) |
||||
|
{ |
||||
|
return $"{appKey}-{userId}"; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,107 @@ |
|||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Azure.Functions.Worker; |
||||
|
using Microsoft.Azure.Functions.Worker.Http; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
namespace CDP |
||||
|
{ |
||||
|
public class GroupsFunctions |
||||
|
{ |
||||
|
private readonly ILogger<GroupsFunctions> _logger; |
||||
|
|
||||
|
public GroupsFunctions(ILogger<GroupsFunctions> logger) |
||||
|
{ |
||||
|
_logger = logger; |
||||
|
} |
||||
|
|
||||
|
[Function("AddGroup")] |
||||
|
public async Task<IActionResult> AddGroup([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("Adding a new group invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
AddUserGroupDto addUserGroupDto = JsonConvert.DeserializeObject<AddUserGroupDto>(requestBody); |
||||
|
if (addUserGroupDto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(addUserGroupDto.EmailId.ToLower()); |
||||
|
|
||||
|
List<ContactRecord> contacts = new List<ContactRecord>(); |
||||
|
foreach (Contact contact in addUserGroupDto.Contacts) |
||||
|
{ |
||||
|
ContactRecord cr = new ContactRecord(contact.Name, contact.Email, contact.Phone, contact.Address, contact.Company); |
||||
|
contacts.Add(cr); |
||||
|
} |
||||
|
|
||||
|
Boolean resp = await GroupsDB.AppendGroup(addUserGroupDto.AppKey, userId, addUserGroupDto.GroupName, addUserGroupDto.GroupDescription, contacts); |
||||
|
|
||||
|
return new OkObjectResult(resp); |
||||
|
} |
||||
|
|
||||
|
[Function("GetGroups")] |
||||
|
public async Task<IActionResult> GetGroups([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("Getting groups invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
GetUserGroupDto getUserGroupDto = JsonConvert.DeserializeObject<GetUserGroupDto>(requestBody); |
||||
|
if (getUserGroupDto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(getUserGroupDto.EmailId.ToLower()); |
||||
|
|
||||
|
List<GroupsRecord> fr = await GroupsDB.GetUserGroups(getUserGroupDto.AppKey, userId); |
||||
|
if (fr == null) |
||||
|
{ |
||||
|
return new BadRequestObjectResult(new { error = true, message = "groups not found " + getUserGroupDto.EmailId }); |
||||
|
} |
||||
|
|
||||
|
return new OkObjectResult(fr); |
||||
|
} |
||||
|
|
||||
|
[Function("DeleteGroup")] |
||||
|
public async Task<IActionResult> DeleteGroup([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("Deleting Group invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
DeleteGroupDto deleteGroupdto = JsonConvert.DeserializeObject<DeleteGroupDto>(requestBody); |
||||
|
if (deleteGroupdto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(deleteGroupdto.UserEmail.ToLower()); |
||||
|
Boolean resp = await GroupsDB.RemoveGroup(deleteGroupdto.AppKey, userId, deleteGroupdto.GroupId); |
||||
|
return new OkObjectResult(resp); |
||||
|
} |
||||
|
|
||||
|
[Function("UpdateGroup")] |
||||
|
public async Task<IActionResult> UpdateGroup([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) |
||||
|
{ |
||||
|
_logger.LogInformation("Updating Group invoked"); |
||||
|
|
||||
|
// Convert the JSON payload to a string
|
||||
|
string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); |
||||
|
UpdateGroupDto updateUserGroupDto = JsonConvert.DeserializeObject<UpdateGroupDto>(requestBody); |
||||
|
if (updateUserGroupDto == null) |
||||
|
return new BadRequestObjectResult(new { error = true, message = "Parse error." }); |
||||
|
|
||||
|
string userId = Helpers.HashAndShortenText(updateUserGroupDto.UserEmail.ToLower()); |
||||
|
|
||||
|
List<ContactRecord> contacts = new List<ContactRecord>(); |
||||
|
foreach (Contact contact in updateUserGroupDto.Contacts) |
||||
|
{ |
||||
|
ContactRecord cr = new ContactRecord(contact.Name, contact.Email, contact.Phone, contact.Address, contact.Company); |
||||
|
contacts.Add(cr); |
||||
|
} |
||||
|
|
||||
|
Boolean resp = await GroupsDB.UpdateGroup(updateUserGroupDto.AppKey, userId, updateUserGroupDto.GroupId, updateUserGroupDto.GroupName, updateUserGroupDto.GroupDescription, contacts); |
||||
|
|
||||
|
return new OkObjectResult(resp); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,12 +1,15 @@ |
|||||
{ |
{ |
||||
"version": "2.0", |
|
||||
"logging": { |
|
||||
"applicationInsights": { |
|
||||
"samplingSettings": { |
|
||||
"isEnabled": true, |
|
||||
"excludedTypes": "Request" |
|
||||
}, |
|
||||
"enableLiveMetricsFilters": true |
|
||||
} |
|
||||
|
"version": "2.0", |
||||
|
"logging": { |
||||
|
"applicationInsights": { |
||||
|
"samplingSettings": { |
||||
|
"isEnabled": true, |
||||
|
"excludedTypes": "Request" |
||||
|
}, |
||||
|
"enableLiveMetricsFilters": true |
||||
} |
} |
||||
|
}, |
||||
|
"tracing": { |
||||
|
"consoleLevel": "verbose" |
||||
|
} |
||||
} |
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue