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 }
/* 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) }
/* 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"}`)) }
/* 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 + `"}`)) }