mirror of https://github.com/gogs/gogs.git
打入补丁:9F1436E39C95D59E.patch
parent
040a86d8c0
commit
5dc50b297f
1
go.mod
1
go.mod
|
@ -73,6 +73,7 @@ require (
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.12.0 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/djherbis/buffer v1.2.0 // indirect
|
||||
github.com/djherbis/nio/v3 v3.0.1 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -58,6 +58,8 @@ github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58s
|
|||
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
|
||||
github.com/derision-test/go-mockgen v1.3.7 h1:b/DXAXL2FkaRPpnbYK3ODdZzklmJAwox0tkc6yyXx74=
|
||||
github.com/derision-test/go-mockgen v1.3.7/go.mod h1:/TXUePlhtHmDDCaDAi/a4g6xOHqMDz3Wf0r2NPGskB4=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/djherbis/buffer v1.1.0/go.mod h1:VwN8VdFkMY0DCALdY8o00d3IZ6Amz/UNVMWcSaJT44o=
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/email"
|
||||
"gogs.io/gogs/internal/markup"
|
||||
"gogs.io/gogs/internal/userutil"
|
||||
)
|
||||
|
||||
func (issue *Issue) MailSubject() string {
|
||||
|
@ -43,16 +42,6 @@ func (this mailerUser) PublicEmail() string {
|
|||
return this.user.PublicEmail
|
||||
}
|
||||
|
||||
func (this mailerUser) GenerateEmailActivateCode(email string) string {
|
||||
return userutil.GenerateActivateCode(
|
||||
this.user.ID,
|
||||
email,
|
||||
this.user.Name,
|
||||
this.user.Password,
|
||||
this.user.Rands,
|
||||
)
|
||||
}
|
||||
|
||||
func NewMailerUser(u *User) email.User {
|
||||
return mailerUser{u}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2022 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.gogs file.
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2015 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.gogs file.
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
|
@ -52,22 +52,22 @@ var migrations = []Migration{
|
|||
// v18 -> v19:v0.11.55
|
||||
// NewMigration("clean unlinked webhook and hook_tasks", cleanUnlinkedWebhookAndHookTasks),
|
||||
|
||||
// v19 -> v20:v0.13.0
|
||||
// v19 -> v20:v0.13.0(Gogs)
|
||||
NewMigration("migrate access tokens to store SHA56", migrateAccessTokenToSHA256),
|
||||
// v20 -> v21:v0.13.0
|
||||
// v20 -> v21:v0.13.0(Gogs)
|
||||
NewMigration("add index to action.user_id", addIndexToActionUserID),
|
||||
// v21 -> v22:v0.13.0
|
||||
// v21 -> v22:v0.13.0(Gogs)
|
||||
//
|
||||
// NOTE: There was a bug in calculating the value of the `version.version`
|
||||
// column after a migration is done, thus some instances are on v21 but some are
|
||||
// on v22. Let's make a noop v22 to make sure every instance will not miss a
|
||||
// real future migration.
|
||||
NewMigration("noop", func(*gorm.DB) error { return nil }),
|
||||
// v22 -> v23:v0.14.0
|
||||
// v22 -> v23:v0.14.0(Gogs)
|
||||
NewMigration("add user.public_email column", addUserPublicEmail),
|
||||
// v23 -> v24:v0.14.0
|
||||
// v23 -> v24:v0.14.0(Gogs)
|
||||
NewMigration("add user.local_email column", addUserLocalEmail),
|
||||
// v24 -> v25:v0.14.0
|
||||
// v24 -> v25:v0.14.0(Gogs) v24:v1.0.0(Gogs)
|
||||
NewMigration("insert user primary to database", insertUserPrimaryEmail),
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2022 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.gogs file.
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2022 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.gogs file.
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2022 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.gogs file.
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2022 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.gogs file.
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func addUserPublicEmail(db *gorm.DB) error {
|
||||
type User struct {
|
||||
PublicEmail string // 不能使用NOT NULL
|
||||
}
|
||||
|
||||
type UserNotNull struct {
|
||||
PublicEmail string `xorm:"NOT NULL" gorm:"not null"`
|
||||
}
|
||||
|
||||
|
@ -16,12 +21,17 @@ func addUserPublicEmail(db *gorm.DB) error {
|
|||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
err := tx.Migrator().AddColumn(&User{}, "PublicEmail")
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("add column user.public_email error: %s", err.Error())
|
||||
}
|
||||
|
||||
err = tx.Exec("UPDATE `user` SET `public_email` = `email` WHERE `public_email` = '' AND `type` = 0").Error
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("update public_email error: %s", err.Error())
|
||||
}
|
||||
|
||||
err = tx.Debug().Migrator().AlterColumn(&UserNotNull{}, "PublicEmail")
|
||||
if err != nil {
|
||||
return fmt.Errorf("alter column user.public_email error: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"fmt"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func addUserLocalEmail(db *gorm.DB) error {
|
||||
type User struct {
|
||||
ID int64 `gorm:"primaryKey"`
|
||||
LocalEmail string `xorm:"NOT NULL" gorm:"not null"`
|
||||
ID int64 `gorm:"primaryKey"`
|
||||
LocalEmail string
|
||||
}
|
||||
|
||||
type UserNotNULL struct {
|
||||
ID int64 `gorm:"primaryKey"`
|
||||
LocalEmail string
|
||||
}
|
||||
|
||||
if db.Migrator().HasColumn(&User{}, "LocalEmail") {
|
||||
|
@ -19,7 +24,7 @@ func addUserLocalEmail(db *gorm.DB) error {
|
|||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
err := tx.Migrator().AddColumn(&User{}, "LocalEmail")
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("add column user.local_email error: %s", err.Error())
|
||||
}
|
||||
|
||||
const limit = 100
|
||||
|
@ -27,14 +32,14 @@ func addUserLocalEmail(db *gorm.DB) error {
|
|||
var res []User
|
||||
err := tx.Table("user").Where("type = ?", 0).Where("local_email = ''").Limit(limit).Find(&res).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "query user")
|
||||
return fmt.Errorf("query user error: %s", err.Error())
|
||||
}
|
||||
|
||||
for _, r := range res {
|
||||
r.LocalEmail = gouuid.NewV4().String() + "@fake.localhost"
|
||||
err = tx.Save(&r).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "save user")
|
||||
return fmt.Errorf("save column user.local_email error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,6 +48,11 @@ func addUserLocalEmail(db *gorm.DB) error {
|
|||
}
|
||||
}
|
||||
|
||||
err = tx.Migrator().AlterColumn(&User{}, "LocalEmail")
|
||||
if err != nil {
|
||||
return fmt.Errorf("alter column user.local_email error: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
@ -26,7 +26,7 @@ func insertUserPrimaryEmail(db *gorm.DB) error {
|
|||
var res []User
|
||||
err := tx.Table("user").Where("type = ?", 0).Offset(offset).Limit(limit).Find(&res).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "query user")
|
||||
return fmt.Errorf("query user error: %s", err.Error())
|
||||
}
|
||||
|
||||
for _, r := range res {
|
||||
|
@ -37,7 +37,7 @@ func insertUserPrimaryEmail(db *gorm.DB) error {
|
|||
}
|
||||
err := tx.Table("email_address").Where("uid = ? AND email = ?", record.UserID, record.Email).FirstOrCreate(record).Error
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "insert email")
|
||||
return fmt.Errorf("insert email error: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// 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.gogs file.
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package database
|
||||
|
||||
|
|
|
@ -1149,6 +1149,7 @@ func (s *UsersStore) Active(ctx context.Context, userID int64) error {
|
|||
|
||||
user.UpdatedUnix = s.db.NowFunc().Unix()
|
||||
user.Rands = rands
|
||||
user.IsActive = true
|
||||
|
||||
err = tx.Save(user).Error
|
||||
if err != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ package email
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"gogs.io/gogs/internal/tool"
|
||||
"html/template"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
@ -88,7 +89,6 @@ type User interface {
|
|||
DisplayName() string
|
||||
Email() string
|
||||
PublicEmail() string
|
||||
GenerateEmailActivateCode(string) string
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
|
@ -123,19 +123,37 @@ func SendUserMail(_ *macaron.Context, u User, tpl, code, subject, info string) {
|
|||
}
|
||||
|
||||
func SendActivateAccountMail(c *macaron.Context, u User) {
|
||||
SendUserMail(c, u, MAIL_AUTH_ACTIVATE, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.activate_account"), "activate account")
|
||||
token, err := tool.NewClaims(u.ID(), u.Email(), tool.SubjectActiveAccount).ToToken()
|
||||
if err != nil {
|
||||
log.Error("Create token error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
SendUserMail(c, u, MAIL_AUTH_ACTIVATE, token, c.Tr("mail.activate_account"), "activate account")
|
||||
}
|
||||
|
||||
func SendResetPasswordMail(c *macaron.Context, u User) {
|
||||
SendUserMail(c, u, MAIL_AUTH_RESET_PASSWORD, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.reset_password"), "reset password")
|
||||
token, err := tool.NewClaims(u.ID(), u.Email(), tool.SubjectForgetPasswd).ToToken()
|
||||
if err != nil {
|
||||
log.Error("Create token error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
SendUserMail(c, u, MAIL_AUTH_RESET_PASSWORD, token, c.Tr("mail.reset_password"), "reset password")
|
||||
}
|
||||
|
||||
// SendActivateAccountMail sends confirmation email.
|
||||
func SendActivateEmailMail(c *macaron.Context, u User, email string) {
|
||||
token, err := tool.NewClaims(u.ID(), email, tool.SubjectActiveEmail).ToToken()
|
||||
if err != nil {
|
||||
log.Error("Create token error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
data := map[string]any{
|
||||
"Username": u.DisplayName(),
|
||||
"ActiveCodeLives": conf.Auth.ActivateCodeLives / 60,
|
||||
"Code": u.GenerateEmailActivateCode(email),
|
||||
"Code": token,
|
||||
"Email": email,
|
||||
}
|
||||
body, err := render(MAIL_AUTH_ACTIVATE_EMAIL, data)
|
||||
|
|
|
@ -6,13 +6,10 @@ package user
|
|||
|
||||
import (
|
||||
gocontext "context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/go-macaron/captcha"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/go-macaron/captcha"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"gogs.io/gogs/internal/auth"
|
||||
|
@ -395,66 +392,51 @@ func SignUpPost(c *context.Context, cpt *captcha.Captcha, f form.Register) {
|
|||
c.RedirectSubpath("/user/login")
|
||||
}
|
||||
|
||||
// parseUserFromCode returns user by username encoded in code.
|
||||
// It returns nil if code or username is invalid.
|
||||
func parseUserFromCode(code string) (user *database.User) {
|
||||
if len(code) <= tool.TIME_LIMIT_CODE_LENGTH {
|
||||
// verify active code when active account
|
||||
func verifyUserActiveCode(code string) (user *database.User) {
|
||||
data, err := tool.ParseToken(code)
|
||||
if err != nil || data.Valid() != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use tail hex username to query user
|
||||
hexStr := code[tool.TIME_LIMIT_CODE_LENGTH:]
|
||||
if b, err := hex.DecodeString(hexStr); err == nil {
|
||||
if user, err = database.Handle.Users().GetByUsername(gocontext.TODO(), string(b)); user != nil {
|
||||
return user
|
||||
} else if !database.IsErrUserNotExist(err) {
|
||||
log.Error("Failed to get user by name %q: %v", string(b), err)
|
||||
if user, err = database.Handle.Users().GetByID(gocontext.TODO(), data.Id); err != nil {
|
||||
if !database.IsErrUserNotExist(err) {
|
||||
log.Error("Failed to get user by id %d: %v", data.Id, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// verify active code when active account
|
||||
func verifyUserActiveCode(code string) (user *database.User) {
|
||||
minutes := conf.Auth.ActivateCodeLives
|
||||
|
||||
if user = parseUserFromCode(code); user != nil {
|
||||
// time limit code
|
||||
prefix := code[:tool.TIME_LIMIT_CODE_LENGTH]
|
||||
data := fmt.Sprintf("%d%s%s%s%s", user.ID, user.Email, strings.ToLower(user.Name), user.Password, user.Rands)
|
||||
|
||||
if tool.VerifyTimeLimitCode(data, minutes, prefix) {
|
||||
return user
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return user
|
||||
}
|
||||
|
||||
// verify active code when active account
|
||||
func verifyActiveEmailCode(code, email string) *database.EmailAddress {
|
||||
minutes := conf.Auth.ActivateCodeLives
|
||||
|
||||
if user := parseUserFromCode(code); user != nil {
|
||||
// time limit code
|
||||
prefix := code[:tool.TIME_LIMIT_CODE_LENGTH]
|
||||
data := fmt.Sprintf("%d%s%s%s%s", user.ID, email, strings.ToLower(user.Name), user.Password, user.Rands)
|
||||
|
||||
if tool.VerifyTimeLimitCode(data, minutes, prefix) {
|
||||
emailAddress, err := database.Handle.Users().GetEmail(gocontext.TODO(), user.ID, email, false)
|
||||
if err == nil {
|
||||
return emailAddress
|
||||
}
|
||||
}
|
||||
data, err := tool.ParseToken(code)
|
||||
if err != nil {
|
||||
return nil
|
||||
} else if data.Valid() != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
user, err := database.Handle.Users().GetByID(gocontext.TODO(), data.Id)
|
||||
if err != nil || user == nil {
|
||||
log.Error("Failed to get user by id %d: %v", data.Id, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
emailAddress, err := database.Handle.Users().GetEmail(gocontext.TODO(), user.ID, email, false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return emailAddress
|
||||
}
|
||||
|
||||
func Activate(c *context.Context) {
|
||||
code := c.Query("code")
|
||||
if code == "" {
|
||||
c.Data["IsActivatePage"] = true
|
||||
if c.User.IsActive {
|
||||
if c.User == nil || c.User.IsActive {
|
||||
c.NotFound()
|
||||
return
|
||||
}
|
||||
|
@ -602,25 +584,31 @@ func ResetPasswdPost(c *context.Context) {
|
|||
}
|
||||
c.Data["Code"] = code
|
||||
|
||||
if u := verifyUserActiveCode(code); u != nil {
|
||||
// Validate password length.
|
||||
password := c.Query("password")
|
||||
if len(password) < 6 {
|
||||
c.Data["IsResetForm"] = true
|
||||
c.Data["Err_Password"] = true
|
||||
c.RenderWithErr(c.Tr("auth.password_too_short"), RESET_PASSWORD, nil)
|
||||
return
|
||||
}
|
||||
data, err := tool.ParseToken(code)
|
||||
if err == nil && data.Valid() == nil {
|
||||
user, err := database.Handle.Users().GetByID(gocontext.TODO(), data.Id)
|
||||
if err == nil && user != nil {
|
||||
// Validate password length.
|
||||
password := c.Query("password")
|
||||
if len(password) < 6 {
|
||||
c.Data["IsResetForm"] = true
|
||||
c.Data["Err_Password"] = true
|
||||
c.RenderWithErr(c.Tr("auth.password_too_short"), RESET_PASSWORD, nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := database.Handle.Users().Update(c.Req.Context(), u.ID, database.UpdateUserOptions{Password: &password})
|
||||
if err != nil {
|
||||
c.Error(err, "update user")
|
||||
return
|
||||
}
|
||||
err := database.Handle.Users().Update(c.Req.Context(), user.ID, database.UpdateUserOptions{Password: &password})
|
||||
if err != nil {
|
||||
c.Error(err, "update user")
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("User password reset: %s", u.Name)
|
||||
c.RedirectSubpath("/user/login")
|
||||
return
|
||||
log.Trace("User password reset: %s", user.Name)
|
||||
c.RedirectSubpath("/user/login")
|
||||
return
|
||||
} else if user == nil {
|
||||
log.Error("Failed to get user by id %d: %v", data.Id, err)
|
||||
}
|
||||
}
|
||||
|
||||
c.Data["IsResetFailed"] = true
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package tool
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Subject int
|
||||
|
||||
const (
|
||||
SubjectActiveAccount Subject = 1
|
||||
SubjectActiveEmail Subject = 2
|
||||
SubjectForgetPasswd Subject = 3
|
||||
)
|
||||
|
||||
var secretKey = make([]byte, 32)
|
||||
|
||||
func init() {
|
||||
if _, err := rand.Read(secretKey); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Claims struct {
|
||||
Audience string `json:"aud,omitempty"`
|
||||
ExpiresAt int64 `json:"exp,omitempty"`
|
||||
Id int64 `json:"jti,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
IssuedAt int64 `json:"iat,omitempty"`
|
||||
Issuer string `json:"iss,omitempty"`
|
||||
NotBefore int64 `json:"nbf,omitempty"`
|
||||
Subject Subject `json:"sub,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Claims) Valid() error {
|
||||
now := time.Now()
|
||||
|
||||
if now.After(time.Unix(c.ExpiresAt, 0)) {
|
||||
return fmt.Errorf("error")
|
||||
}
|
||||
|
||||
if now.Before(time.Unix(c.NotBefore, 0)) {
|
||||
return fmt.Errorf("error")
|
||||
}
|
||||
|
||||
if now.Before(time.Unix(c.IssuedAt, 0)) {
|
||||
return fmt.Errorf("error")
|
||||
}
|
||||
|
||||
if c.Audience != c.Email {
|
||||
return fmt.Errorf("error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewClaims(id int64, email string, subject Subject) *Claims {
|
||||
now := time.Now()
|
||||
return &Claims{
|
||||
Audience: email,
|
||||
ExpiresAt: now.Add(time.Duration(conf.Auth.ActivateCodeLives) * time.Minute).Unix(),
|
||||
Id: id,
|
||||
Email: email,
|
||||
IssuedAt: now.Unix(),
|
||||
Issuer: conf.Server.ExternalURL,
|
||||
NotBefore: now.Unix(),
|
||||
Subject: subject,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Claims) ToToken() (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
|
||||
//使用指定的secret签名并获得完成的编码后的字符串token
|
||||
|
||||
return token.SignedString(secretKey)
|
||||
}
|
||||
|
||||
func ParseToken(t string) (*Claims, error) {
|
||||
//解析token
|
||||
token, err := jwt.ParseWithClaims(t, &Claims{}, func(token *jwt.Token) (i interface{}, err error) {
|
||||
return secretKey, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if claims, ok := token.Claims.(*Claims); ok && claims != nil && token.Valid {
|
||||
return claims, nil
|
||||
} else if err := claims.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*Claims); ok && claims != nil && token.Valid {
|
||||
if err := claims.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid token")
|
||||
}
|
|
@ -5,9 +5,7 @@
|
|||
package tool
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"strings"
|
||||
|
@ -62,66 +60,6 @@ func BasicAuthDecode(encoded string) (string, string, error) {
|
|||
return auth[0], auth[1], nil
|
||||
}
|
||||
|
||||
// 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 := com.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, _ := time.ParseInLocation("200601021504", start, time.Local)
|
||||
now := time.Now()
|
||||
if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const TIME_LIMIT_CODE_LENGTH = 12 + 6 + 40
|
||||
|
||||
// CreateTimeLimitCode generates a time limit code based on given input data.
|
||||
// Format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
|
||||
func CreateTimeLimitCode(data string, minutes int, startInf any) string {
|
||||
format := "200601021504"
|
||||
|
||||
var start, end time.Time
|
||||
var startStr, endStr string
|
||||
|
||||
if startInf == nil {
|
||||
// Use now time create code
|
||||
start = time.Now()
|
||||
startStr = start.Format(format)
|
||||
} else {
|
||||
// use start string create code
|
||||
startStr = startInf.(string)
|
||||
start, _ = time.ParseInLocation(format, startStr, time.Local)
|
||||
startStr = start.Format(format)
|
||||
}
|
||||
|
||||
end = start.Add(time.Minute * time.Duration(minutes))
|
||||
endStr = end.Format(format)
|
||||
|
||||
// create sha1 encode string
|
||||
sh := sha1.New()
|
||||
_, _ = sh.Write([]byte(data + conf.Security.SecretKey + startStr + endStr + com.ToStr(minutes)))
|
||||
encoded := hex.EncodeToString(sh.Sum(nil))
|
||||
|
||||
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
|
||||
return code
|
||||
}
|
||||
|
||||
// HashEmail hashes email address to MD5 string.
|
||||
// https://en.gravatar.com/site/implement/hash/
|
||||
func HashEmail(email string) string {
|
||||
|
|
|
@ -8,23 +8,19 @@ import (
|
|||
"bytes"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"image"
|
||||
"image/png"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"gogs.io/gogs/internal/avatar"
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/strutil"
|
||||
"gogs.io/gogs/internal/tool"
|
||||
)
|
||||
|
||||
// DashboardURLPath returns the URL path to the user or organization dashboard.
|
||||
|
@ -35,20 +31,6 @@ func DashboardURLPath(name string, isOrganization bool) string {
|
|||
return conf.Server.Subpath + "/"
|
||||
}
|
||||
|
||||
// GenerateActivateCode generates an activate code based on user information and
|
||||
// the given email.
|
||||
func GenerateActivateCode(userID int64, email, name, password, rands string) string {
|
||||
code := tool.CreateTimeLimitCode(
|
||||
fmt.Sprintf("%d%s%s%s%s", userID, email, strings.ToLower(name), password, rands),
|
||||
conf.Auth.ActivateCodeLives,
|
||||
nil,
|
||||
)
|
||||
|
||||
// Add tailing hex username
|
||||
code += hex.EncodeToString([]byte(strings.ToLower(name)))
|
||||
return code
|
||||
}
|
||||
|
||||
// CustomAvatarPath returns the absolute path of the user custom avatar file.
|
||||
func CustomAvatarPath(userID int64) string {
|
||||
return filepath.Join(conf.Picture.AvatarUploadPath, strconv.FormatInt(userID, 10))
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/osutil"
|
||||
"gogs.io/gogs/internal/tool"
|
||||
"gogs.io/gogs/public"
|
||||
)
|
||||
|
||||
|
@ -32,18 +31,6 @@ func TestDashboardURLPath(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestGenerateActivateCode(t *testing.T) {
|
||||
conf.SetMockAuth(t,
|
||||
conf.AuthOpts{
|
||||
ActivateCodeLives: 10,
|
||||
},
|
||||
)
|
||||
|
||||
code := GenerateActivateCode(1, "alice@example.com", "Alice", "123456", "rands")
|
||||
got := tool.VerifyTimeLimitCode("1alice@example.comalice123456rands", conf.Auth.ActivateCodeLives, code[:tool.TIME_LIMIT_CODE_LENGTH])
|
||||
assert.True(t, got)
|
||||
}
|
||||
|
||||
func TestCustomAvatarPath(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping testing on Windows")
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 58 KiB |
|
@ -5,7 +5,7 @@
|
|||
<div class="ui middle very relaxed page grid">
|
||||
<div class="sixteen wide center aligned centered column">
|
||||
<div class="explore-logo">
|
||||
<img alt="logo" src="{{AppSubURL}}/img/logo-text.png" />
|
||||
<img alt="logo" src="{{AppSubURL}}/img/gogs-hero.png" />
|
||||
</div>
|
||||
<div class="export-hero">
|
||||
<h1>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="ui stackable middle very relaxed page grid">
|
||||
<div class="sixteen wide center aligned centered column">
|
||||
<div class="logo">
|
||||
<img alt="logo" src="{{AppSubURL}}/img/logo-text.png" />
|
||||
<img alt="logo" src="{{AppSubURL}}/img/gogs-hero.png" />
|
||||
</div>
|
||||
<div class="hero">
|
||||
<h2>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="ui stackable middle very relaxed page grid">
|
||||
<div class="sixteen wide center aligned centered column">
|
||||
<div class="dashboard-logo">
|
||||
<img alt="logo" src="{{AppSubURL}}/img/logo-text.png" />
|
||||
<img alt="logo" src="{{AppSubURL}}/img/gogs-hero.png" />
|
||||
</div>
|
||||
<div class="dashboard-hero">
|
||||
<h1>
|
||||
|
|
Loading…
Reference in New Issue