Example #1
0
        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
            });
        }
Example #2
0
        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
            });
        }
Example #4
0
        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
            });
        }