package web import ( "encoding/json" "fmt" "net/http" "strings" "time" "github.com/gorilla/mux" "github.com/shunfei/cronsun" "github.com/shunfei/cronsun/log" mgo "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" ) type Administrator struct{} func adminAuthHandler(ctx *Context) (abort bool) { if abort = authHandler(true)(ctx); abort { return } abort = ctx.Session.Data["role"].(cronsun.Role) != cronsun.Administrator if abort { outJSONWithCode(ctx.W, http.StatusForbidden, "access deny.") } return } func NewAdminAuthHandler(f func(ctx *Context)) BaseHandler { return BaseHandler{ BeforeHandle: adminAuthHandler, Handle: f, } } type Account struct { Role cronsun.Role `json:"role"` Email string `json:"email"` Status cronsun.UserStatus `json:"status"` Session bool `json:"session"` CreateTime time.Time `json:"createTime"` } func (this *Administrator) GetAccountList(ctx *Context) { list, err := cronsun.GetAccounts(nil) if err != nil { outJSONWithCode(ctx.W, http.StatusInternalServerError, err.Error()) return } var alist = make([]Account, len(list)) for i := range list { alist[i] = Account{ Role: list[i].Role, Email: list[i].Email, Status: list[i].Status, Session: list[i].Session != "", CreateTime: list[i].CreateTime, } } outJSONWithCode(ctx.W, http.StatusOK, alist) } func (this *Administrator) GetAccount(ctx *Context) { vars := mux.Vars(ctx.R) email := strings.TrimSpace(vars["email"]) if email == "" { outJSONWithCode(ctx.W, http.StatusBadRequest, "Email required.") return } u, err := cronsun.GetAccountByEmail(email) if err != nil { if err == mgo.ErrNotFound { outJSONWithCode(ctx.W, http.StatusNotFound, fmt.Sprintf("Email [%s] not found.", email)) } else { outJSONWithCode(ctx.W, http.StatusInternalServerError, err.Error()) } return } outJSONWithCode(ctx.W, http.StatusOK, &Account{ Role: u.Role, Email: u.Email, Status: u.Status, Session: u.Session != "", CreateTime: u.CreateTime, }) } func (this *Administrator) AddAccount(ctx *Context) { account := struct { Role cronsun.Role `json:"role"` Email string `json:"email"` Password string `json:"password"` }{} decoder := json.NewDecoder(ctx.R.Body) err := decoder.Decode(&account) if err != nil { outJSONWithCode(ctx.W, http.StatusBadRequest, err.Error()) return } ctx.R.Body.Close() if !account.Role.Defined() { outJSONWithCode(ctx.W, http.StatusBadRequest, "Account role undefined.") return } account.Email = strings.TrimSpace(account.Email) if len(account.Email) == 0 { outJSONWithCode(ctx.W, http.StatusBadRequest, "Account email is required.") return } account.Password = strings.TrimSpace(account.Password) if len(account.Password) == 0 { outJSONWithCode(ctx.W, http.StatusBadRequest, "Account password is required.") return } u, err := cronsun.GetAccountByEmail(account.Email) if err == nil || u != nil { outJSONWithCode(ctx.W, http.StatusConflict, fmt.Sprintf("Email [%s] has been used.", account.Email)) return } salt := genSalt() err = cronsun.CreateAccount(&cronsun.Account{ Role: account.Role, Email: account.Email, Salt: salt, Status: cronsun.UserActived, Password: encryptPassword(account.Password, salt), }) if err != nil { outJSONWithCode(ctx.W, http.StatusBadRequest, fmt.Sprintf("Failed to create user: %s.", err.Error())) return } outJSONWithCode(ctx.W, http.StatusNoContent, nil) } func (this *Administrator) UpdateAccount(ctx *Context) { account := struct { Role cronsun.Role `json:"role"` OriginEmail string `json:"originEmail"` Email string `json:"email"` Password string `json:"password"` Status cronsun.UserStatus `json:"status"` }{} decoder := json.NewDecoder(ctx.R.Body) err := decoder.Decode(&account) if err != nil { outJSONWithCode(ctx.W, http.StatusBadRequest, err.Error()) return } ctx.R.Body.Close() account.OriginEmail = strings.TrimSpace(account.OriginEmail) if account.OriginEmail == "" { outJSONWithCode(ctx.W, http.StatusBadRequest, "Account origin email is required.") return } if !account.Role.Defined() { outJSONWithCode(ctx.W, http.StatusBadRequest, "Account role undefined.") return } if !account.Status.Defined() { outJSONWithCode(ctx.W, http.StatusBadRequest, "Account status undefined.") return } account.Email = strings.TrimSpace(account.Email) if len(account.Email) == 0 { outJSONWithCode(ctx.W, http.StatusBadRequest, "Account email is required.") return } originAccount, err := cronsun.GetAccountByEmail(account.OriginEmail) if err != nil { if err == mgo.ErrNotFound { outJSONWithCode(ctx.W, http.StatusNotFound, "Email not found.") } else { outJSONWithCode(ctx.W, http.StatusInternalServerError, err.Error()) } return } if originAccount.Unchangeable && originAccount.Email != ctx.Session.Email { outJSONWithCode(ctx.W, http.StatusForbidden, "You can not change this account.") return } var update = bson.M{} if !originAccount.Unchangeable { update = bson.M{ "status": account.Status, "role": account.Role, } } if account.Email != account.OriginEmail { update["email"] = account.Email } account.Password = strings.TrimSpace(account.Password) if len(account.Password) != 0 { salt := genSalt() update["salt"] = salt update["password"] = encryptPassword(account.Password, salt) } if len(update) == 0 { outJSONWithCode(ctx.W, http.StatusOK, nil) return } err = cronsun.UpdateAccount(bson.M{"email": account.OriginEmail}, update) if err != nil { outJSONWithCode(ctx.W, http.StatusBadRequest, fmt.Sprintf("Failed to update user: %s.", err.Error())) return } this.removeSession(account.Email) if ctx.Session.Email == originAccount.Email { ctx.Session.Email = "" delete(ctx.Session.Data, "role") ctx.Session.Store() outJSONWithCode(ctx.W, http.StatusUnauthorized, nil) return } outJSONWithCode(ctx.W, http.StatusOK, nil) } func (this *Administrator) removeSession(email string) { u, err := cronsun.GetAccountByEmail(email) if err != nil { log.Errorf("Failed to remove user [%s] session: %s", email, err.Error()) return } if u.Session == "" { return } sessManager.CleanSeesionData(u.Session) }