func UserList(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() uLst, err := userMngr.FindAllUser("", -1) if err != nil { response.InternalErrorResponse(rw, &response.JSONErr{ Message: err.Error(), Description: "Error when FindAllUser.", }) return } for _, v := range uLst { v.Pwd = auth.Password{} v.OldPwd = []auth.Password{} v.Info = auth.UserInfo{} v.ConfirmCodes = make(map[string]string) } json.NewEncoder(rw).Encode(uLst) }
func OAuthHandleWrapper(handler http.HandlerFunc, groups []string, pri []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 } if len(groups) > 0 { for _, bg := range user.BriefGroups { for _, g2 := range groups { if bg.Name == g2 { handler(rw, req) return } } } } var ( cannot bool cannotDo string ) for _, do := range pri { if !userMngr.Can(user, do) { cannotDo += do + "," } } if cannot { response.ForbiddenResponse(rw, &response.JSONErr{ Code: response.ErrNoPermission, Message: "User doesn't have valid permission.", Description: "User doesn't have " + cannotDo + " permission.", }) return } // run user defined handler handler(rw, req) } }
/* 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) }
/* 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 } }
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) } }
/* UserInfoDetail handle the request for getting user account info. Example Request Body: GET /profile/some-kind-of-ID/info Example Success Response { "FirstName": "Cao Nguyên" ...//other feilds of auth,UserInfo struct } */ func UserInfoDetail(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 } } json.NewEncoder(rw).Encode(u.Info) }
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 }
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) } }
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) }
/* 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"}`)) }
/* 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."}`)) }
/* 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 + `"}`)) }