Temporary repo to track my changes on LTS functions app porting
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

311 lines
13 KiB

  1. using Azure.Messaging.ServiceBus;
  2. using Microsoft.Extensions.Logging;
  3. using System;
  4. using System.Text.Json;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using Azure.Storage.Blobs;
  10. using Microsoft.AspNetCore.Mvc;
  11. using Microsoft.Azure.Functions.Worker.Http;
  12. using Microsoft.Azure.Functions.Worker;
  13. using System.Net;
  14. using Microsoft.DurableTask;
  15. using Microsoft.DurableTask.Client;
  16. using FromBodyAttribute = Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute;
  17. namespace CDP
  18. {
  19. public class MetaProcessor
  20. {
  21. private readonly ILogger<MetaProcessor> _logger;
  22. private static Lazy<ServiceBusClient> lazyBusClient = new Lazy<ServiceBusClient>(InitializeServiceBusClient);
  23. private static ServiceBusClient _serviceBusClient = lazyBusClient.Value;
  24. public MetaProcessor(ILogger<MetaProcessor> logger)
  25. {
  26. _logger = logger;
  27. }
  28. private static ServiceBusClient InitializeServiceBusClient()
  29. {
  30. return new ServiceBusClient(Constants.SvcBusConnectionString);
  31. }
  32. public static async Task PublishBatchJobs(List<Job> jobs)
  33. {
  34. //Job[] jobsCopy = jobs.ToArray();
  35. var sender = _serviceBusClient.CreateSender("mail-events-queue");
  36. ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync();
  37. jobs.ForEach(job =>
  38. {
  39. var msg = new ServiceBusMessage(JsonSerializer.Serialize(job));
  40. bool isAdded = batch.TryAddMessage(msg);
  41. });
  42. await sender.SendMessagesAsync(batch);
  43. }
  44. public static async Task PublishJob(Job job)
  45. {
  46. var sender = _serviceBusClient.CreateSender("mail-events-queue");
  47. ServiceBusMessage msg = new ServiceBusMessage(JsonSerializer.Serialize(job));
  48. await sender.SendMessageAsync(msg);
  49. }
  50. [Function("MailMetaProcessor")]
  51. public async Task<Job> MailMetaProcessor([ActivityTrigger] MailRecord mailRecord, FunctionContext functionContext)
  52. {
  53. Job job = await TriggerMailProcessor(mailRecord);
  54. return job;
  55. }
  56. [Function("MailMetaOrchestrator")]
  57. public async Task<Job> MailMetaOrchestrator([OrchestrationTrigger] TaskOrchestrationContext ctx)
  58. {
  59. try
  60. {
  61. MailRecord record = ctx.GetInput<MailRecord>();
  62. string jobId = await ctx.CallActivityAsync<string>(nameof(AddProtectionAudits), record);
  63. Job job = await ctx.CallActivityAsync<Job>(nameof(MailMetaProcessor), record);
  64. string revId = await ctx.CallActivityAsync<string>(nameof(AddMailCompundRevision), record);
  65. return job;
  66. }
  67. catch (Exception ex)
  68. {
  69. _logger.LogError(ex.Message);
  70. return new Job() { AppKey = "", EventType = JobType.MailMetaProcessing, Id = "failed" };
  71. }
  72. }
  73. [Function("AddMailMetaDurable")]
  74. public async Task<HttpResponseData> AddMailMetaDurable([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
  75. [FromBody] MailRecord recordMail,
  76. [DurableClient] DurableTaskClient client)
  77. {
  78. bool isValid = true;
  79. //bool isValid = await CDPLite.CheckJwtRequest(req);
  80. if (!isValid)
  81. {
  82. HttpResponseData res = req.CreateResponse(HttpStatusCode.Unauthorized);
  83. return res;
  84. }
  85. try
  86. {
  87. string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(nameof(MailMetaOrchestrator), recordMail);
  88. _logger.LogInformation("created instance: {0}", instanceId);
  89. return client.CreateCheckStatusResponse(req, instanceId);
  90. }
  91. catch (Exception ex)
  92. {
  93. _logger.LogError(ex.ToString());
  94. HttpResponseData response = req.CreateResponse(HttpStatusCode.InternalServerError);
  95. return response;
  96. }
  97. }
  98. [Function("AddMailMeta")]
  99. public async Task<ActionResult> AddMailMeta([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req, [FromBody] MailRecord recordMail)
  100. {
  101. bool isValid = true;
  102. //bool isValid = await CDPLite.CheckJwtRequest(req);
  103. if (!isValid)
  104. {
  105. return new UnauthorizedObjectResult("Jwt has expired please validate");
  106. }
  107. try
  108. {
  109. MailRecord mailRecord = recordMail;
  110. //_logger.LogInformation(JsonSerializer.Serialize(recordMail));
  111. string res = await TriggerProtectionAuditsJob(mailRecord);
  112. res = await AddMailCompoundRevisionInternal(mailRecord);
  113. _logger.LogInformation($"protection audits? {res}");
  114. Job job = await TriggerMailProcessor(mailRecord);
  115. return new OkObjectResult(job);
  116. }
  117. catch (Exception ex)
  118. {
  119. _logger.LogError(ex.ToString());
  120. return new StatusCodeResult(500);
  121. }
  122. }
  123. [Function("AddMailForwardingRequest")]
  124. public async Task<IActionResult> AddMailForwardingRequest([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req)
  125. {
  126. ForwardMailDto forwardMail;
  127. try
  128. {
  129. string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
  130. _logger.LogInformation("requestbody: {0}", requestBody);
  131. forwardMail = JsonSerializer.Deserialize<ForwardMailDto>(requestBody);
  132. }
  133. catch (Exception ex)
  134. {
  135. _logger.LogInformation(ex.ToString());
  136. return new StatusCodeResult(500);
  137. }
  138. string appKey = forwardMail.AppKey;
  139. var userId = Helpers.HashAndShortenText(forwardMail.SenderEmail.ToLower());
  140. var fr = await CDPDB.GetFile(appKey, forwardMail.MailId, userId);
  141. if ((!fr.Policy.CheckAccess("Manage")) && (!fr.Policy.CheckAccess("Owner")))
  142. {
  143. return new UnauthorizedObjectResult("User doesn't have rights to forward this email");
  144. }
  145. RevisionEntry entry = await FileRevision.GetFileRevisionInternal(forwardMail.AppKey, forwardMail.MailId);
  146. if (entry.CompoundDocument == null)
  147. {
  148. return new BadRequestObjectResult("IncorrectMailId");
  149. }
  150. MailMetaRecord mailRecord = JsonSerializer.Deserialize<MailMetaRecord>(entry.CompoundDocument.Contents);
  151. mailRecord.AttachmentDetails.ForEach(async (att) =>
  152. {
  153. forwardMail.ReceiverEmails.ForEach(async (receiver) =>
  154. {
  155. await AddMailForwardPolicy(appKey, att.FileId, att.FileName, forwardMail.SenderEmail, receiver, "Read");
  156. });
  157. });
  158. forwardMail.ReceiverEmails.ForEach(async (receiver) =>
  159. {
  160. await AddMailForwardPolicy(appKey, mailRecord.BodyId, string.Concat(mailRecord.BodyId, "-", "MailBody"), forwardMail.SenderEmail, receiver, "Read");
  161. });
  162. return new OkObjectResult("Done");
  163. }
  164. internal async Task AddMailForwardPolicy(string appKey, string fileId, string fileName, string sender, string receiver, string policy = "Read")
  165. {
  166. AddFileUserDto addFileUserDto = new AddFileUserDto
  167. {
  168. AppKey = appKey,
  169. FileId = fileId,
  170. FileName = fileName,
  171. Email = sender,
  172. EmailToAdd = receiver,
  173. Policy = policy
  174. };
  175. await CDPLite.AddFileUserInternal(addFileUserDto);
  176. }
  177. [Function("AddMailCompundRevision")]
  178. public async Task<string> AddMailCompundRevision([ActivityTrigger] MailRecord record, FunctionContext context)
  179. {
  180. return await AddMailCompoundRevisionInternal(record);
  181. }
  182. internal async Task<string> AddMailCompoundRevisionInternal(MailRecord record)
  183. {
  184. RevisionDocument doc = new RevisionDocument
  185. {
  186. RevisionId = Guid.NewGuid().ToString(),
  187. RevisionDate = DateTime.UtcNow,
  188. EditedBy = record.SenderEmail,
  189. Comments = "MailBody"
  190. };
  191. MailMetaRecord mailMeta = new MailMetaRecord
  192. {
  193. AppKey = record.AppKey,
  194. SenderEmail = record.SenderEmail,
  195. BodyContent = record.BodyContent,
  196. BodyId = record.MailId,
  197. AttachmentDetails = record.AttachmentDetails
  198. };
  199. CompoundDocument compound = new CompoundDocument
  200. {
  201. Id = Guid.NewGuid().ToString(),
  202. AppKey = record.AppKey,
  203. Contents = JsonSerializer.Serialize(mailMeta),
  204. DocumentType = "MailRecord"
  205. };
  206. string revisionId = await FileRevision.AddMailBodyRevision(doc, compound, record.AppKey, record.MailId);
  207. return revisionId;
  208. }
  209. [Function("AddProtectionAudits")]
  210. public async Task<string> AddProtectionAudits([ActivityTrigger] MailRecord record, FunctionContext functionContext)
  211. {
  212. var res = await TriggerProtectionAuditsJob(record);
  213. return res;
  214. }
  215. internal async Task<string> TriggerProtectionAuditsJob(MailRecord record)
  216. {
  217. try
  218. {
  219. string userId = Helpers.HashAndShortenText(record.SenderEmail);
  220. string message = string.Format($"{record.SenderEmail} protected an email body with id: {record.MailId}.");
  221. List<AuditEventMetadata> auditEvents = new List<AuditEventMetadata>();
  222. AuditEventMetadata auditEvent = new AuditEventMetadata { Action = "Addded", FileId = record.MailId, FileName = "Outlook-Mail-Body", Message = message, UserId = userId };
  223. auditEvents.Add(auditEvent);
  224. record.AttachmentDetails.ForEach((att) =>
  225. {
  226. message = string.Format($"{record.SenderEmail} protected the email attachment {att.FileName} with id: {att.FileId}.");
  227. AuditEventMetadata attachmentEvent = new AuditEventMetadata { Action = "Added", FileId = att.FileId, FileName = att.FileName, Message = message, UserId = userId };
  228. auditEvents.Add(attachmentEvent);
  229. });
  230. Job job = await CDPLite.AddAuditsBatchedEvent(auditEvents, record.AppKey);
  231. await PublishJob(job);
  232. _logger.LogInformation(JsonSerializer.Serialize(job));
  233. return "Done";
  234. }
  235. catch (Exception ex)
  236. {
  237. _logger.LogError(ex.ToString());
  238. throw ex;
  239. }
  240. }
  241. internal async Task<Job> TriggerMailProcessor(MailRecord mailRecord)
  242. {
  243. string Connection = Constants.StorageConnString;
  244. var blobSvc = new BlobServiceClient(Connection);
  245. var blobClient = blobSvc.GetBlobContainerClient(mailRecord.AppKey.ToLower());
  246. if (!blobClient.Exists())
  247. {
  248. await blobClient.CreateAsync();
  249. }
  250. byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(JsonSerializer.Serialize(mailRecord));
  251. string jobId = Guid.NewGuid().ToString();
  252. var blob = blobClient.GetBlobClient(jobId);
  253. Stream fs = new MemoryStream(fileBytes);
  254. try
  255. {
  256. await blob.UploadAsync(fs);
  257. }
  258. catch (Exception ex)
  259. {
  260. _logger.LogError(ex.ToString());
  261. throw ex;
  262. }
  263. Job job = new Job { AppKey = mailRecord.AppKey, EventType = JobType.MailMetaProcessing, Id = jobId };
  264. await PublishJob(job);
  265. return job;
  266. }
  267. }
  268. public class MailMetaRecord
  269. {
  270. public required string AppKey { get; set; }
  271. public required string SenderEmail { get; set; }
  272. public required string BodyId { get; set; }
  273. public required string BodyContent { get; set; } //encrypted body
  274. public required List<AttachmentDetails> AttachmentDetails { get; set; }
  275. }
  276. public class ForwardMailDto
  277. {
  278. public string SenderEmail { get; set; }
  279. public List<string> ReceiverEmails { get; set; }
  280. public string MailId { get; set; }
  281. public string AppKey { get; set; }
  282. }
  283. }