public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "users/{id}")] [RequestBodyType(typeof(User), "User")] HttpRequest req, string id, ILogger log, ExecutionContext context) { log.LogInformation($"{context?.FunctionName} processed a HTTP request."); // TelemetryClient.Context.Operation.Id = context?.InvocationId.ToString(); // No longer needed? string requestBody = new StreamReader(req.Body).ReadToEnd(); var user = JsonConvert.DeserializeObject <User>(requestBody); if (user is null) { return(new BadRequestObjectResult("No User definition specified in body.")); } user.Id = user.Id ?? id; if (user.Id != id) { return(new BadRequestObjectResult("User Id provided in request JSON does not match the Id provided in the route.")); } try { var query = GremlinHelper.UpdateVertexQuery(id, user, log); var response = new GraphResponse(await GremlinClient.SubmitAsync <dynamic>(query)); GremlinHelper.ThrowIfResponseInvalid(response); GremlinHelper.GraphTelemetryEvent(TelemetryClient, "GraphVertexUpdate", response, "vertex", "user"); if (response.Entities == null || response.Entities.Count() < 1) { return(new NotFoundResult()); } user = response.GetEntityAsType <User>(); } catch (ResponseException ex) { GremlinHelper.HandleGraphResponseException(ex, log, context, TelemetryClient); } catch (Exception ex) { GremlinHelper.HandleGeneralException(ex, log, context, TelemetryClient); } return(user != null ? new OkObjectResult(user) { StatusCode = 200 } : new OkObjectResult("Failed to update user.") { StatusCode = 500 }); }
public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "users/{id}")] HttpRequest req, string id, ILogger log, [FunctionToken] FunctionTokenResult token, ExecutionContext context) { log.LogInformation($"{context?.FunctionName} processed a HTTP request."); // IMPORTANT: Authorization! var authResult = AuthValidation(req, token, id); if (!authResult.IsAuthorized) { return(authResult.ActionResult); } User user = null; try { var query = GremlinHelper.GetVertexQuery <User>(id); var response = new GraphResponse(await GremlinClient.SubmitAsync <dynamic>(query)); GremlinHelper.ThrowIfResponseInvalid(response); if (response.Entities == null || response.Entities.Count() < 1) { return(new NotFoundResult()); } GremlinHelper.GraphTelemetryEvent(TelemetryClient, "GraphVertexRetrieve", response, "vertex", "user"); user = response.GetEntityAsType <User>(); } catch (ResponseException ex) { GremlinHelper.HandleGraphResponseException(ex, log, context, TelemetryClient); } catch (Exception ex) { GremlinHelper.HandleGeneralException(ex, log, context, TelemetryClient); } return(user != null ? (ActionResult) new OkObjectResult(user) : new NotFoundResult()); }
public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = "users/password/{id}")] [RequestBodyType(typeof(string), "Password")] HttpRequest req, string id, ILogger log, ExecutionContext context) { log.LogInformation($"{context?.FunctionName} processed a HTTP request."); // TelemetryClient.Context.Operation.Id = context?.InvocationId.ToString(); // No longer needed? string password = new StreamReader(req.Body).ReadToEnd(); if (string.IsNullOrWhiteSpace(password)) { return(new BadRequestObjectResult("No password value supplied in body.")); } try { // FIND USER IN GRAPH var query = GremlinHelper.GetVertexQuery <User>(id); var response = new GraphResponse(await GremlinClient.SubmitAsync <dynamic>(query)); GremlinHelper.ThrowIfResponseInvalid(response); if (response.Entities == null || response.Entities.Count() < 1) { return(new NotFoundResult()); } GremlinHelper.GraphTelemetryEvent(TelemetryClient, "GraphVertexRetrieve_ChangePassword", response, "vertex", "user"); var user = response.GetEntityAsType <User>(); // CHANGE PASSWORD ON B2C try { var passwordProfile = new B2CPasswordProfile() { Password = password, ForceChangePasswordNextLogin = false }; dynamic wrapper = new ExpandoObject() { }; wrapper.passwordProfile = passwordProfile; var b2cResponse = await B2CGraphClient.UpdateUser(user.IdentityId, JsonConvert.SerializeObject(wrapper), log); } catch (B2CPasswordComplexityException) { return(new BadRequestObjectResult("The specified password does not comply with password complexity requirements. Please provide a different password.")); } catch (Exception ex) { TelemetryClient.TrackException(ex, new Dictionary <string, string>() { { "userId", id } }, null); log.LogError($"{context?.FunctionName} Error: {ex.Message}"); return(new OkObjectResult("Error with identity provider update.") { StatusCode = 500 }); } if (user.CustomProperties.ContainsKey("PasswordAutoGenerated")) { query = GremlinHelper.UpdateVertexQuery(id, user, log); response = new GraphResponse(await GremlinClient.SubmitAsync <dynamic>(query)); GremlinHelper.ThrowIfResponseInvalid(response); GremlinHelper.GraphTelemetryEvent(TelemetryClient, "GraphVertexUpdate_PasswordChange", response, "vertex", "user"); } } catch (ResponseException ex) { GremlinHelper.HandleGraphResponseException(ex, log, context, TelemetryClient); } catch (Exception ex) { GremlinHelper.HandleGeneralException(ex, log, context, TelemetryClient); } return(new OkObjectResult(null) { StatusCode = 204 }); }
public async Task <IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "users/{id}")] HttpRequest req, string id, ILogger log, ExecutionContext context) { log.LogInformation($"{context?.FunctionName} processed a HTTP request."); // TelemetryClient.Context.Operation.Id = context?.InvocationId.ToString(); // No longer needed? try { // FIND USER IN GRAPH var query = GremlinHelper.GetVertexQuery(id); var response = new GraphResponse(await GremlinClient.SubmitAsync <dynamic>(query)); GremlinHelper.ThrowIfResponseInvalid(response); if (response.Entities == null || response.Entities.Count() < 1) { return(new NotFoundResult()); } GremlinHelper.GraphTelemetryEvent(TelemetryClient, "GraphVertexRetrieve_DeleteUser", response, "vertex", "user"); var user = response.GetEntityAsType <User>(); // DELETE USER FROM B2C try { var b2cCreatedUserResult = await B2CGraphClient.DeleteUser(user.IdentityId, log); } catch (Exception ex) { var ignoreTask = Task.Run(() => { log.LogError($"{context?.FunctionName} Delete B2C User Error: {ex.Message}"); TelemetryClient.TrackException(ex, new Dictionary <string, string>() { { "ExceptionType", "B2CUserDeleteError" }, { "UserName", user?.UserName }, { "EmailAddress", user?.PrimaryEmailAddress } }, null); }); } // DELETE USER FROM GRAPH query = GremlinHelper.DeleteVertexQuery(user.Id); response = new GraphResponse(await GremlinClient.SubmitAsync <dynamic>(query)); GremlinHelper.ThrowIfResponseInvalid(response); GremlinHelper.GraphTelemetryEvent(TelemetryClient, "GraphVertexDelete", response, "vertex", "user"); } catch (ResponseException ex) { GremlinHelper.HandleGraphResponseException(ex, log, context, TelemetryClient); } catch (Exception ex) { GremlinHelper.HandleGeneralException(ex, log, context, TelemetryClient); } return(new StatusCodeResult(204)); }
public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "users")] [RequestBodyType(typeof(User), "User")] HttpRequest req, ILogger log, ExecutionContext context) { log.LogInformation($"{context?.FunctionName} processed a HTTP request."); // TelemetryClient.Context.Operation.Id = context?.InvocationId.ToString(); // No longer needed? string requestBody = new StreamReader(req.Body).ReadToEnd(); var user = JsonConvert.DeserializeObject <User>(requestBody); if (user is null) { return(new BadRequestObjectResult("No user definition specified in body.")); } if (string.IsNullOrWhiteSpace(user.Id)) { user.Id = Guid.NewGuid().ToString(); } if (string.IsNullOrWhiteSpace(user.PartitionKey)) { user.PartitionKey = user.Id; } if (string.IsNullOrWhiteSpace(user.DocumentType)) { user.DocumentType = "user"; } var missingProperties = new List <string>(); // Check required user properties are present otherwise return 400. if (string.IsNullOrWhiteSpace(user.PrimaryEmailAddress)) { missingProperties.Add("primaryEmailAddress"); } if (string.IsNullOrWhiteSpace(user.FirstName)) { missingProperties.Add("firstName"); } if (string.IsNullOrWhiteSpace(user.LastName)) { missingProperties.Add("lastName"); } if (missingProperties.Count > 0) { return(new BadRequestObjectResult($"User definition is missing required properties... {((missingProperties.Count > 1) ? string.Join(", ", missingProperties) : missingProperties[0])}.")); } // CHECK USER NOT IN GRAPH try { var filterProperties = new { userName = user.UserName, primaryEmailAddress = user.PrimaryEmailAddress }; var query = GremlinHelper.GetVertexQuery(filterProperties, true); var response = new GraphResponse(await GremlinClient.SubmitAsync <dynamic>(query)); GremlinHelper.ThrowIfResponseInvalid(response); if (response.Entities != null && response.Entities.Count() > 0) { return(new ConflictObjectResult("User already exists (graph).")); } } catch (ResponseException ex) { GremlinHelper.HandleGraphResponseException(ex, log, context, TelemetryClient); } catch (Exception ex) { GremlinHelper.HandleGeneralException(ex, log, context, TelemetryClient); } // CREATE USER ON B2C if (string.IsNullOrWhiteSpace(user.Password)) { user.Password = Guid.NewGuid().ToString(); user.CustomProperties.Add("PasswordAutoGenerated", "true"); } var b2cUser = new B2CUser() { DisplayName = user.Id, // We do not store user profile data on B2C identity - simply use the customer user id for display name. UserAttribute_CustomerUserId = user.Id, SignInNames = new List <B2CSignInName>() { new B2CSignInName() { Type = "emailAddress", Value = user.PrimaryEmailAddress } }, PasswordProfile = new B2CPasswordProfile() { Password = user.Password, ForceChangePasswordNextLogin = false } }; // Remove password value from user object. user.Password = null; try { var b2cCreatedUserResult = await B2CGraphClient.CreateUser(b2cUser, log); var b2cCreatedUser = JsonConvert.DeserializeObject <dynamic>(b2cCreatedUserResult); user.IdentityId = b2cCreatedUser?.objectId; user.UserStatus = UserStatus.Active; } catch (B2CUserExistsException) { // User already exists on B2C return(new ConflictObjectResult("User already exists (identity).")); } catch (B2CPasswordComplexityException) { return(new BadRequestObjectResult("The specified password does not comply with password complexity requirements. Please provide a different password.")); } catch (Exception ex) { var ignoreTask = Task.Run(() => { log.LogError($"{context?.FunctionName} Create B2C User Error: {ex.Message}"); TelemetryClient.TrackException(ex, new Dictionary <string, string>() { { "ExceptionType", "B2CUserCreateError" }, { "UserName", user?.UserName }, { "EmailAddress", user?.PrimaryEmailAddress } }, null); }); return(new OkObjectResult("Failed to create user (identity).") { StatusCode = 500 }); } // CREATE USER IN GRAPH try { var query = GremlinHelper.CreateVertexQuery(user, log); var response = new GraphResponse(await GremlinClient.SubmitAsync <dynamic>(query)); GremlinHelper.ThrowIfResponseInvalid(response); GremlinHelper.GraphTelemetryEvent(TelemetryClient, "GraphVertexCreate", response, "vertex", "user"); user = response.GetEntityAsType <User>(); } catch (ResponseException ex) { GremlinHelper.HandleGraphResponseException(ex, log, context, TelemetryClient); } catch (Exception ex) { GremlinHelper.HandleGeneralException(ex, log, context, TelemetryClient); } return(user != null ? new OkObjectResult(user) { StatusCode = 201 } : new OkObjectResult("Failed to create user.") { StatusCode = 500 }); }