diff --git a/models/user.go b/models/user.go index 5f08f9e92d..3c31b3aff4 100644 --- a/models/user.go +++ b/models/user.go @@ -5,6 +5,7 @@ package models import ( + "encoding/hex" "errors" "fmt" "os" @@ -17,6 +18,7 @@ import ( "github.com/gogits/git" "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/log" ) // User types. @@ -139,9 +141,43 @@ func RegisterUser(user *User) (*User, error) { return user, nil } +// get user by erify code +func getVerifyUser(code string) (user *User) { + if len(code) <= base.TimeLimitCodeLength { + return nil + } + + // use tail hex username query user + hexStr := code[base.TimeLimitCodeLength:] + if b, err := hex.DecodeString(hexStr); err == nil { + if user, err = GetUserByName(string(b)); user != nil { + return user + } + log.Error("user.getVerifyUser: %v", err) + } + + return nil +} + +// verify active code when active account +func VerifyUserActiveCode(code string) (user *User) { + minutes := base.Service.ActiveCodeLives + + if user = getVerifyUser(code); user != nil { + // time limit code + prefix := code[:base.TimeLimitCodeLength] + data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands + + if base.VerifyTimeLimitCode(data, minutes, prefix) { + return user + } + } + return nil +} + // UpdateUser updates user's information. func UpdateUser(user *User) (err error) { - _, err = orm.Id(user.Id).Update(user) + _, err = orm.Id(user.Id).UseBool().Update(user) return err } diff --git a/modules/base/conf.go b/modules/base/conf.go index 64c97028b9..41226459d9 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -131,15 +131,15 @@ func newMailService() { } } -func newRegisterService() { +func newRegisterMailService() { if !Cfg.MustBool("service", "REGISTER_EMAIL_CONFIRM") { return } else if MailService == nil { - log.Warn("Register Service: Mail Service is not enabled") + log.Warn("Register Mail Service: Mail Service is not enabled") return } Service.RegisterEmailConfirm = true - log.Info("Register Service Enabled") + log.Info("Register Mail Service Enabled") } func init() { @@ -177,5 +177,5 @@ func init() { newService() newLogService() newMailService() - newRegisterService() + newRegisterMailService() } diff --git a/modules/base/template.go b/modules/base/template.go index 23d3d27713..5268da6490 100644 --- a/modules/base/template.go +++ b/modules/base/template.go @@ -8,6 +8,7 @@ import ( "container/list" "fmt" "html/template" + "strings" "time" ) @@ -54,4 +55,7 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ "ActionDesc": ActionDesc, "DateFormat": DateFormat, "List": List, + "Mail2Domain": func(mail string) string { + return "mail." + strings.Split(mail, "@")[1] + }, } diff --git a/modules/base/tool.go b/modules/base/tool.go index d0b6bfbfda..8fabb8c531 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -36,6 +36,35 @@ func GetRandomString(n int) string { return string(bytes) } +// verify time limit code +func VerifyTimeLimitCode(data string, minutes int, code string) bool { + if len(code) <= 18 { + return false + } + + // split code + start := code[:12] + lives := code[12:18] + if d, err := StrTo(lives).Int(); err == nil { + minutes = d + } + + // right active code + retCode := CreateTimeLimitCode(data, minutes, start) + if retCode == code && minutes > 0 { + // check time is expired or not + before, _ := DateParse(start, "YmdHi") + now := time.Now() + if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() { + return true + } + } + + return false +} + +const TimeLimitCodeLength = 12 + 6 + 40 + // create a time limit code // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string { @@ -283,16 +312,24 @@ func DateFormat(t time.Time, format string) string { return t.Format(format) } -type argInt []int +// convert string to specify type -func (a argInt) Get(i int, args ...int) (r int) { - if i >= 0 && i < len(a) { - r = a[i] +type StrTo string + +func (f StrTo) Exist() bool { + return string(f) != string(0x1E) +} + +func (f StrTo) Int() (int, error) { + v, err := strconv.ParseInt(f.String(), 10, 32) + return int(v), err +} + +func (f StrTo) String() string { + if f.Exist() { + return string(f) } - if len(args) > 0 { - r = args[0] - } - return + return "" } // convert any type to string @@ -334,6 +371,18 @@ func ToStr(value interface{}, args ...int) (s string) { return s } +type argInt []int + +func (a argInt) Get(i int, args ...int) (r int) { + if i >= 0 && i < len(a) { + r = a[i] + } + if len(args) > 0 { + r = args[0] + } + return +} + type Actioner interface { GetOpType() int GetActUserName() string diff --git a/modules/mailer/mail.go b/modules/mailer/mail.go index de4f24a47d..c1d12bba45 100644 --- a/modules/mailer/mail.go +++ b/modules/mailer/mail.go @@ -37,9 +37,9 @@ func GetMailTmplData(user *models.User) map[interface{}]interface{} { // create a time limit code for user active func CreateUserActiveCode(user *models.User, startInf interface{}) string { - hours := base.Service.ActiveCodeLives / 60 + minutes := base.Service.ActiveCodeLives data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands - code := base.CreateTimeLimitCode(data, hours, startInf) + code := base.CreateTimeLimitCode(data, minutes, startInf) // add tail hex username code += hex.EncodeToString([]byte(user.LowerName)) @@ -70,11 +70,11 @@ func SendRegisterMail(r *middleware.Render, user *models.User) { func SendActiveMail(r *middleware.Render, user *models.User) { code := CreateUserActiveCode(user, nil) - subject := "Verify your email address" + subject := "Verify your e-mail address" data := GetMailTmplData(user) data["Code"] = code - body, err := r.HTMLString("mail/auth/active_email.html", data) + body, err := r.HTMLString("mail/auth/active_email", data) if err != nil { log.Error("mail.SendActiveMail(fail to render): %v", err) return diff --git a/modules/middleware/auth.go b/modules/middleware/auth.go index ed847dd83c..d45a21e988 100644 --- a/modules/middleware/auth.go +++ b/modules/middleware/auth.go @@ -6,6 +6,8 @@ package middleware import ( "github.com/codegangsta/martini" + + "github.com/gogits/gogs/modules/base" ) // SignInRequire requires user to sign in. @@ -16,10 +18,10 @@ func SignInRequire(redirect bool) martini.Handler { ctx.Redirect("/") } return - } else if !ctx.User.IsActive { - // ctx.Data["Title"] = "Activate Your Account" - // ctx.Render.HTML(200, "user/active", ctx.Data) - // return + } else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { + ctx.Data["Title"] = "Activate Your Account" + ctx.Render.HTML(200, "user/active", ctx.Data) + return } } } diff --git a/routers/dev/template.go b/routers/dev/template.go new file mode 100644 index 0000000000..7d5225ece7 --- /dev/null +++ b/routers/dev/template.go @@ -0,0 +1,25 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package dev + +import ( + "github.com/codegangsta/martini" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/middleware" +) + +func TemplatePreview(ctx *middleware.Context, params martini.Params) { + ctx.Data["User"] = models.User{Name: "Unknown"} + ctx.Data["AppName"] = base.AppName + ctx.Data["AppVer"] = base.AppVer + ctx.Data["AppUrl"] = base.AppUrl + ctx.Data["AppLogo"] = base.AppLogo + ctx.Data["Code"] = "2014031910370000009fff6782aadb2162b4a997acb69d4400888e0b9274657374" + ctx.Data["ActiveCodeLives"] = base.Service.ActiveCodeLives / 60 + ctx.Data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives / 60 + ctx.HTML(200, params["_1"], ctx.Data) +} diff --git a/routers/user/user.go b/routers/user/user.go index da70ced9f5..32f458f835 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -243,4 +243,18 @@ func Activate(ctx *middleware.Context) { ctx.Render.HTML(200, "user/active", ctx.Data) return } + + // Verify code. + if user := models.VerifyUserActiveCode(code); user != nil { + user.IsActive = true + user.Rands = models.GetUserSalt() + models.UpdateUser(user) + ctx.Session.Set("userId", user.Id) + ctx.Session.Set("userName", user.Name) + ctx.Redirect("/", 302) + return + } + + ctx.Data["IsActivateFailed"] = true + ctx.Render.HTML(200, "user/active", ctx.Data) } diff --git a/templates/mail/auth/active_email.html b/templates/mail/auth/active_email.html deleted file mode 100644 index ccb1202680..0000000000 --- a/templates/mail/auth/active_email.html +++ /dev/null @@ -1,25 +0,0 @@ -{{template "mail/base.html" .}} -{{define "title"}} - {{if eq .Lang "zh-CN"}} - {{.User.NickName}},激活你的账户 - {{end}} - {{if eq .Lang "en-US"}} - {{.User.NickName}}, please active your account - {{end}} -{{end}} -{{define "body"}} - {{if eq .Lang "zh-CN"}} -
点击链接验证email,{{.ActiveCodeLives}} 分钟内有效
-- {{.AppUrl}}active/{{.Code}} -
-如果链接点击无反应,请复制到浏览器打开。
- {{end}} - {{if eq .Lang "en-US"}} -Please click following link to verify your e-mail in {{.ActiveCodeLives}} hours
-- {{.AppUrl}}active/{{.Code}} -
-Copy and paste it to your browser if it's not working.
- {{end}} -{{end}} \ No newline at end of file diff --git a/templates/mail/base.html b/templates/mail/auth/active_email.tmpl similarity index 52% rename from templates/mail/base.html rename to templates/mail/auth/active_email.tmpl index e86c2adfc2..c04ddc8a30 100644 --- a/templates/mail/base.html +++ b/templates/mail/auth/active_email.tmpl @@ -2,26 +2,30 @@ -Please click following link to verify your e-mail address within {{.ActiveCodeLives}} hours.
++ {{.AppUrl}}user/activate?code={{.Code}} +
+Copy and paste it to your browser if the link is not working.
Please click following link to verify your e-mail in {{.ActiveCodeLives}} hours
+Please click following link to verify your e-mail address within {{.ActiveCodeLives}} hours.
- {{.AppUrl}}active/{{.Code}} + {{.AppUrl}}user/activate?code={{.Code}}
-Copy and paste it to your browser if it's not working.
+Copy and paste it to your browser if the link is not working.