Example #1
0
// 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)
}
Example #4
0
// 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)
}
Example #5
0
// 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)
}
Example #6
0
// 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)
}
Example #7
0
// 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)
}
Example #8
0
// 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)
}
Example #9
0
// 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)
}
Example #10
0
// 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)
}
Example #11
0
// 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)
}
Example #12
0
// 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)
}
Example #13
0
// 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)
}