| @@ -34,6 +34,10 @@ PATH = data/gogs.db | |||
| [security] | |||
| ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! | |||
| SECRET_KEY = !#@FDEWREWR&*( | |||
| ; Auto-login remember days | |||
| LOGIN_REMEMBER_DAYS = 7 | |||
| COOKIE_USERNAME = gogs_awesome | |||
| COOKIE_REMEMBER_NAME = gogs_incredible | |||
| [service] | |||
| ACTIVE_CODE_LIVE_MINUTES = 180 | |||
| @@ -61,6 +61,7 @@ func (f *RegisterForm) Validate(errors *binding.Errors, req *http.Request, conte | |||
| type LogInForm struct { | |||
| UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"` | |||
| Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| Remember string `form:"remember"` | |||
| } | |||
| func (f *LogInForm) Name(field string) string { | |||
| @@ -38,6 +38,10 @@ var ( | |||
| RunUser string | |||
| RepoRootPath string | |||
| LogInRememberDays int | |||
| CookieUserName string | |||
| CookieRememberName string | |||
| Cfg *goconfig.ConfigFile | |||
| MailService *Mailer | |||
| @@ -252,6 +256,10 @@ func NewConfigContext() { | |||
| SecretKey = Cfg.MustValue("security", "SECRET_KEY") | |||
| RunUser = Cfg.MustValue("", "RUN_USER") | |||
| LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS") | |||
| CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME") | |||
| CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") | |||
| PictureService = Cfg.MustValue("picture", "SERVICE") | |||
| PictureRootPath = Cfg.MustValue("picture", "PATH") | |||
| @@ -5,9 +5,14 @@ | |||
| package middleware | |||
| import ( | |||
| "crypto/hmac" | |||
| "crypto/sha1" | |||
| "encoding/base64" | |||
| "fmt" | |||
| "html/template" | |||
| "net/http" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| "github.com/codegangsta/martini" | |||
| @@ -155,6 +160,44 @@ func (ctx *Context) SetCookie(name string, value string, others ...interface{}) | |||
| ctx.Res.Header().Add("Set-Cookie", cookie.String()) | |||
| } | |||
| // Get secure cookie from request by a given key. | |||
| func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) { | |||
| val := ctx.GetCookie(key) | |||
| if val == "" { | |||
| return "", false | |||
| } | |||
| parts := strings.SplitN(val, "|", 3) | |||
| if len(parts) != 3 { | |||
| return "", false | |||
| } | |||
| vs := parts[0] | |||
| timestamp := parts[1] | |||
| sig := parts[2] | |||
| h := hmac.New(sha1.New, []byte(Secret)) | |||
| fmt.Fprintf(h, "%s%s", vs, timestamp) | |||
| if fmt.Sprintf("%02x", h.Sum(nil)) != sig { | |||
| return "", false | |||
| } | |||
| res, _ := base64.URLEncoding.DecodeString(vs) | |||
| return string(res), true | |||
| } | |||
| // Set Secure cookie for response. | |||
| func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) { | |||
| vs := base64.URLEncoding.EncodeToString([]byte(value)) | |||
| timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) | |||
| h := hmac.New(sha1.New, []byte(Secret)) | |||
| fmt.Fprintf(h, "%s%s", vs, timestamp) | |||
| sig := fmt.Sprintf("%02x", h.Sum(nil)) | |||
| cookie := strings.Join([]string{vs, timestamp, sig}, "|") | |||
| ctx.SetCookie(name, cookie, others...) | |||
| } | |||
| func (ctx *Context) CsrfToken() string { | |||
| if len(ctx.csrfToken) > 0 { | |||
| return ctx.csrfToken | |||
| @@ -77,7 +77,39 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||
| ctx.Data["Title"] = "Log In" | |||
| if ctx.Req.Method == "GET" { | |||
| ctx.HTML(200, "user/signin") | |||
| // Check auto-login. | |||
| userName := ctx.GetCookie(base.CookieUserName) | |||
| if len(userName) == 0 { | |||
| ctx.HTML(200, "user/signin") | |||
| return | |||
| } | |||
| isSucceed := false | |||
| defer func() { | |||
| if !isSucceed { | |||
| log.Trace("%s auto-login cookie cleared: %s", ctx.Req.RequestURI, userName) | |||
| ctx.SetCookie(base.CookieUserName, "", -1) | |||
| ctx.SetCookie(base.CookieRememberName, "", -1) | |||
| } | |||
| }() | |||
| user, err := models.GetUserByName(userName) | |||
| if err != nil { | |||
| ctx.HTML(200, "user/signin") | |||
| return | |||
| } | |||
| secret := base.EncodeMd5(user.Rands + user.Passwd) | |||
| value, _ := ctx.GetSecureCookie(secret, base.CookieRememberName) | |||
| if value != user.Name { | |||
| ctx.HTML(200, "user/signin") | |||
| return | |||
| } | |||
| isSucceed = true | |||
| ctx.Session.Set("userId", user.Id) | |||
| ctx.Session.Set("userName", user.Name) | |||
| ctx.Redirect("/") | |||
| return | |||
| } | |||
| @@ -89,6 +121,7 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||
| user, err := models.LoginUserPlain(form.UserName, form.Password) | |||
| if err != nil { | |||
| if err == models.ErrUserNotExist { | |||
| log.Trace("%s Log in failed: %s/%s", ctx.Req.RequestURI, form.UserName, form.Password) | |||
| ctx.RenderWithErr("Username or password is not correct", "user/signin", &form) | |||
| return | |||
| } | |||
| @@ -97,6 +130,13 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||
| return | |||
| } | |||
| if form.Remember == "on" { | |||
| secret := base.EncodeMd5(user.Rands + user.Passwd) | |||
| days := 86400 * base.LogInRememberDays | |||
| ctx.SetCookie(base.CookieUserName, user.Name, days) | |||
| ctx.SetSecureCookie(secret, base.CookieRememberName, user.Name, days) | |||
| } | |||
| ctx.Session.Set("userId", user.Id) | |||
| ctx.Session.Set("userName", user.Name) | |||
| ctx.Redirect("/") | |||
| @@ -19,6 +19,17 @@ | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <div class="col-md-6 col-md-offset-4"> | |||
| <div class="checkbox"> | |||
| <label> | |||
| <input type="checkbox" name="remember" {{if .remember}}checked{{end}}> | |||
| <strong>Remember me</strong> | |||
| </label> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <div class="col-md-offset-4 col-md-6"> | |||
| <button type="submit" class="btn btn-lg btn-primary">Log In</button> | |||