// Handles calls to list user accounts // GET /v1/users func (s *Service) listUsersHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated user from the request context authenticatedUser, err := GetAuthenticatedUser(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Check permissions if err = checkListUsersPermissions(authenticatedUser); err != nil { response.Error(w, err.Error(), http.StatusForbidden) return } // Get pagination params page, limit, sorts, err := pagination.GetParams(r, []string{"id"}) if err != nil { response.Error(w, err.Error(), http.StatusBadRequest) return } // Count total number of results count, err := s.PaginatedUsersCount() if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Get pagination links first, last, previous, next, err := pagination.GetLinks(r.URL, count, page, limit) if err != nil { response.Error(w, err.Error(), http.StatusBadRequest) return } // Get paginated results users, err := s.FindPaginatedUsers( pagination.GetOffsetForPage(count, page, limit), limit, sorts, ) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Create response self := util.GetCurrentURL(r) listUsersResponse, err := NewListUsersResponse( count, page, self, first, last, previous, next, users, ) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Write JSON response response.WriteJSON(w, listUsersResponse, http.StatusOK) }
// Handles requests to complete an invitation of a user by setting password // POST /v1/invitations/{reference:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}} func (s *Service) confirmInvitationHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated account from the request context _, err := GetAuthenticatedAccount(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Request body cannot be nil if r.Body == nil { response.Error(w, "Request body cannot be nil", http.StatusBadRequest) return } // Read the request body payload, err := ioutil.ReadAll(r.Body) if err != nil { response.Error(w, "Error reading request body", http.StatusBadRequest) return } // Unmarshal the request body into the request prototype confirmInvitationRequest := new(ConfirmInvitationRequest) if err = json.Unmarshal(payload, confirmInvitationRequest); err != nil { logger.Errorf("Failed to unmarshal confirm invitation request: %s", payload) response.Error(w, err.Error(), http.StatusBadRequest) return } // Get the reference from request URI vars := mux.Vars(r) reference := vars["reference"] // Fetch the invitation we want to work with (by reference from email link) invitation, err := s.FindInvitationByReference(reference) if err != nil { response.Error(w, err.Error(), http.StatusNotFound) return } // Confirm the invitation if err = s.ConfirmInvitation( invitation, confirmInvitationRequest.Password, ); err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Create invitation response invitationResponse, err := NewInvitationResponse(invitation) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Write the response response.WriteJSON(w, invitationResponse, 200) }
// Handles requests to reset a password // POST /v1/password-resets func (s *Service) createPasswordResetHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated client from the request context _, err := GetAuthenticatedAccount(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Request body cannot be nil if r.Body == nil { response.Error(w, "Request body cannot be nil", http.StatusBadRequest) return } // Read the request body payload, err := ioutil.ReadAll(r.Body) if err != nil { response.Error(w, "Error reading request body", http.StatusBadRequest) return } // Unmarshal the request body into the request prototype passwordResetRequest := new(PasswordResetRequest) if err = json.Unmarshal(payload, passwordResetRequest); err != nil { logger.Errorf("Failed to unmarshal password reset request: %s", payload) response.Error(w, err.Error(), http.StatusBadRequest) return } // Fetch the user who wants to reset his/her password based on the email user, err := s.FindUserByEmail(passwordResetRequest.Email) if err != nil { response.Error(w, err.Error(), http.StatusNotFound) return } // Create a new password reset passwordReset, err := s.createPasswordReset(user) if err != nil { logger.Errorf("Create password reset error: %s", err) response.Error(w, err.Error(), http.StatusInternalServerError) return } // Create password reset response passwordResetResponse, err := NewPasswordResetResponse(passwordReset) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Write the response response.WriteJSON(w, passwordResetResponse, 201) }
// Handles health check requests (GET /v1/health) func (s *Service) healthcheck(w http.ResponseWriter, r *http.Request) { rows, err := s.db.Raw("SELECT 1=1").Rows() defer rows.Close() var healthy bool if err == nil { healthy = true } response.WriteJSON(w, map[string]interface{}{ "healthy": healthy, }, 200) }
// Handles requests to create a new user // POST /v1/users func (s *Service) createUserHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated client from the request context authenticatedAccount, err := GetAuthenticatedAccount(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Request body cannot be nil if r.Body == nil { response.Error(w, "Request body cannot be nil", http.StatusBadRequest) return } // Read the request body payload, err := ioutil.ReadAll(r.Body) if err != nil { response.Error(w, "Error reading request body", http.StatusBadRequest) return } // Unmarshal the request body into the request prototype userRequest := new(UserRequest) if err = json.Unmarshal(payload, userRequest); err != nil { logger.Errorf("Failed to unmarshal user request: %s", payload) response.Error(w, err.Error(), http.StatusBadRequest) return } // Create a new user account user, err := s.CreateUser(authenticatedAccount, userRequest) if err != nil { logger.Errorf("Create user error: %s", err) response.Error(w, err.Error(), getErrStatusCode(err)) return } // Create response userResponse, err := NewUserResponse(user) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Set Location header to the newly created resource w.Header().Set("Location", fmt.Sprintf("/v1/users/%d", user.ID)) // Write JSON response response.WriteJSON(w, userResponse, http.StatusCreated) }
// Handles requests to invite a new user // POST /v1/invitations func (s *Service) inviteUserHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated user from the request context authenticatedUser, err := GetAuthenticatedUser(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Request body cannot be nil if r.Body == nil { response.Error(w, "Request body cannot be nil", http.StatusBadRequest) return } // Read the request body payload, err := ioutil.ReadAll(r.Body) if err != nil { response.Error(w, "Error reading request body", http.StatusBadRequest) return } // Unmarshal the request body into the request prototype invitationRequest := new(InvitationRequest) if err = json.Unmarshal(payload, invitationRequest); err != nil { logger.Errorf("Failed to unmarshal invitation request: %s", payload) response.Error(w, err.Error(), http.StatusBadRequest) return } // Create a new invited user account invitation, err := s.InviteUser(authenticatedUser, invitationRequest) if err != nil { logger.Errorf("Invite user error: %s", err) response.Error(w, err.Error(), getErrStatusCode(err)) return } // Create invitation response invitationResponse, err := NewInvitationResponse(invitation) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Write the response response.WriteJSON(w, invitationResponse, 201) }
// Handles requests to get own user data // GET /v1/me func (s *Service) getMyUserHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated user from the request context authenticatedUser, err := GetAuthenticatedUser(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Create response userResponse, err := NewUserResponse(authenticatedUser) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Write JSON response response.WriteJSON(w, userResponse, http.StatusOK) }
// introspectHandler handles OAuth 2.0 introspect request // (POST /v1/oauth/introspect) func (s *Service) introspectHandler(w http.ResponseWriter, r *http.Request) { // Client auth client, err := s.basicAuthClient(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Introspect the token resp, err := s.introspectToken(r, client) if err != nil { response.Error(w, err.Error(), getErrStatusCode(err)) return } // Write response to json response.WriteJSON(w, resp, 200) }
// tokensHandler handles all OAuth 2.0 grant types // (POST /v1/oauth/tokens) func (s *Service) tokensHandler(w http.ResponseWriter, r *http.Request) { // Parse the form so r.Form becomes available if err := r.ParseForm(); err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Map of grant types against handler functions grantTypes := map[string]func(r *http.Request, client *Client) (*AccessTokenResponse, error){ "authorization_code": s.authorizationCodeGrant, "password": s.passwordGrant, "client_credentials": s.clientCredentialsGrant, "refresh_token": s.refreshTokenGrant, } // Check the grant type grantHandler, ok := grantTypes[r.Form.Get("grant_type")] if !ok { response.Error(w, ErrInvalidGrantType.Error(), http.StatusBadRequest) return } // Client auth client, err := s.basicAuthClient(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Grant processing resp, err := grantHandler(r, client) if err != nil { response.Error(w, err.Error(), getErrStatusCode(err)) return } // Write response to json response.WriteJSON(w, resp, 200) }
// Handles requests to get a user // GET /v1/users/{id:[0-9]+} func (s *Service) getUserHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated user from the request context authenticatedUser, err := GetAuthenticatedUser(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Get the id from request URI and type assert it vars := mux.Vars(r) userID, err := strconv.Atoi(vars["id"]) if err != nil { response.Error(w, err.Error(), http.StatusBadRequest) return } // Fetch the user we want to update user, err := s.FindUserByID(uint(userID)) if err != nil { response.Error(w, err.Error(), http.StatusNotFound) return } // Check permissions if err = checkGetUserPermissions(authenticatedUser, user); err != nil { response.Error(w, err.Error(), http.StatusForbidden) return } // Create response userResponse, err := NewUserResponse(user) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Write JSON response response.WriteJSON(w, userResponse, http.StatusOK) }
// Handles requests to update a user // PUT /v1/users/{id:[0-9]+} func (s *Service) updateUserHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated user from the request context authenticatedUser, err := GetAuthenticatedUser(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Get the id from request URI and type assert it vars := mux.Vars(r) userID, err := strconv.Atoi(vars["id"]) if err != nil { response.Error(w, err.Error(), http.StatusBadRequest) return } // Fetch the user we want to update user, err := s.FindUserByID(uint(userID)) if err != nil { response.Error(w, err.Error(), http.StatusNotFound) return } // Request body cannot be nil if r.Body == nil { response.Error(w, "Request body cannot be nil", http.StatusBadRequest) return } // Read the request body payload, err := ioutil.ReadAll(r.Body) if err != nil { response.Error(w, "Error reading request body", http.StatusBadRequest) return } // Unmarshal the request body into the request prototype userRequest := new(UserRequest) if err = json.Unmarshal(payload, userRequest); err != nil { logger.Errorf("Failed to unmarshal user request: %s", payload) response.Error(w, err.Error(), http.StatusBadRequest) return } // Check permissions if err = checkUpdateUserPermissions(authenticatedUser, user, userRequest); err != nil { response.Error(w, err.Error(), http.StatusForbidden) return } // Update the user if err = s.UpdateUser(user, userRequest); err != nil { logger.Errorf("Update user error: %s", err) response.Error(w, err.Error(), getErrStatusCode(err)) return } // Create response userResponse, err := NewUserResponse(user) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Write JSON response response.WriteJSON(w, userResponse, http.StatusOK) }
// Handles requests to confirm user's email // GET /v1/confirmations/{reference:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}} func (s *Service) confirmEmailHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated account from the request context authenticatedAccount, err := GetAuthenticatedAccount(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Get the reference from request URI vars := mux.Vars(r) reference := vars["reference"] // Fetch the confirmation we want to work with (by reference from email link) confirmation, err := s.FindConfirmationByReference(reference) if err != nil { response.Error(w, err.Error(), http.StatusNotFound) return } // Confirm the user if err = s.ConfirmUser(confirmation); err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Create confirmation response confirmationResponse, err := NewConfirmationResponse(confirmation) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Was autologin flag passed in the query string autoLogin, _ := strconv.ParseBool(r.URL.Query().Get("autologin")) // If autologin == true, login the user and embed access token in the response object if autoLogin { // Login the user accessToken, refreshToken, err := s.GetOauthService().Login( authenticatedAccount.OauthClient, confirmation.User.OauthUser, "read_write", ) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Create access token response accessTokenResponse, err := oauth.NewAccessTokenResponse( accessToken, refreshToken, s.cnf.Oauth.AccessTokenLifetime, tokentypes.Bearer, ) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Set embedded access token confirmationResponse.SetEmbedded( "access-token", jsonhal.Embedded(accessTokenResponse), ) } // Write the response response.WriteJSON(w, confirmationResponse, 200) }
// Handlers requests to login with Facebook access token // (POST /v1/facebook/login) func (s *Service) loginHandler(w http.ResponseWriter, r *http.Request) { // Get the authenticated account from the request context authenticatedAccount, err := accounts.GetAuthenticatedAccount(r) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Parse the form so r.Form becomes available if err = r.ParseForm(); err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Get the scope string scope, err := s.GetAccountsService().GetOauthService().GetScope(r.Form.Get("scope")) if err != nil { response.Error(w, err.Error(), http.StatusBadRequest) return } // Fetch the user data from facebook resp, err := s.adapter.GetMe(r.Form.Get("access_token")) if err != nil { response.UnauthorizedError(w, err.Error()) return } // Initialise variables var ( profile UserProfile user *accounts.User ) // Decode the response to struct if err = resp.Decode(&profile); err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } if profile.Email == nil { // There is an edge case where Facebook does not return a valid email // User could have registered with a phone number or have an unconfirmed // email address. In such rare case, default to {facebook_id}@facebook.com edgeCaseEmail := fmt.Sprintf("*****@*****.**", profile.ID) profile.Email = &edgeCaseEmail } // Build user request object userRequest := &accounts.UserRequest{ Email: *profile.Email, FirstName: profile.FirstName, LastName: profile.LastName, Picture: profile.GetPictureURL(), } // Get or create a new user based on facebook ID and other details user, err = s.GetAccountsService().GetOrCreateFacebookUser( authenticatedAccount, profile.ID, userRequest, ) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Check that the same account is being used if authenticatedAccount.ID != user.Account.ID { response.UnauthorizedError(w, ErrAccountMismatch.Error()) return } // Log in the user accessToken, refreshToken, err := s.GetAccountsService().GetOauthService().Login( user.Account.OauthClient, user.OauthUser, scope, ) if err != nil { response.Error(w, err.Error(), http.StatusInternalServerError) return } // Write the JSON access token to the response accessTokenRespone := &oauth.AccessTokenResponse{ UserID: user.ID, AccessToken: accessToken.Token, ExpiresIn: s.cnf.Oauth.AccessTokenLifetime, TokenType: tokentypes.Bearer, Scope: accessToken.Scope, RefreshToken: refreshToken.Token, } response.WriteJSON(w, accessTokenRespone, 200) }