| @@ -16,3 +16,6 @@ NAME = gogs | |||||
| USER = root | USER = root | ||||
| PASSWD = | PASSWD = | ||||
| PASSWD_jiahua = root | PASSWD_jiahua = root | ||||
| [security] | |||||
| USER_PASSWD_SALT = !#@FDEWREWR&*( | |||||
| @@ -19,7 +19,7 @@ import ( | |||||
| // Test that go1.1 tag above is included in builds. main.go refers to this definition. | // Test that go1.1 tag above is included in builds. main.go refers to this definition. | ||||
| const go11tag = true | const go11tag = true | ||||
| const APP_VER = "0.0.0.0305" | |||||
| const APP_VER = "0.0.0.0306" | |||||
| func init() { | func init() { | ||||
| runtime.GOMAXPROCS(runtime.NumCPU()) | runtime.GOMAXPROCS(runtime.NumCPU()) | ||||
| @@ -17,6 +17,12 @@ import ( | |||||
| "github.com/gogits/gogs/utils" | "github.com/gogits/gogs/utils" | ||||
| ) | ) | ||||
| var UserPasswdSalt string | |||||
| func init() { | |||||
| UserPasswdSalt = utils.Cfg.MustValue("security", "USER_PASSWD_SALT") | |||||
| } | |||||
| // User types. | // User types. | ||||
| const ( | const ( | ||||
| UT_INDIVIDUAL = iota + 1 | UT_INDIVIDUAL = iota + 1 | ||||
| @@ -33,9 +39,9 @@ const ( | |||||
| type User struct { | type User struct { | ||||
| Id int64 | Id int64 | ||||
| LowerName string `xorm:"unique not null"` | LowerName string `xorm:"unique not null"` | ||||
| Name string `xorm:"unique not null" valid:"AlphaDash;MinSize(5);MaxSize(30)"` | |||||
| Email string `xorm:"unique not null" valid:"Email"` | |||||
| Passwd string `xorm:"not null" valid:"MinSize(8)"` | |||||
| Name string `xorm:"unique not null"` | |||||
| Email string `xorm:"unique not null"` | |||||
| Passwd string `xorm:"not null"` | |||||
| LoginType int | LoginType int | ||||
| Type int | Type int | ||||
| NumFollowers int | NumFollowers int | ||||
| @@ -79,6 +85,7 @@ var ( | |||||
| ErrUserOwnRepos = errors.New("User still have ownership of repositories") | ErrUserOwnRepos = errors.New("User still have ownership of repositories") | ||||
| ErrUserAlreadyExist = errors.New("User already exist") | ErrUserAlreadyExist = errors.New("User already exist") | ||||
| ErrUserNotExist = errors.New("User does not exist") | ErrUserNotExist = errors.New("User does not exist") | ||||
| ErrEmailAlreadyUsed = errors.New("E-mail already used") | |||||
| ) | ) | ||||
| // IsUserExist checks if given user name exist, | // IsUserExist checks if given user name exist, | ||||
| @@ -87,6 +94,10 @@ func IsUserExist(name string) (bool, error) { | |||||
| return orm.Get(&User{LowerName: strings.ToLower(name)}) | return orm.Get(&User{LowerName: strings.ToLower(name)}) | ||||
| } | } | ||||
| func IsEmailUsed(email string) (bool, error) { | |||||
| return orm.Get(&User{Email: email}) | |||||
| } | |||||
| // RegisterUser creates record of a new user. | // RegisterUser creates record of a new user. | ||||
| func RegisterUser(user *User) (err error) { | func RegisterUser(user *User) (err error) { | ||||
| isExist, err := IsUserExist(user.Name) | isExist, err := IsUserExist(user.Name) | ||||
| @@ -96,9 +107,18 @@ func RegisterUser(user *User) (err error) { | |||||
| return ErrUserAlreadyExist | return ErrUserAlreadyExist | ||||
| } | } | ||||
| isExist, err = IsEmailUsed(user.Email) | |||||
| if err != nil { | |||||
| return err | |||||
| } else if isExist { | |||||
| return ErrEmailAlreadyUsed | |||||
| } | |||||
| user.LowerName = strings.ToLower(user.Name) | user.LowerName = strings.ToLower(user.Name) | ||||
| user.Avatar = utils.EncodeMd5(user.Email) | user.Avatar = utils.EncodeMd5(user.Email) | ||||
| user.EncodePasswd() | |||||
| if err = user.EncodePasswd(); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err = orm.Insert(user); err != nil { | if _, err = orm.Insert(user); err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -136,7 +156,7 @@ func DeleteUser(user *User) error { | |||||
| // EncodePasswd encodes password to safe format. | // EncodePasswd encodes password to safe format. | ||||
| func (user *User) EncodePasswd() error { | func (user *User) EncodePasswd() error { | ||||
| newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte("!#@FDEWREWR&*("), 16384, 8, 1, 64) | |||||
| newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(UserPasswdSalt), 16384, 8, 1, 64) | |||||
| user.Passwd = fmt.Sprintf("%x", newPasswd) | user.Passwd = fmt.Sprintf("%x", newPasswd) | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -17,10 +17,25 @@ import ( | |||||
| "github.com/gogits/gogs/utils/log" | "github.com/gogits/gogs/utils/log" | ||||
| ) | ) | ||||
| type Form interface { | |||||
| Name(field string) string | |||||
| } | |||||
| type RegisterForm struct { | type RegisterForm struct { | ||||
| Username string `form:"username" binding:"Required;AlphaDash;MinSize(5);MaxSize(30)"` | |||||
| Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | |||||
| Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||||
| UserName string `form:"username" binding:"Required;AlphaDash;MinSize(5);MaxSize(30)"` | |||||
| Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | |||||
| Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||||
| RetypePasswd string `form:"retypepasswd"` | |||||
| } | |||||
| func (r *RegisterForm) Name(field string) string { | |||||
| names := map[string]string{ | |||||
| "UserName": "Username", | |||||
| "Email": "E-mail address", | |||||
| "Password": "Password", | |||||
| "RetypePasswd": "Re-type password", | |||||
| } | |||||
| return names[field] | |||||
| } | } | ||||
| func getMinMaxSize(field reflect.StructField) string { | func getMinMaxSize(field reflect.StructField) string { | ||||
| @@ -32,7 +47,7 @@ func getMinMaxSize(field reflect.StructField) string { | |||||
| return "" | return "" | ||||
| } | } | ||||
| func validate(errors *binding.Errors, data base.TmplData, form interface{}) { | |||||
| func validate(errors *binding.Errors, data base.TmplData, form Form) { | |||||
| typ := reflect.TypeOf(form) | typ := reflect.TypeOf(form) | ||||
| val := reflect.ValueOf(form) | val := reflect.ValueOf(form) | ||||
| @@ -54,15 +69,15 @@ func validate(errors *binding.Errors, data base.TmplData, form interface{}) { | |||||
| data["Err_"+field.Name] = true | data["Err_"+field.Name] = true | ||||
| switch err { | switch err { | ||||
| case binding.RequireError: | case binding.RequireError: | ||||
| data["ErrorMsg"] = field.Name + " cannot be empty" | |||||
| data["ErrorMsg"] = form.Name(field.Name) + " cannot be empty" | |||||
| case binding.AlphaDashError: | case binding.AlphaDashError: | ||||
| data["ErrorMsg"] = field.Name + " must be valid alpha or numeric or dash(-_) characters" | |||||
| data["ErrorMsg"] = form.Name(field.Name) + " must be valid alpha or numeric or dash(-_) characters" | |||||
| case binding.MinSizeError: | case binding.MinSizeError: | ||||
| data["ErrorMsg"] = field.Name + " must contain at least has " + getMinMaxSize(field) + " characters" | |||||
| data["ErrorMsg"] = form.Name(field.Name) + " must contain at least " + getMinMaxSize(field) + " characters" | |||||
| case binding.MaxSizeError: | case binding.MaxSizeError: | ||||
| data["ErrorMsg"] = field.Name + " must contain at most has " + getMinMaxSize(field) + " characters" | |||||
| data["ErrorMsg"] = form.Name(field.Name) + " must contain at most " + getMinMaxSize(field) + " characters" | |||||
| case binding.EmailError: | case binding.EmailError: | ||||
| data["ErrorMsg"] = field.Name + " is not valid" | |||||
| data["ErrorMsg"] = form.Name(field.Name) + " is not valid" | |||||
| default: | default: | ||||
| data["ErrorMsg"] = "Unknown error: " + err | data["ErrorMsg"] = "Unknown error: " + err | ||||
| } | } | ||||
| @@ -118,30 +118,43 @@ func SignUp(form auth.RegisterForm, data base.TmplData, req *http.Request, r ren | |||||
| return | return | ||||
| } | } | ||||
| if form.Password != form.RetypePasswd { | |||||
| data["HasError"] = true | |||||
| data["Err_Password"] = true | |||||
| data["Err_RetypePasswd"] = true | |||||
| data["ErrorMsg"] = "Password and re-type password are not same" | |||||
| auth.AssignForm(form, data) | |||||
| } | |||||
| if hasErr, ok := data["HasError"]; ok && hasErr.(bool) { | if hasErr, ok := data["HasError"]; ok && hasErr.(bool) { | ||||
| r.HTML(200, "user/signup", data) | r.HTML(200, "user/signup", data) | ||||
| return | return | ||||
| } | } | ||||
| //Front-end should do double check of password. | |||||
| u := &models.User{ | u := &models.User{ | ||||
| Name: form.Username, | |||||
| Name: form.UserName, | |||||
| Email: form.Email, | Email: form.Email, | ||||
| Passwd: form.Password, | Passwd: form.Password, | ||||
| } | } | ||||
| if err := models.RegisterUser(u); err != nil { | if err := models.RegisterUser(u); err != nil { | ||||
| if err.Error() == models.ErrUserAlreadyExist.Error() { | |||||
| data["HasError"] = true | |||||
| data["HasError"] = true | |||||
| auth.AssignForm(form, data) | |||||
| switch err.Error() { | |||||
| case models.ErrUserAlreadyExist.Error(): | |||||
| data["Err_Username"] = true | data["Err_Username"] = true | ||||
| data["ErrorMsg"] = "Username has been already taken" | data["ErrorMsg"] = "Username has been already taken" | ||||
| auth.AssignForm(form, data) | |||||
| r.HTML(200, "user/signup", data) | r.HTML(200, "user/signup", data) | ||||
| return | |||||
| case models.ErrEmailAlreadyUsed.Error(): | |||||
| data["Err_Email"] = true | |||||
| data["ErrorMsg"] = "E-mail address has been already used" | |||||
| r.HTML(200, "user/signup", data) | |||||
| default: | |||||
| data["ErrorMsg"] = err | |||||
| log.Error("user.SignUp: %v", data) | |||||
| r.HTML(500, "base/error", nil) | |||||
| } | } | ||||
| log.Error("user.SignUp: %v", err) | |||||
| r.HTML(500, "status/500", nil) | |||||
| return | return | ||||
| } | } | ||||
| @@ -1,6 +1,6 @@ | |||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||
| <div id="gogs-body" class="container"> | <div id="gogs-body" class="container"> | ||||
| An error is occupied : {{.Error}} | |||||
| An error is occurred : {{.ErrorMsg}} | |||||
| </div> | </div> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| @@ -6,7 +6,7 @@ | |||||
| {{if .HasError}} | {{if .HasError}} | ||||
| <div class="alert alert-danger">{{.ErrorMsg}}</div> | <div class="alert alert-danger">{{.ErrorMsg}}</div> | ||||
| {{end}} | {{end}} | ||||
| <div class="form-group {{if .Err_Username}}has-error has-feedback{{end}}"> | |||||
| <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | |||||
| <label class="col-md-4 control-label">Username: </label> | <label class="col-md-4 control-label">Username: </label> | ||||
| <div class="col-md-6"> | <div class="col-md-6"> | ||||
| <input name="username" class="form-control" placeholder="Type your username" value="{{.username}}"> | <input name="username" class="form-control" placeholder="Type your username" value="{{.username}}"> | ||||
| @@ -25,7 +25,7 @@ | |||||
| <input name="passwd" type="password" class="form-control" placeholder="Type your password"> | <input name="passwd" type="password" class="form-control" placeholder="Type your password"> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | |||||
| <div class="form-group {{if .Err_RetypePasswd}}has-error has-feedback{{end}}"> | |||||
| <label class="col-md-4 control-label">Re-type: </label> | <label class="col-md-4 control-label">Re-type: </label> | ||||
| <div class="col-md-6"> | <div class="col-md-6"> | ||||
| <input type="password" class="form-control" placeholder="Re-type your password"> | <input type="password" class="form-control" placeholder="Re-type your password"> | ||||