Exemple #1
0
func getUserByIdAndWriteIfError(rw http.ResponseWriter, req *http.Request) (*auth.User, auth.UserManager, error) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")

	vars := mux.Vars(req)
	idStr := vars["id"]
	if len(idStr) == 0 {
		response.BadRequestResponse(rw, &response.JSONErr{
			Code:        ErrCodeInvalidId,
			Message:     "Missing 'id' from request",
			Description: "The request URI must be /active/{id}?code=xxxx",
		})
		return nil, nil, errors.New("rest: Missing 'id' from request")
	}

	userMngr, err := auth.Provider().OpenUserMngr(req)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{Message: err.Error()})
		return nil, nil, err
	}

	u, err := userMngr.FindUser(idStr)
	if err != nil {
		response.ErrorResponse(rw, http.StatusPreconditionFailed, &response.JSONErr{
			Code:    ErrCodeNotExistId,
			Message: "Account not exists",
		})
		return nil, userMngr, err
	}

	return u, userMngr, nil
}
Exemple #2
0
func SetConf(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")

	cfg, err := config.Provider().OpenConfigurator(req)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{
			Message:     err.Error(),
			Description: "Error when open Configurator.",
		})
		return
	}
	defer cfg.Close()

	m := make(map[string]string)
	err = json.NewDecoder(req.Body).Decode(&m)
	if err != nil {
		response.BadRequestResponse(rw, &response.JSONErr{
			Message:     err.Error(),
			Description: "Error when unmarshal json data.",
		})
		return
	}
	defer req.Body.Close()

	for k, v := range m {
		cfg.Set(k, v)
	}
}
Exemple #3
0
func GetConf(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")

	cfg, err := config.Provider().OpenConfigurator(req)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{
			Message:     err.Error(),
			Description: "Error when open Configurator.",
		})
		return
	}
	defer cfg.Close()

	result := make(map[string]string)
	ks := strings.Split(req.FormValue("key"), ",")
	if len(ks) == 0 {
		response.BadRequestResponse(rw, &response.JSONErr{
			Message:     "Must have one more key in request parameter",
			Description: "",
		})
		return
	}

	for _, k := range ks {
		v, err := cfg.Get(k)
		if err != nil {
			result[k] = ""
		} else {
			result[k] = v
		}
	}

	json.NewEncoder(rw).Encode(&result)
}
Exemple #4
0
/*
GetToken handle both POST and GET method to obtain login token.
Note that only "password" support for "grant_type" right now.

Example Request:
  GET /tokens?grant_type=password&[email protected]&password=xxxxxxxxx
Example Success Response:
  {
    "User": {...}, // auth.User object with empty Pwd, OldPwd, ConfirmCodes
    "ExpiredOn": "2009-11-10T23:00:00Z",
    "AccessToken": "afE.....MNWt-HfVYcFOs7w_ryOzvsYA==" // a secure random base64 encoded string
  }
*/
func GetToken(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")

	grantType := req.FormValue("grant_type")
	email := req.FormValue("email")
	password := req.FormValue("password")

	// TODO: more detail error message
	if len(grantType) == 0 || len(email) == 0 || len(password) == 0 {
		response.BadRequestResponse(rw, &response.JSONErr{
			Code:        ErrCodeInvalidInput,
			Message:     "Invalid input",
			Description: "grant_type, email and password need to be set.",
		})
		return
	}

	if grantType != "password" {
		response.ErrorResponse(rw, http.StatusNotImplemented, &response.JSONErr{
			Code:        ErrCodeInvalidGrantType,
			Message:     "Invlaid grant_type",
			Description: "Only support grant_type=password",
		})
		return
	}

	userMngr, err := auth.Provider().OpenUserMngr(req)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{Message: err.Error()})
		return
	}
	defer userMngr.Close()

	user, err := userMngr.ValidateUser(email, password)
	if err != nil {
		response.UnauthorizedResponse(rw, &response.JSONErr{
			Code:        response.ErrCodeNotLogged,
			Message:     err.Error(),
			Description: "Invlaid emaill or password.",
		})
		return
	}

	token, err := userMngr.Login(user.Id, OnlineThreshold)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{Message: err.Error()})
		return
	}

	// hide sensitive data
	user.Pwd = auth.Password{}
	user.OldPwd = []auth.Password{}
	user.ConfirmCodes = map[string]string{}

	inf := LoginInfo{user, time.Now().Add(OnlineThreshold), token}
	json.NewEncoder(rw).Encode(&inf)
}
Exemple #5
0
/*
UserInfoUpdate handle the reuqest for update user infomation. Its require 'manage_user'
to change other's infomation.

Example Request Body:
  PUT /profile/some-kind-of-ID/info
  {
    "FirstName": "Cao Nguyên"
    ...//other feilds of auth,UserInfo struct
  }
*/
func UserInfoUpdate(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")

	u, userMngr, err := getUserByIdAndWriteIfError(rw, req)
	if err != nil {
		return
	}
	defer userMngr.Close()

	token := strings.TrimPrefix(req.Header.Get("Authorization"), "Bearer ")
	currentUser, err := userMngr.GetUser(token)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{
			Message:     err.Error(),
			Description: "Error when loading current user.",
		})
		return
	}

	if currentUser.Id != u.Id {
		if !userMngr.Can(currentUser, "manage_user") {
			response.ForbiddenResponse(rw, &response.JSONErr{
				Code:        response.ErrNoPermission,
				Message:     "Current user doesn't have valid permission.",
				Description: "Current user need 'manage_user' permission to perform this action.",
			})
			return
		}
	}

	var inf auth.UserInfo
	err = json.NewDecoder(req.Body).Decode(&inf)
	if err != nil {
		response.BadRequestResponse(rw, &response.JSONErr{
			Code:        ErrCodeInvalidInput,
			Message:     err.Error(),
			Description: "The request body must be a valid response.UserInfo JSON object",
		})
		return
	}
	defer req.Body.Close()

	u.Info = inf
	err = userMngr.UpdateUserDetail(u)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{
			Message:     err.Error(),
			Description: "Error when saving user object to database.",
		})
		return
	}

}
Exemple #6
0
func OAuthOwnerPrivilegeWrapper(handler http.HandlerFunc, userIdField string) http.HandlerFunc {
	return func(rw http.ResponseWriter, req *http.Request) {
		userMngr, err := Provider().OpenUserMngr(req)
		if err != nil {
			response.InternalErrorResponse(rw, &response.JSONErr{Message: err.Error()})
		}
		defer userMngr.Close()

		token := strings.TrimPrefix(req.Header.Get("Authorization"), "Bearer ")
		user, err := userMngr.GetUser(token)
		if err != nil {
			if err == ErrNotLogged {
				response.ForbiddenResponse(rw, &response.JSONErr{
					Code:        response.ErrCodeNotLogged,
					Message:     err.Error(),
					Description: "User need to be logged in to perform this action.",
				})
				return
			}
			response.InternalErrorResponse(rw, &response.JSONErr{Message: err.Error()})
			return
		}

		vars := mux.Vars(req)
		idStr := vars[userIdField]
		if len(idStr) == 0 {
			response.BadRequestResponse(rw, &response.JSONErr{
				Message:     "Missing " + userIdField + " from request URL",
				Description: "OAuthOwnerPrivilegeWrapper require " + userIdField + " defiend by 'userIdField' to be exist in handle pattern.",
			})
			return
		}

		u, err := userMngr.FindUser(idStr)
		if err != nil {
			response.InternalErrorResponse(rw, &response.JSONErr{Message: err.Error()})
			return
		}

		if u.Id != user.Id {
			response.ForbiddenResponse(rw, &response.JSONErr{
				Code:        response.ErrNoPermission,
				Message:     "Current user must be the owner of profile.",
				Description: "Current user must be the owner of the profile defined by " + userIdField + " field.",
			})
			return
		}

		handler(rw, req)
	}
}
Exemple #7
0
/*
ActiveAccount handle active request by using confirm code.

Example Request:
  GET /active/some-kind-of-ID?code=secure-random-base64-string
Example Success Response:
  {
    "Message":"Account activated"
  }
*/
func ActiveAccount(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")

	code := req.FormValue("code")
	if len(code) == 0 {
		response.BadRequestResponse(rw, &response.JSONErr{
			Code:        1.8,
			Message:     "Missing 'code' from request parameter",
			Description: "The request URI must be /active/{id}?code=xxxx",
		})
		return
	}

	u, userMngr, err := getUserByIdAndWriteIfError(rw, req)
	if err != nil {
		return
	}
	defer userMngr.Close()

	if ok := u.ValidConfirmCode("activate", code, false, true); !ok {
		response.ErrorResponse(rw, http.StatusPreconditionFailed, &response.JSONErr{
			Code:    1.8,
			Message: "Invlaid activate code",
		})
		return
	}

	u.Approved = true
	err = userMngr.UpdateUserDetail(u)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{
			Message:     err.Error(),
			Description: "Error when updating user infomation to database.",
		})
		return
	}

	rw.Write([]byte(`{"Message":"Account activated"}`))
}
Exemple #8
0
/*
PasswordUpdate handle the request for changin user password. its require 'manage_user' permission to
change other's password.

Example Request Body:
  POST /profile/some-kind-of-ID/change-password
  {
    "Pwd": "xxxxxxxxx",
    "NewPwd": "yyyyyyyyy",
    "NewPwdRepeat": "yyyyyyyyy"
  }
Example Success Response:
{
  "Message":"Password for user  has been updated."
}
*/
func PasswordUpdate(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")

	u, userMngr, err := getUserByIdAndWriteIfError(rw, req)
	if err != nil {
		return
	}
	defer userMngr.Close()

	token := strings.TrimPrefix(req.Header.Get("Authorization"), "Bearer ")
	currentUser, err := userMngr.GetUser(token)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{
			Message:     err.Error(),
			Description: "Error when loading current user.",
		})
		return
	}

	var can_manage_user bool

	if currentUser.Id != u.Id {
		if can_manage_user = userMngr.Can(currentUser, "manage_user"); !can_manage_user {
			response.ForbiddenResponse(rw, &response.JSONErr{
				Code:        response.ErrNoPermission,
				Message:     "Current user doesn't have valid permission.",
				Description: "Current user need 'manage_user' permission to perform this action.",
			})
			return
		}
	}

	pwdChange := struct {
		Pwd          string
		NewPwd       string
		NewPwdRepeat string
	}{}

	err = json.NewDecoder(req.Body).Decode(&pwdChange)
	if err != nil {
		response.BadRequestResponse(rw, &response.JSONErr{
			Code:        ErrCodeInvalidInput,
			Message:     err.Error(),
			Description: "The request body mus contain an JSON object with valid Pwd, NewPwd and NewPwdRepeat field.",
		})
		return
	}

	if pwdChange.NewPwd != pwdChange.NewPwdRepeat {
		response.BadRequestResponse(rw, &response.JSONErr{
			Code:    ErrCodePwdMismatch,
			Message: "NewPwd doesn't match.",
		})
		return
	}

	if !can_manage_user {
		if u.ComparePassword(pwdChange.Pwd) != nil {
			response.ForbiddenResponse(rw, &response.JSONErr{
				Code:        ErrCodeInvlaidPwd,
				Message:     "Invalid passsword",
				Description: "Your provied password are invlaid.",
			})
			return
		}
	}

	err = u.ChangePassword(pwdChange.NewPwd)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{
			Message:     err.Error(),
			Description: "Error when update user password.",
		})
		return
	}

	if !can_manage_user {
		rw.WriteHeader(http.StatusAccepted)
		go func() {
			var logger *log.Logger
			logdbw, err := dblog.Provider().OpenLogger(req)
			if err != nil {
				// use std log... :(
				// mostly never be in appengine env
				logger = log.New(os.Stdout, "", log.LstdFlags)
				logger.Println("rest: cannot OpenLogger")
			} else {
				logger = log.New(logdbw, "", 4)
			}

			conf, err := config.Provider().OpenConfigurator(req)
			if err != nil {
				logger.Println("rest: OpenConfigurator", err)
				return
			}
			defer conf.Close()

			mess, err := conf.Get("pwd_change_message")
			if err != nil {
				logger.Println("rest: Configurator can't load 'pwd_change_message'", err)
			}

			err = util.SendSimpleMail(conf, u.Email, "Password changed", mess)
			if err != nil {
				logger.Println("rest: SendSimpleMail", err)
			}
		}()
	}

	rw.Write([]byte(`{"Message":"Password for user ` + u.Email + ` has been updated."}`))
}
Exemple #9
0
/*
SignUp handle the request for account sign-up. The handler will
check the email and password format. If success it will send an
email and immediately return a 202 status code.

Example Request Body:
  POST /signup
  {
    "Email": "*****@*****.**",
    "Pwd": "xxxxxxxxx",
    "PwdRepeat": "xxxxxxxxx"
  }
Example Success Response:
  {
    "Message":"email sent to [email protected]"
  }
*/
func SignUp(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")

	userMngr, err := auth.Provider().OpenUserMngr(req)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{Message: err.Error()})
		return
	}
	defer userMngr.Close()

	credential := struct {
		Email     string
		Pwd       string
		PwdRepeat string
	}{}

	err = json.NewDecoder(req.Body).Decode(&credential)
	if err != nil {
		response.BadRequestResponse(rw, &response.JSONErr{
			Code:        ErrCodeInvalidCredential,
			Message:     err.Error(),
			Description: "Credential must be an valid json object contain Email, Pwd and PwdRepeat.",
		})
		return
	}

	if credential.Pwd != credential.PwdRepeat {
		response.BadRequestResponse(rw, &response.JSONErr{
			Code:    ErrCodePwdMismatch,
			Message: "Pwd and PwdRepeat doesn't match",
		})
		return
	}

	var app bool

	u, err := userMngr.AddUser(credential.Email, credential.PwdRepeat, app)
	if err != nil {
		switch err {
		case auth.ErrInvalidEmail:
			response.ErrorResponse(rw, http.StatusPreconditionFailed, &response.JSONErr{
				Code:    ErrCodeInvalidEmail,
				Message: err.Error(),
			})
		case auth.ErrInvalidPassword:
			response.ErrorResponse(rw, http.StatusPreconditionFailed, &response.JSONErr{
				Code:    ErrCodeInvlaidPwd,
				Message: err.Error(),
			})
		case auth.ErrDuplicateEmail:
			response.ErrorResponse(rw, http.StatusPreconditionFailed, &response.JSONErr{
				Code:    ErrCodeDupEmail,
				Message: err.Error(),
			})
		default:
			response.InternalErrorResponse(rw, &response.JSONErr{Message: err.Error()})
		}
		return
	}

	conf, err := config.Provider().OpenConfigurator(req)
	if err != nil {
		response.InternalErrorResponse(rw, &response.JSONErr{Message: err.Error()})
		return
	}

	if !app {
		// TODO(!)
		// time out control for mail send
		go func() {
			err = util.SendSimpleMail(conf, u.Email, "Email confirm", u.ConfirmCodes["activate"])
			if err != nil {
				log.Println("rest: SendSimpleMail", err)
			}
			conf.Close()
		}()
	}

	rw.WriteHeader(http.StatusAccepted)
	rw.Write([]byte(`{"Message":"email sent to ` + u.Email + `"}`))
}