cronsun/web/authentication.go

201 lines
4.9 KiB
Go

package web
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/shunfei/cronsun"
"github.com/shunfei/cronsun/conf"
"github.com/shunfei/cronsun/log"
"github.com/shunfei/cronsun/utils"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
func checkAuthBasicData() error {
if conf.Config.Web.Auth.Enabled {
log.Infof("Authentication enabled.")
list, err := cronsun.GetAccounts(bson.M{"role": cronsun.Administrator, "status": cronsun.UserActived})
if err != nil {
return fmt.Errorf("Failed to check available Administrators: %s.", err.Error())
}
if len(list) == 0 {
// create a default administrator with admin@admin.com/admin
// the email and password can be change from the user profile page.
salt := genSalt()
err = cronsun.CreateAccount(&cronsun.Account{
Role: cronsun.Administrator,
Email: "admin@admin.com",
Salt: salt,
Password: encryptPassword("admin", salt),
Status: cronsun.UserActived,
Unchangeable: true,
})
if err != nil {
return fmt.Errorf("Failed to create default Administrators: %s.", err.Error())
}
}
if err = cronsun.EnsureAccountIndex(); err != nil {
log.Warnf("Failed to make db index on the `user` collection: %s.", err.Error())
}
}
return nil
}
func encryptPassword(pwd, salt string) string {
m := md5.Sum([]byte(pwd + salt))
m = md5.Sum(m[:])
return hex.EncodeToString(m[:])
}
func genSalt() string {
return utils.RandString(8)
}
type Authentication struct{}
func (this *Authentication) GetAuthSession(ctx *Context) {
var authInfo = &struct {
Role cronsun.Role `json:"role,omitempty"`
Email string `json:"email,omitempty"`
EnabledAuth bool `json:"enabledAuth"`
}{}
if !conf.Config.Web.Auth.Enabled {
outJSONWithCode(ctx.W, http.StatusOK, authInfo)
return
}
authInfo.EnabledAuth = true
if ctx.Session.Email != "" {
authInfo.Email = ctx.Session.Email
authInfo.Role = ctx.Session.Data["role"].(cronsun.Role)
outJSONWithCode(ctx.W, http.StatusOK, authInfo)
return
}
if len(getStringVal("check", ctx.R)) > 0 {
outJSONWithCode(ctx.W, http.StatusUnauthorized, nil)
return
}
email := getStringVal("email", ctx.R)
password := getStringVal("password", ctx.R)
remember := getStringVal("remember", ctx.R) == "on"
u, err := cronsun.GetAccountByEmail(email)
if err != nil {
if err == mgo.ErrNotFound {
outJSONWithCode(ctx.W, http.StatusNotFound, "User ["+email+"] not found.")
} else {
outJSONWithCode(ctx.W, http.StatusInternalServerError, err.Error())
}
return
}
if u.Password != encryptPassword(password, u.Salt) {
outJSONWithCode(ctx.W, http.StatusBadRequest, "Incorrect password.")
return
}
if u.Status != cronsun.UserActived {
outJSONWithCode(ctx.W, http.StatusForbidden, "Access deny.")
return
}
if !remember {
if c, err := ctx.R.Cookie(conf.Config.Web.Session.CookieName); err == nil {
c.MaxAge = 0
c.Path = "/"
http.SetCookie(ctx.W, c)
}
}
ctx.Session.Email = u.Email
ctx.Session.Data["role"] = u.Role
ctx.Session.Store()
authInfo.Role = u.Role
authInfo.Email = u.Email
err = cronsun.UpdateAccount(bson.M{"email": email}, bson.M{"session": ctx.Session.ID()})
outJSONWithCode(ctx.W, http.StatusOK, authInfo)
}
func (this *Authentication) DeleteAuthSession(ctx *Context) {
ctx.Session.Email = ""
delete(ctx.Session.Data, "role")
ctx.Session.Store()
outJSONWithCode(ctx.W, http.StatusOK, nil)
}
func (this *Authentication) SetPassword(ctx *Context) {
var sp = &struct {
Password string `json:"password"`
NewPassword string `json:"newPassword"`
}{}
decoder := json.NewDecoder(ctx.R.Body)
err := decoder.Decode(&sp)
if err != nil {
outJSONWithCode(ctx.W, http.StatusBadRequest, err.Error())
return
}
ctx.R.Body.Close()
sp.Password = strings.TrimSpace(sp.Password)
sp.NewPassword = strings.TrimSpace(sp.NewPassword)
if sp.Password == "" {
outJSONWithCode(ctx.W, http.StatusBadRequest, "Passowrd is required.")
return
}
if sp.NewPassword == "" {
outJSONWithCode(ctx.W, http.StatusBadRequest, "New passowrd is required.")
return
}
var email = ctx.Session.Email
u, err := cronsun.GetAccountByEmail(email)
if err != nil {
if err == mgo.ErrNotFound {
outJSONWithCode(ctx.W, http.StatusNotFound, "User ["+email+"] not found.")
} else {
outJSONWithCode(ctx.W, http.StatusInternalServerError, err.Error())
}
return
}
if u.Password != encryptPassword(sp.Password, u.Salt) {
outJSONWithCode(ctx.W, http.StatusBadRequest, "Incorrect password.")
return
}
salt := genSalt()
update := bson.M{
"salt": salt,
"password": encryptPassword(sp.NewPassword, salt),
}
if err = cronsun.UpdateAccount(bson.M{"email": email}, update); err != nil {
if err == mgo.ErrNotFound {
outJSONWithCode(ctx.W, http.StatusBadRequest, "User ["+email+"] not found.")
} else {
outJSONWithCode(ctx.W, http.StatusInternalServerError, err.Error())
}
return
}
outJSONWithCode(ctx.W, http.StatusOK, nil)
}