@@ -1,5 +1,6 @@ | |||
; App name that shows on every page title | |||
APP_NAME = Gogs: Go Git Service | |||
APP_LOGO = img/favicon.png | |||
; !!MUST CHANGE TO YOUR USER NAME!! | |||
RUN_USER = lunny | |||
; Either "dev", "prod" or "test", default is "dev" | |||
@@ -11,7 +12,8 @@ LANG_IGNS = Google Go|C|Python|Ruby|C Sharp | |||
LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|BSD (3-Clause) License | |||
[server] | |||
DOMAIN = gogits.org | |||
DOMAIN = localhost | |||
ROOT_URL = http://%(DOMAIN)s:%(HTTP_PORT)s/ | |||
HTTP_ADDR = | |||
HTTP_PORT = 3000 | |||
@@ -27,7 +29,13 @@ SSL_MODE = disable | |||
[security] | |||
; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! | |||
USER_PASSWD_SALT = !#@FDEWREWR&*( | |||
SECRET_KEY = !#@FDEWREWR&*( | |||
[service] | |||
ACTIVE_CODE_LIVE_MINUTES = 180 | |||
RESET_PASSWD_CODE_LIVE_MINUTES = 180 | |||
; User need to confirm e-mail for registration | |||
REGISTER_EMAIL_CONFIRM = true | |||
[mailer] | |||
ENABLED = false | |||
@@ -19,14 +19,6 @@ import ( | |||
"github.com/gogits/gogs/modules/base" | |||
) | |||
var ( | |||
UserPasswdSalt string | |||
) | |||
func init() { | |||
UserPasswdSalt = base.Cfg.MustValue("security", "USER_PASSWD_SALT") | |||
} | |||
// User types. | |||
const ( | |||
UT_INDIVIDUAL = iota + 1 | |||
@@ -56,6 +48,9 @@ type User struct { | |||
AvatarEmail string `xorm:"not null"` | |||
Location string | |||
Website string | |||
IsActive bool | |||
Rands string `xorm:"VARCHAR(10)"` | |||
Expired time.Time | |||
Created time.Time `xorm:"created"` | |||
Updated time.Time `xorm:"updated"` | |||
} | |||
@@ -104,6 +99,11 @@ func (user *User) NewGitSig() *git.Signature { | |||
} | |||
} | |||
// return a user salt token | |||
func GetUserSalt() string { | |||
return base.GetRandomString(10) | |||
} | |||
// RegisterUser creates record of a new user. | |||
func RegisterUser(user *User) (err error) { | |||
isExist, err := IsUserExist(user.Name) | |||
@@ -123,6 +123,8 @@ func RegisterUser(user *User) (err error) { | |||
user.LowerName = strings.ToLower(user.Name) | |||
user.Avatar = base.EncodeMd5(user.Email) | |||
user.AvatarEmail = user.Email | |||
user.Expired = time.Now().Add(3 * 24 * time.Hour) | |||
user.Rands = GetUserSalt() | |||
if err = user.EncodePasswd(); err != nil { | |||
return err | |||
} else if _, err = orm.Insert(user); err != nil { | |||
@@ -134,6 +136,11 @@ func RegisterUser(user *User) (err error) { | |||
} | |||
return err | |||
} | |||
// Send confirmation e-mail. | |||
if base.Service.RegisterEmailConfitm { | |||
} | |||
return nil | |||
} | |||
@@ -183,7 +190,7 @@ func DeleteUser(user *User) error { | |||
// EncodePasswd encodes password to safe format. | |||
func (user *User) EncodePasswd() error { | |||
newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(UserPasswdSalt), 16384, 8, 1, 64) | |||
newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(base.SecretKey), 16384, 8, 1, 64) | |||
user.Passwd = fmt.Sprintf("%x", newPasswd) | |||
return err | |||
} | |||
@@ -0,0 +1,42 @@ | |||
// 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 auth | |||
import ( | |||
"encoding/hex" | |||
"fmt" | |||
"github.com/gogits/gogs/models" | |||
"github.com/gogits/gogs/modules/base" | |||
"github.com/gogits/gogs/modules/mailer" | |||
) | |||
// create a time limit code for user active | |||
func CreateUserActiveCode(user *models.User, startInf interface{}) string { | |||
hours := base.Service.ActiveCodeLives / 60 | |||
data := fmt.Sprintf("%d", user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands | |||
code := base.CreateTimeLimitCode(data, hours, startInf) | |||
// add tail hex username | |||
code += hex.EncodeToString([]byte(user.LowerName)) | |||
return code | |||
} | |||
// Send user register mail with active code | |||
func SendRegisterMail(user *models.User) { | |||
code := CreateUserActiveCode(user, nil) | |||
subject := "Register success, Welcome" | |||
data := mailer.GetMailTmplData(user) | |||
data["Code"] = code | |||
body := base.RenderTemplate("mail/auth/register_success.html", data) | |||
_, _, _ = code, subject, body | |||
// msg := mailer.NewMailMessage([]string{user.Email}, subject, body) | |||
// msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id) | |||
// // async send mail | |||
// mailer.SendAsync(msg) | |||
} |
@@ -28,11 +28,20 @@ type Mailer struct { | |||
var ( | |||
AppVer string | |||
AppName string | |||
AppLogo string | |||
AppUrl string | |||
Domain string | |||
SecretKey string | |||
Cfg *goconfig.ConfigFile | |||
MailService *Mailer | |||
) | |||
var Service struct { | |||
RegisterEmailConfitm bool | |||
ActiveCodeLives int | |||
ResetPwdCodeLives int | |||
} | |||
func exeDir() (string, error) { | |||
file, err := exec.LookPath(os.Args[0]) | |||
if err != nil { | |||
@@ -54,6 +63,11 @@ var logLevels = map[string]string{ | |||
"Critical": "5", | |||
} | |||
func newService() { | |||
Service.ActiveCodeLives = Cfg.MustInt("service", "ACTIVE_CODE_LIVE_MINUTES", 180) | |||
Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180) | |||
} | |||
func newLogService() { | |||
// Get and check log mode. | |||
mode := Cfg.MustValue("log", "MODE", "console") | |||
@@ -117,6 +131,17 @@ func newMailService() { | |||
} | |||
} | |||
func newRegisterService() { | |||
if !Cfg.MustBool("service", "REGISTER_EMAIL_CONFIRM") { | |||
return | |||
} else if MailService == nil { | |||
log.Warn("Register Service: Mail Service is not enabled") | |||
return | |||
} | |||
Service.RegisterEmailConfitm = true | |||
log.Info("Register Service Enabled") | |||
} | |||
func init() { | |||
var err error | |||
workDir, err := exeDir() | |||
@@ -143,9 +168,13 @@ func init() { | |||
Cfg.BlockMode = false | |||
AppName = Cfg.MustValue("", "APP_NAME", "Gogs: Go Git Service") | |||
AppLogo = Cfg.MustValue("", "APP_LOGO", "img/favicon.png") | |||
AppUrl = Cfg.MustValue("server", "ROOT_URL") | |||
Domain = Cfg.MustValue("server", "DOMAIN") | |||
SecretKey = Cfg.MustValue("security", "SECRET_KEY") | |||
// Extensions. | |||
newLogService() | |||
newMailService() | |||
newRegisterService() | |||
} |
@@ -7,6 +7,8 @@ package base | |||
import ( | |||
"bytes" | |||
"crypto/md5" | |||
"crypto/rand" | |||
"crypto/sha1" | |||
"encoding/hex" | |||
"encoding/json" | |||
"fmt" | |||
@@ -22,6 +24,66 @@ func EncodeMd5(str string) string { | |||
return hex.EncodeToString(m.Sum(nil)) | |||
} | |||
// Random generate string | |||
func GetRandomString(n int) string { | |||
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | |||
var bytes = make([]byte, n) | |||
rand.Read(bytes) | |||
for i, b := range bytes { | |||
bytes[i] = alphanum[b%byte(len(alphanum))] | |||
} | |||
return string(bytes) | |||
} | |||
// 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 { | |||
format := "YmdHi" | |||
var start, end time.Time | |||
var startStr, endStr string | |||
if startInf == nil { | |||
// Use now time create code | |||
start = time.Now() | |||
startStr = DateFormat(start, format) | |||
} else { | |||
// use start string create code | |||
startStr = startInf.(string) | |||
start, _ = DateParse(startStr, format) | |||
startStr = DateFormat(start, format) | |||
} | |||
end = start.Add(time.Minute * time.Duration(minutes)) | |||
endStr = DateFormat(end, format) | |||
// create sha1 encode string | |||
sh := sha1.New() | |||
sh.Write([]byte(data + SecretKey + startStr + endStr + fmt.Sprintf("%d", minutes))) | |||
encoded := hex.EncodeToString(sh.Sum(nil)) | |||
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded) | |||
return code | |||
} | |||
func RenderTemplate(TplNames string, Data map[interface{}]interface{}) string { | |||
// if beego.RunMode == "dev" { | |||
// beego.BuildTemplate(beego.ViewsPath) | |||
// } | |||
// ibytes := bytes.NewBufferString("") | |||
// if _, ok := beego.BeeTemplates[TplNames]; !ok { | |||
// panic("can't find templatefile in the path:" + TplNames) | |||
// } | |||
// err := beego.BeeTemplates[TplNames].ExecuteTemplate(ibytes, TplNames, Data) | |||
// if err != nil { | |||
// beego.Trace("template Execute err:", err) | |||
// } | |||
// icontent, _ := ioutil.ReadAll(ibytes) | |||
// return string(icontent) | |||
return "not implement yet" | |||
} | |||
// AvatarLink returns avatar link by given e-mail. | |||
func AvatarLink(email string) string { | |||
return "http://1.gravatar.com/avatar/" + EncodeMd5(email) | |||
@@ -0,0 +1,24 @@ | |||
// 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 mailer | |||
import ( | |||
"github.com/gogits/gogs/models" | |||
"github.com/gogits/gogs/modules/base" | |||
) | |||
func GetMailTmplData(user *models.User) map[interface{}]interface{} { | |||
data := make(map[interface{}]interface{}, 10) | |||
data["AppName"] = base.AppName | |||
data["AppVer"] = base.AppVer | |||
data["AppUrl"] = base.AppUrl | |||
data["AppLogo"] = base.AppLogo | |||
data["ActiveCodeLives"] = base.Service.ActiveCodeLives | |||
data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives | |||
if user != nil { | |||
data["User"] = user | |||
} | |||
return data | |||
} |
@@ -37,6 +37,8 @@ func Logger() martini.Handler { | |||
content = fmt.Sprintf("\033[1;33m%s\033[0m", content) | |||
case 404: | |||
content = fmt.Sprintf("\033[1;31m%s\033[0m", content) | |||
case 500: | |||
content = fmt.Sprintf("\033[1;36m%s\033[0m", content) | |||
} | |||
} | |||
log.Println(content) | |||
@@ -131,9 +131,10 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { | |||
} | |||
u := &models.User{ | |||
Name: form.UserName, | |||
Email: form.Email, | |||
Passwd: form.Password, | |||
Name: form.UserName, | |||
Email: form.Email, | |||
Passwd: form.Password, | |||
IsActive: !base.Service.RegisterEmailConfitm, | |||
} | |||
if err := models.RegisterUser(u); err != nil { | |||