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.

344 lines
16 KiB

  1. using Microsoft.AspNetCore.Mvc;
  2. using Microsoft.Azure.Cosmos;
  3. using Microsoft.Azure.Functions.Worker.Http;
  4. using Microsoft.Azure.Functions.Worker;
  5. using Microsoft.Extensions.Logging;
  6. using Newtonsoft.Json;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. namespace CDP
  13. {
  14. public class CDPLite
  15. {
  16. private readonly ILogger<CDPLite> _logger;
  17. public static string FileAuditContainer = "FileAudits";
  18. public static string UserAuditContainer = "UserAudits";
  19. public static string GroupAuditContainer = "GroupAudits";
  20. public static string TenantAuditContainer = "TenantAudits";
  21. public CDPLite(ILogger<CDPLite> log)
  22. {
  23. _logger = log;
  24. }
  25. /*internal async Task<IActionResult> AddFilesBatchedInternal(AddFileBatchedDto dto)
  26. {
  27. string userId = Helpers.HashAndShortenText(dto.Email.ToLower());
  28. string jobId = Guid.NewGuid().ToString();
  29. List<KeyVaultEvent> vaultEvents = new List<KeyVaultEvent>();
  30. //List<AuditEventMetadata> auditEvents = new List<AuditEventMetadata>();
  31. List<FileRecord> fileRecords = new List<FileRecord>();
  32. for (int i = 0; i < dto.Count; i++)
  33. {
  34. string fileId = Guid.NewGuid().ToString();
  35. string fileName = dto.FileNames[i];
  36. string aesKey = Helpers.GenerateAES256KeyToBase64();
  37. //string message = string.Format($"{dto.Email} protected {fileName} file having {fileId} id.");
  38. KeyVaultEvent vaultEvent = new KeyVaultEvent { AESKey = aesKey, FileId = fileId };
  39. //AuditEventMetadata auditEvent = new AuditEventMetadata { Action = "Addded", FileId = fileId, FileName = dto.FileNames[i], Message = message, UserId = userId };
  40. //auditEvents.Add(auditEvent);
  41. vaultEvents.Add(vaultEvent);
  42. AccessPolicy ac = new AccessPolicy()
  43. {
  44. Access = "Owner",
  45. Key = aesKey,
  46. Email = dto.Email
  47. };
  48. FileRecord fr = await CDPDB.UpsertFile(dto.AppKey, fileId, fileName, userId, "", ac);
  49. fileRecords.Add(fr);
  50. }
  51. List<Job> jobs = new List<Job>();
  52. Job vaultJob = await AddKeyVaultBatchedEvent(vaultEvents, dto.AppKey);
  53. //Job auditJob = await AddAuditsBatchedEvent(auditEvents, dto.AppKey);
  54. //jobs.Add(auditJob);
  55. jobs.Add(vaultJob);
  56. await MetaProcessor.PublishBatchJobs(jobs);
  57. return new OkObjectResult(fileRecords);
  58. }*/
  59. internal async Task<IActionResult> AddFileInternal(AddFileDto dto, bool useKeyVaultEvent = false)
  60. {
  61. string fileId = Guid.NewGuid().ToString();
  62. string userId = Helpers.HashAndShortenText(dto.Email.ToLower());
  63. string fileName = dto.FileName;
  64. string aesKey = Helpers.GenerateAES256KeyToBase64();
  65. // the KeyVault is slow for some reason and while it's dangerous to return a key
  66. // before we're sure it got added to the database...I'm going to do it anyway.
  67. if (useKeyVaultEvent)
  68. {
  69. await AddKeyVaultEvent(fileId, aesKey, dto.AppKey);
  70. }
  71. else
  72. {
  73. await Task.Run(async () =>
  74. {
  75. try
  76. {
  77. KeyVaultService kvs = new KeyVaultService(Constants.KeyVaultURI);
  78. await kvs.SetSecretAsync(fileId, aesKey);
  79. }
  80. catch (Exception e)
  81. {
  82. Console.WriteLine(e);
  83. }
  84. });
  85. }
  86. // when you add a file, you have rights to manage access to it.
  87. AccessPolicy ac = new AccessPolicy()
  88. {
  89. Access = "Owner",
  90. Key = aesKey,
  91. Email = dto.Email
  92. };
  93. // since we're generating a new file id, a new entry will always be created.
  94. FileRecord fr = await CDPDB.UpsertFile(dto.AppKey, fileId, fileName, userId, "", ac);
  95. string message = string.Format($"{dto.Email} protected {fileName} file having {fileId} id.");
  96. string action = "Added";
  97. await AuditFunctions.AddAuditsEvent(dto.AppKey, fileId, fileName, userId, dto.GroupId, action, message); //commenting for speed test
  98. return new OkObjectResult(fr);
  99. }
  100. internal static async Task<IActionResult> AddFileUserInternal(AddFileUserDto dto)
  101. {
  102. // check to see if the email has the power to add a user
  103. string userId = Helpers.HashAndShortenText(dto.Email.ToLower());
  104. FileRecord fr = await CDPDB.GetFile(dto.AppKey, dto.FileId, userId);
  105. if (fr == null)
  106. {
  107. 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");
  108. Console.WriteLine(message);
  109. string action = "Policy change failed";
  110. await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, dto.FileName, userId, "", action, message);
  111. return new BadRequestObjectResult(new { error = true, message = "File not found for user " + dto.Email });
  112. }
  113. if ((!fr.Policy.CheckAccess("Manage")) && (!fr.Policy.CheckAccess("Owner")))
  114. {
  115. 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");
  116. Console.WriteLine(message);
  117. string action = "Policy change failed";
  118. await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, dto.FileName, userId, "", action, message);
  119. return new BadRequestObjectResult(new { error = true, message = $"{dto.Email} doesn't have the rights to add a user." });
  120. }
  121. string fileId = dto.FileId;
  122. string fileName = dto.FileName;
  123. string userIdToAdd = "";
  124. if (dto.EmailToAdd != "")
  125. {
  126. userIdToAdd = Helpers.HashAndShortenText(dto.EmailToAdd.ToLower());
  127. }
  128. else if (dto.Group != null)
  129. {
  130. userIdToAdd = dto.GroupId;
  131. }
  132. else if (dto.Group != null)
  133. {
  134. userIdToAdd = dto.GroupId;
  135. }
  136. AccessPolicy ac = new AccessPolicy()
  137. {
  138. Access = dto.Policy,
  139. Email = dto.EmailToAdd.ToLower(),
  140. Group = dto.Group,
  141. GroupId = dto.GroupId,
  142. Key = ""
  143. };
  144. fr = await CDPDB.UpsertFile(dto.AppKey, fileId, fileName, userIdToAdd, "", ac);
  145. if (dto.EmailToAdd != "")
  146. {
  147. string message = string.Format($"{dto.Email} added/changed the access policy for User : {dto.EmailToAdd} to {dto.Policy} on {fileName} file having {fileId} id");
  148. string action = "Policy change";
  149. await AuditFunctions.AddAuditsEvent(dto.AppKey, fileId, fileName, userId, "", action, message);
  150. }
  151. if (dto.Group != null)
  152. {
  153. string message = string.Format($"{dto.Email} added/changed the access policy for Group : {dto.Group} to {dto.Policy} on {fileName} file having {fileId} id");
  154. string action = "Policy change";
  155. await AuditFunctions.AddAuditsEvent(dto.AppKey, fileId, fileName, "", dto.Group.id, action, message);
  156. }
  157. return new OkObjectResult(fr);
  158. }
  159. #region CDP File Functions
  160. [Function("AddFile")]
  161. public async Task<IActionResult> AddFile([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
  162. {
  163. _logger.LogInformation("AddFile invoked");
  164. // Convert the JSON payload to a string
  165. string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
  166. AddFileDto dto = JsonConvert.DeserializeObject<AddFileDto>(requestBody);
  167. if (dto == null)
  168. return new BadRequestObjectResult(new { error = true, message = "Parse error." });
  169. return await AddFileInternal(dto, true);
  170. }
  171. [Function("AddFileUser")]
  172. public async Task<IActionResult> AddFileUser([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
  173. {
  174. _logger.LogInformation("AddFileUser invoked");
  175. // Convert the JSON payload to a string
  176. string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
  177. AddFileUserDto dto = JsonConvert.DeserializeObject<AddFileUserDto>(requestBody);
  178. if (dto == null)
  179. return new BadRequestObjectResult(new { error = true, message = "Parse error." });
  180. return await AddFileUserInternal(dto);
  181. }
  182. [Function("GetFileForUser")]
  183. public async Task<IActionResult> GetFileForUser([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
  184. {
  185. _logger.LogInformation("GetFile invoked");
  186. // Convert the JSON payload to a string
  187. string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
  188. AddFileUserDto dto = JsonConvert.DeserializeObject<AddFileUserDto>(requestBody);
  189. if (dto == null)
  190. return new BadRequestObjectResult(new { error = true, message = "Parse error." });
  191. // check to see if the email has the power to add a user
  192. // string a = Helpers.HashAndShortenText(dto.Email.ToLower());
  193. // string b = Helpers.HashToHex(dto.Email.ToLower());
  194. // string c = Helpers.ConvertShortHashToHex(a);
  195. string userId = Helpers.HashAndShortenText(dto.Email.ToLower());
  196. // userId = "user-" + Helpers.HashToHex(dto.Email.ToLower());
  197. FileRecord fr = await CDPDB.GetFile(dto.AppKey, dto.FileId, userId);
  198. if (fr == null)
  199. {
  200. string AuditMessage = string.Format($"File not found for user {dto.Email} having fileId {dto.FileId}");
  201. string AuditAction = "Decrypt failed";
  202. await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, dto.FileName, userId, "", AuditAction, AuditMessage);
  203. return new BadRequestObjectResult(new { error = true, message = "File not found for user " + dto.Email });
  204. }
  205. if (fr.Policy.CheckAccess("None"))
  206. {
  207. string AuditMessage = string.Format($"{dto.Email} don't have the rights to decrypt {fr.FileName} file having {dto.FileId} id");
  208. string AuditAction = "Decrypt failed";
  209. await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, fr.FileName, userId, "", AuditAction, AuditMessage);
  210. return new BadRequestObjectResult(new { error = true, message = "Access is denied for user " + dto.Email });
  211. }
  212. KeyVaultService kvs = new KeyVaultService(Constants.KeyVaultURI);
  213. fr.Policy.Key = await kvs.GetSecretAsync(fr.FileId);
  214. string message = string.Format($"{dto.Email} decrypted {fr.FileName} file having {dto.FileId} id");
  215. string action = "Decrypted";
  216. await AuditFunctions.AddAuditsEvent(dto.AppKey, dto.FileId, dto.FileName, userId, "", action, message);
  217. return new OkObjectResult(fr);
  218. }
  219. [Function("GetPoliciesForFile")]
  220. public async Task<IActionResult> GetPoliciesForFile([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
  221. {
  222. _logger.LogInformation("GetFile invoked");
  223. // Convert the JSON payload to a string
  224. string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
  225. GetPoliciesForFileDto dto = JsonConvert.DeserializeObject<GetPoliciesForFileDto>(requestBody);
  226. if (dto == null)
  227. return new BadRequestObjectResult(new { error = true, message = "Parse error." });
  228. List<FileRecord> fr = await CDPDB.GetPoliciesForFile(dto.AppKey, dto.FileId);
  229. if (fr == null)
  230. {
  231. return new BadRequestObjectResult(new { error = true, message = "File not found " + dto.FileId });
  232. }
  233. KeyVaultService kvs = new KeyVaultService(Constants.KeyVaultURI);
  234. string aesKey = await kvs.GetSecretAsync(dto.FileId);
  235. foreach (var f in fr)
  236. {
  237. f.Policy.Key = aesKey;
  238. }
  239. return new OkObjectResult(fr);
  240. }
  241. [Function("DeleteRegisteredUserPolicies")]
  242. public async Task<IActionResult> DeleteRegisteredUserPolicies([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
  243. {
  244. _logger.LogInformation("Deleting Registered User invoked");
  245. // Convert the JSON payload to a string
  246. string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
  247. DeleteRegisteredUserDto deleteContactdto = JsonConvert.DeserializeObject<DeleteRegisteredUserDto>(requestBody);
  248. if (deleteContactdto == null)
  249. return new BadRequestObjectResult(new { error = true, message = "Parse error." });
  250. string userId = Helpers.HashAndShortenText(deleteContactdto.UserEmail.ToLower());
  251. await CDPDB.revokeRegisteredUserPolicies(deleteContactdto.AppKey, deleteContactdto.UserEmail.ToLower(), deleteContactdto.ContactEmail.ToLower(), deleteContactdto.AdminEmail.ToLower());
  252. return new OkObjectResult(true);
  253. }
  254. #endregion
  255. #region Background Processing Support
  256. public static async Task<Job> AddAuditsBatchedEvent(List<AuditEventMetadata> auditEvents, string appKey)
  257. {
  258. using (var mt = new MethodTimer("AddAuditsBatchedEvent"))
  259. {
  260. string jobMeta = JsonConvert.SerializeObject(auditEvents);
  261. string jobId = Guid.NewGuid().ToString();
  262. Job job = new Job { AppKey = appKey, EventType = JobType.AddAuditsBatch, Id = jobId, JobMetadata = jobMeta };
  263. return job;
  264. }
  265. }
  266. public static async Task<Job> AddKeyVaultBatchedEvent(List<KeyVaultEvent> vaultEvents, string appKey)
  267. {
  268. using (var mt = new MethodTimer("AddKeyVaultBatchedEvent"))
  269. {
  270. string jobMeta = JsonConvert.SerializeObject(vaultEvents);
  271. string jobId = Guid.NewGuid().ToString();
  272. Job job = new Job { AppKey = appKey, EventType = JobType.KeyVaultInsertionBatch, Id = jobId, JobMetadata = jobMeta };
  273. //await MetaProcessor.PublishJob(job);
  274. return job;
  275. }
  276. }
  277. public static async Task<string> AddKeyVaultEvent(string fileId, string aesKey, string appKey)
  278. {
  279. using (var mt = new MethodTimer("AddKeyVaultEvent"))
  280. {
  281. KeyVaultEvent vaultEvent = new KeyVaultEvent { AESKey = aesKey, FileId = fileId };
  282. string jobMeta = JsonConvert.SerializeObject(vaultEvent);
  283. string jobId = Guid.NewGuid().ToString();
  284. Job keyVaultJob = new Job { AppKey = appKey, EventType = JobType.KeyVaultInsertion, Id = jobId, JobMetadata = jobMeta };
  285. await MetaProcessor.PublishJob(keyVaultJob);
  286. return jobId;
  287. }
  288. }
  289. #endregion
  290. }
  291. }