aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
25 files changed, 297 insertions, 214 deletions
diff --git a/Taskfile.yml b/Taskfile.yml
index 431421b4..6607b8a6 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -5,6 +5,8 @@ vars:
sh: echo '{{if eq OS "windows"}}.exe{{end}}'
tasks:
+ default:
+ deps: [build]
web:
desc: Build the binary and start the web server
deps: [build]
diff --git a/internal/cmd/web.go b/internal/cmd/web.go
index 42e4e065..1689afef 100644
--- a/internal/cmd/web.go
+++ b/internal/cmd/web.go
@@ -99,7 +99,7 @@ func newMacaron() *macaron.Macaron {
conf.Picture.AvatarUploadPath,
macaron.StaticOptions{
ETag: true,
- Prefix: db.USER_AVATAR_URL_PREFIX,
+ Prefix: conf.UsersAvatarURLPath,
SkipLogging: conf.Server.DisableRouterLog,
},
))
diff --git a/internal/conf/mocks.go b/internal/conf/mocks.go
index d4e57d38..af5b1bd7 100644
--- a/internal/conf/mocks.go
+++ b/internal/conf/mocks.go
@@ -16,6 +16,14 @@ func SetMockApp(t *testing.T, opts AppOpts) {
})
}
+func SetMockAuth(t *testing.T, otps AuthOpts) {
+ before := Auth
+ Auth = otps
+ t.Cleanup(func() {
+ Auth = before
+ })
+}
+
func SetMockServer(t *testing.T, opts ServerOpts) {
before := Server
Server = opts
diff --git a/internal/conf/static.go b/internal/conf/static.go
index b0092dde..8132547f 100644
--- a/internal/conf/static.go
+++ b/internal/conf/static.go
@@ -12,6 +12,12 @@ import (
"github.com/gogs/go-libravatar"
)
+const (
+ // UsersAvatarURLPath is used to identify whether a URL is to access user
+ // avatars.
+ UsersAvatarURLPath = "avatars"
+)
+
// ℹ️ README: This file contains static values that should only be set at initialization time.
//
// ⚠️ WARNING: After changing any options, do not forget to update template of
@@ -71,20 +77,6 @@ var (
FromEmail string `ini:"-"` // Parsed email address of From without person's name.
}
- // Authentication settings
- Auth struct {
- ActivateCodeLives int
- ResetPasswordCodeLives int
- RequireEmailConfirmation bool
- RequireSigninView bool
- DisableRegistration bool
- EnableRegistrationCaptcha bool
-
- EnableReverseProxyAuthentication bool
- EnableReverseProxyAutoRegistration bool
- ReverseProxyAuthenticationHeader string
- }
-
// User settings
User struct {
EnableEmailNotification bool
@@ -268,6 +260,22 @@ type AppOpts struct {
// Application settings
var App AppOpts
+type AuthOpts struct {
+ ActivateCodeLives int
+ ResetPasswordCodeLives int
+ RequireEmailConfirmation bool
+ RequireSigninView bool
+ DisableRegistration bool
+ EnableRegistrationCaptcha bool
+
+ EnableReverseProxyAuthentication bool
+ EnableReverseProxyAutoRegistration bool
+ ReverseProxyAuthenticationHeader string
+}
+
+// Authentication settings
+var Auth AuthOpts
+
type ServerOpts struct {
ExternalURL string `ini:"EXTERNAL_URL"`
Domain string
diff --git a/internal/db/issue_mail.go b/internal/db/issue_mail.go
index c9b5471a..91d89a41 100644
--- a/internal/db/issue_mail.go
+++ b/internal/db/issue_mail.go
@@ -13,6 +13,7 @@ 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 {
@@ -36,12 +37,14 @@ func (this mailerUser) Email() string {
return this.user.Email
}
-func (this mailerUser) GenerateActivateCode() string {
- return this.user.GenerateActivateCode()
-}
-
func (this mailerUser) GenerateEmailActivateCode(email string) string {
- return this.user.GenerateEmailActivateCode(email)
+ return userutil.GenerateActivateCode(
+ this.user.ID,
+ email,
+ this.user.Name,
+ this.user.Passwd,
+ this.user.Rands,
+ )
}
func NewMailerUser(u *User) email.User {
diff --git a/internal/db/org.go b/internal/db/org.go
index cf984f14..c5c55f44 100644
--- a/internal/db/org.go
+++ b/internal/db/org.go
@@ -177,7 +177,7 @@ func GetOrgByName(name string) (*User, error) {
}
u := &User{
LowerName: strings.ToLower(name),
- Type: UserOrganization,
+ Type: UserTypeOrganization,
}
has, err := x.Get(u)
if err != nil {
diff --git a/internal/db/repo.go b/internal/db/repo.go
index 94f50dfe..97228015 100644
--- a/internal/db/repo.go
+++ b/internal/db/repo.go
@@ -1157,8 +1157,8 @@ func (err ErrReachLimitOfRepo) Error() string {
// CreateRepository creates a repository for given user or organization.
func CreateRepository(doer, owner *User, opts CreateRepoOptionsLegacy) (_ *Repository, err error) {
- if !owner.CanCreateRepo() {
- return nil, ErrReachLimitOfRepo{Limit: owner.RepoCreationNum()}
+ if !owner.canCreateRepo() {
+ return nil, ErrReachLimitOfRepo{Limit: owner.maxNumRepos()}
}
repo := &Repository{
@@ -2467,8 +2467,8 @@ func HasForkedRepo(ownerID, repoID int64) (*Repository, bool, error) {
// ForkRepository creates a fork of target repository under another user domain.
func ForkRepository(doer, owner *User, baseRepo *Repository, name, desc string) (_ *Repository, err error) {
- if !owner.CanCreateRepo() {
- return nil, ErrReachLimitOfRepo{Limit: owner.RepoCreationNum()}
+ if !owner.canCreateRepo() {
+ return nil, ErrReachLimitOfRepo{Limit: owner.maxNumRepos()}
}
repo := &Repository{
diff --git a/internal/db/user.go b/internal/db/user.go
index 08b7e09e..33b69af1 100644
--- a/internal/db/user.go
+++ b/internal/db/user.go
@@ -27,7 +27,6 @@ import (
"xorm.io/xorm"
"github.com/gogs/git-module"
- api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/avatar"
"gogs.io/gogs/internal/conf"
@@ -37,77 +36,13 @@ import (
"gogs.io/gogs/internal/tool"
)
-// USER_AVATAR_URL_PREFIX is used to identify a URL is to access user avatar.
-const USER_AVATAR_URL_PREFIX = "avatars"
-
-type UserType int
-
-const (
- UserIndividual UserType = iota // Historic reason to make it starts at 0.
- UserOrganization
-)
-
-// User represents the object of individual and member of organization.
-type User struct {
- ID int64 `gorm:"primaryKey"`
- LowerName string `xorm:"UNIQUE NOT NULL" gorm:"unique;not null"`
- Name string `xorm:"UNIQUE NOT NULL" gorm:"not null"`
- FullName string
- // Email is the primary email address (to be used for communication)
- Email string `xorm:"NOT NULL" gorm:"not null"`
- Passwd string `xorm:"NOT NULL" gorm:"not null"`
- LoginSource int64 `xorm:"NOT NULL DEFAULT 0" gorm:"not null;default:0"`
- LoginName string
- Type UserType
- OwnedOrgs []*User `xorm:"-" gorm:"-" json:"-"`
- Orgs []*User `xorm:"-" gorm:"-" json:"-"`
- Repos []*Repository `xorm:"-" gorm:"-" json:"-"`
- Location string
- Website string
- Rands string `xorm:"VARCHAR(10)" gorm:"type:VARCHAR(10)"`
- Salt string `xorm:"VARCHAR(10)" gorm:"type:VARCHAR(10)"`
-
- Created time.Time `xorm:"-" gorm:"-" json:"-"`
- CreatedUnix int64
- Updated time.Time `xorm:"-" gorm:"-" json:"-"`
- UpdatedUnix int64
-
- // Remember visibility choice for convenience, true for private
- LastRepoVisibility bool
- // Maximum repository creation limit, -1 means use global default
- MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1" gorm:"not null;default:-1"`
-
- // Permissions
- IsActive bool // Activate primary email
- IsAdmin bool
- AllowGitHook bool
- AllowImportLocal bool // Allow migrate repository by local path
- ProhibitLogin bool
-
- // Avatar
- Avatar string `xorm:"VARCHAR(2048) NOT NULL" gorm:"type:VARCHAR(2048);not null"`
- AvatarEmail string `xorm:"NOT NULL" gorm:"not null"`
- UseCustomAvatar bool
-
- // Counters
- NumFollowers int
- NumFollowing int `xorm:"NOT NULL DEFAULT 0" gorm:"not null;default:0"`
- NumStars int
- NumRepos int
-
- // For organization
- Description string
- NumTeams int
- NumMembers int
- Teams []*Team `xorm:"-" gorm:"-" json:"-"`
- Members []*User `xorm:"-" gorm:"-" json:"-"`
-}
-
+// TODO(unknwon): Delete me once refactoring is done.
func (u *User) BeforeInsert() {
u.CreatedUnix = time.Now().Unix()
u.UpdatedUnix = u.CreatedUnix
}
+// TODO(unknwon): Refactoring together with methods that do updates.
func (u *User) BeforeUpdate() {
if u.MaxRepoCreation < -1 {
u.MaxRepoCreation = -1
@@ -115,6 +50,7 @@ func (u *User) BeforeUpdate() {
u.UpdatedUnix = time.Now().Unix()
}
+// TODO(unknwon): Delete me once refactoring is done.
func (u *User) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
@@ -124,81 +60,6 @@ func (u *User) AfterSet(colName string, _ xorm.Cell) {
}
}
-func (u *User) APIFormat() *api.User {
- return &api.User{
- ID: u.ID,
- UserName: u.Name,
- Login: u.Name,
- FullName: u.FullName,
- Email: u.Email,
- AvatarUrl: u.AvatarLink(),
- }
-}
-
-func (u *User) RepoCreationNum() int {
- if u.MaxRepoCreation <= -1 {
- return conf.Repository.MaxCreationLimit
- }
- return u.MaxRepoCreation
-}
-
-func (u *User) CanCreateRepo() bool {
- if u.MaxRepoCreation <= -1 {
- if conf.Repository.MaxCreationLimit <= -1 {
- return true
- }
- return u.NumRepos < conf.Repository.MaxCreationLimit
- }
- return u.NumRepos < u.MaxRepoCreation
-}
-
-func (u *User) CanCreateOrganization() bool {
- return !conf.Admin.DisableRegularOrgCreation || u.IsAdmin
-}
-
-// CanEditGitHook returns true if user can edit Git hooks.
-func (u *User) CanEditGitHook() bool {
- return u.IsAdmin || u.AllowGitHook
-}
-
-// CanImportLocal returns true if user can migrate repository by local path.
-func (u *User) CanImportLocal() bool {
- return conf.Repository.EnableLocalPathMigration && (u.IsAdmin || u.AllowImportLocal)
-}
-
-// DashboardLink returns the user dashboard page link.
-func (u *User) DashboardLink() string {
- if u.IsOrganization() {
- return conf.Server.Subpath + "/org/" + u.Name + "/dashboard/"
- }
- return conf.Server.Subpath + "/"
-}
-
-// HomeLink returns the user or organization home page link.
-func (u *User) HomeLink() string {
- return conf.Server.Subpath + "/" + u.Name
-}
-
-func (u *User) HTMLURL() string {
- return conf.Server.ExternalURL + u.Name
-}
-
-// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
-func (u *User) GenerateEmailActivateCode(email string) string {
- code := tool.CreateTimeLimitCode(
- com.ToStr(u.ID)+email+u.LowerName+u.Passwd+u.Rands,
- conf.Auth.ActivateCodeLives, nil)
-
- // Add tail hex username
- code += hex.EncodeToString([]byte(u.LowerName))
- return code
-}
-
-// GenerateActivateCode generates an activate code based on user information.
-func (u *User) GenerateActivateCode() string {
- return u.GenerateEmailActivateCode(u.Email)
-}
-
// CustomAvatarPath returns user custom avatar file path.
func (u *User) CustomAvatarPath() string {
return filepath.Join(conf.Picture.AvatarUploadPath, com.ToStr(u.ID))
@@ -246,7 +107,7 @@ func (u *User) RelAvatarLink() string {
if !com.IsExist(u.CustomAvatarPath()) {
return defaultImgUrl
}
- return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, USER_AVATAR_URL_PREFIX, u.ID)
+ return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, conf.UsersAvatarURLPath, u.ID)
case conf.Picture.DisableGravatar:
if !com.IsExist(u.CustomAvatarPath()) {
if err := u.GenerateRandomAvatar(); err != nil {
@@ -254,7 +115,7 @@ func (u *User) RelAvatarLink() string {
}
}
- return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, USER_AVATAR_URL_PREFIX, u.ID)
+ return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, conf.UsersAvatarURLPath, u.ID)
}
return tool.AvatarLink(u.AvatarEmail)
}
@@ -374,7 +235,7 @@ func (u *User) IsWriterOfRepo(repo *Repository) bool {
// IsOrganization returns true if user is actually a organization.
func (u *User) IsOrganization() bool {
- return u.Type == UserOrganization
+ return u.Type == UserTypeOrganization
}
// IsUserOrgOwner returns true if user is in the owner team of given organization.
@@ -434,7 +295,7 @@ func (u *User) GetOrganizations(showPrivate bool) error {
}
u.Orgs = make([]*User, 0, len(orgIDs))
- if err = x.Where("type = ?", UserOrganization).In("id", orgIDs).Find(&u.Orgs); err != nil {
+ if err = x.Where("type = ?", UserTypeOrganization).In("id", orgIDs).Find(&u.Orgs); err != nil {
return err
}
return nil
diff --git a/internal/db/user_mail.go b/internal/db/user_mail.go
index fc6356fe..ea6c1a73 100644
--- a/internal/db/user_mail.go
+++ b/internal/db/user_mail.go
@@ -69,7 +69,7 @@ func isEmailUsed(e Engine, email string) (bool, error) {
}
// We need to check primary email of users as well.
- return e.Where("type=?", UserIndividual).And("email=?", email).Get(new(User))
+ return e.Where("type=?", UserTypeIndividual).And("email=?", email).Get(new(User))
}
// IsEmailUsed returns true if the email has been used.
diff --git a/internal/db/users.go b/internal/db/users.go
index d52cfc4c..94bc25f5 100644
--- a/internal/db/users.go
+++ b/internal/db/users.go
@@ -11,10 +11,12 @@ import (
"time"
"github.com/go-macaron/binding"
+ api "github.com/gogs/go-gogs-client"
"github.com/pkg/errors"
"gorm.io/gorm"
"gogs.io/gogs/internal/auth"
+ "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/cryptoutil"
"gogs.io/gogs/internal/errutil"
)
@@ -54,27 +56,6 @@ type UsersStore interface {
var Users UsersStore
-// BeforeCreate implements the GORM create hook.
-func (u *User) BeforeCreate(tx *gorm.DB) error {
- if u.CreatedUnix == 0 {
- u.CreatedUnix = tx.NowFunc().Unix()
- u.UpdatedUnix = u.CreatedUnix
- }
- return nil
-}
-
-// AfterFind implements the GORM query hook.
-func (u *User) AfterFind(_ *gorm.DB) error {
- u.Created = time.Unix(u.CreatedUnix, 0).Local()
- u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
- return nil
-}
-
-// IsLocal returns true if user is created as local account.
-func (u *User) IsLocal() bool {
- return u.LoginSource <= 0
-}
-
var _ UsersStore = (*users)(nil)
type users struct {
@@ -303,7 +284,7 @@ func (db *users) GetByEmail(ctx context.Context, email string) (*User, error) {
// First try to find the user by primary email
user := new(User)
err := db.WithContext(ctx).
- Where("email = ? AND type = ? AND is_active = ?", email, UserIndividual, true).
+ Where("email = ? AND type = ? AND is_active = ?", email, UserTypeIndividual, true).
First(user).
Error
if err == nil {
@@ -357,3 +338,147 @@ func (db *users) HasForkedRepository(ctx context.Context, userID, repoID int64)
db.WithContext(ctx).Model(new(Repository)).Where("owner_id = ? AND fork_id = ?", userID, repoID).Count(&count)
return count > 0
}
+
+// UserType indicates the type of the user account.
+type UserType int
+
+const (
+ UserTypeIndividual UserType = iota // NOTE: Historic reason to make it starts at 0.
+ UserTypeOrganization
+)
+
+// User represents the object of an individual or an organization.
+type User struct {
+ ID int64 `gorm:"primaryKey"`
+ LowerName string `xorm:"UNIQUE NOT NULL" gorm:"unique;not null"`
+ Name string `xorm:"UNIQUE NOT NULL" gorm:"not null"`
+ FullName string
+ // Email is the primary email address (to be used for communication)
+ Email string `xorm:"NOT NULL" gorm:"not null"`
+ Passwd string `xorm:"NOT NULL" gorm:"not null"`
+ LoginSource int64 `xorm:"NOT NULL DEFAULT 0" gorm:"not null;default:0"`
+ LoginName string
+ Type UserType
+ OwnedOrgs []*User `xorm:"-" gorm:"-" json:"-"`
+ Orgs []*User `xorm:"-" gorm:"-" json:"-"`
+ Repos []*Repository `xorm:"-" gorm:"-" json:"-"`
+ Location string
+ Website string
+ Rands string `xorm:"VARCHAR(10)" gorm:"type:VARCHAR(10)"`
+ Salt string `xorm:"VARCHAR(10)" gorm:"type:VARCHAR(10)"`
+
+ Created time.Time `xorm:"-" gorm:"-" json:"-"`
+ CreatedUnix int64
+ Updated time.Time `xorm:"-" gorm:"-" json:"-"`
+ UpdatedUnix int64
+
+ // Remember visibility choice for convenience, true for private
+ LastRepoVisibility bool
+ // Maximum repository creation limit, -1 means use global default
+ MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1" gorm:"not null;default:-1"`
+
+ // Permissions
+ IsActive bool // Activate primary email
+ IsAdmin bool
+ AllowGitHook bool
+ AllowImportLocal bool // Allow migrate repository by local path
+ ProhibitLogin bool
+
+ // Avatar
+ Avatar string `xorm:"VARCHAR(2048) NOT NULL" gorm:"type:VARCHAR(2048);not null"`
+ AvatarEmail string `xorm:"NOT NULL" gorm:"not null"`
+ UseCustomAvatar bool
+
+ // Counters
+ NumFollowers int
+ NumFollowing int `xorm:"NOT NULL DEFAULT 0" gorm:"not null;default:0"`
+ NumStars int
+ NumRepos int
+
+ // For organization
+ Description string
+ NumTeams int
+ NumMembers int
+ Teams []*Team `xorm:"-" gorm:"-" json:"-"`
+ Members []*User `xorm:"-" gorm:"-" json:"-"`
+}
+
+// BeforeCreate implements the GORM create hook.
+func (u *User) BeforeCreate(tx *gorm.DB) error {
+ if u.CreatedUnix == 0 {
+ u.CreatedUnix = tx.NowFunc().Unix()
+ u.UpdatedUnix = u.CreatedUnix
+ }
+ return nil
+}
+
+// AfterFind implements the GORM query hook.
+func (u *User) AfterFind(_ *gorm.DB) error {
+ u.Created = time.Unix(u.CreatedUnix, 0).Local()
+ u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
+ return nil
+}
+
+// IsLocal returns true if user is created as local account.
+func (u *User) IsLocal() bool {
+ return u.LoginSource <= 0
+}
+
+// APIFormat returns the API format of a user.
+func (u *User) APIFormat() *api.User {
+ return &api.User{
+ ID: u.ID,
+ UserName: u.Name,
+ Login: u.Name,
+ FullName: u.FullName,
+ Email: u.Email,
+ AvatarUrl: u.AvatarLink(),
+ }
+}
+
+// maxNumRepos returns the maximum number of repositories that the user can have
+// direct ownership.
+func (u *User) maxNumRepos() int {
+ if u.MaxRepoCreation <= -1 {
+ return conf.Repository.MaxCreationLimit
+ }
+ return u.MaxRepoCreation
+}
+
+// canCreateRepo returns true if the user can create a repository.
+func (u *User) canCreateRepo() bool {
+ return u.maxNumRepos() <= -1 || u.NumRepos < u.maxNumRepos()
+}
+
+// CanCreateOrganization returns true if user can create organizations.
+func (u *User) CanCreateOrganization() bool {
+ return !conf.Admin.DisableRegularOrgCreation || u.IsAdmin
+}
+
+// CanEditGitHook returns true if user can edit Git hooks.
+func (u *User) CanEditGitHook() bool {
+ return u.IsAdmin || u.AllowGitHook
+}
+
+// CanImportLocal returns true if user can migrate repositories by local path.
+func (u *User) CanImportLocal() bool {
+ return conf.Repository.EnableLocalPathMigration && (u.IsAdmin || u.AllowImportLocal)
+}
+
+// HomeURLPath returns the URL path to the user or organization home page.
+//
+// TODO(unknwon): This is also used in templates, which should be fixed by
+// having a dedicated type `template.User` and move this to the "userutil"
+// package.
+func (u *User) HomeURLPath() string {
+ return conf.Server.Subpath + "/" + u.Name
+}
+
+// HTMLURL returns the HTML URL to the user or organization home page.
+//
+// TODO(unknwon): This is also used in templates, which should be fixed by
+// having a dedicated type `template.User` and move this to the "userutil"
+// package.
+func (u *User) HTMLURL() string {
+ return conf.Server.ExternalURL + u.Name
+}
diff --git a/internal/db/users_test.go b/internal/db/users_test.go
index 68c1bfdc..67be21dd 100644
--- a/internal/db/users_test.go
+++ b/internal/db/users_test.go
@@ -198,7 +198,7 @@ func usersGetByEmail(t *testing.T, db *users) {
org, err := db.Create(ctx, "gogs", "gogs@exmaple.com", CreateUserOptions{})
require.NoError(t, err)
- err = db.Model(&User{}).Where("id", org.ID).UpdateColumn("type", UserOrganization).Error
+ err = db.Model(&User{}).Where("id", org.ID).UpdateColumn("type", UserTypeOrganization).Error
require.NoError(t, err)
_, err = db.GetByEmail(ctx, org.Email)
diff --git a/internal/email/email.go b/internal/email/email.go
index f63e3c86..107b5e11 100644
--- a/internal/email/email.go
+++ b/internal/email/email.go
@@ -86,7 +86,6 @@ type User interface {
ID() int64
DisplayName() string
Email() string
- GenerateActivateCode() string
GenerateEmailActivateCode(string) string
}
@@ -122,11 +121,11 @@ 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.GenerateActivateCode(), c.Tr("mail.activate_account"), "activate account")
+ SendUserMail(c, u, MAIL_AUTH_ACTIVATE, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.activate_account"), "activate account")
}
func SendResetPasswordMail(c *macaron.Context, u User) {
- SendUserMail(c, u, MAIL_AUTH_RESET_PASSWORD, u.GenerateActivateCode(), c.Tr("mail.reset_password"), "reset password")
+ SendUserMail(c, u, MAIL_AUTH_RESET_PASSWORD, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.reset_password"), "reset password")
}
// SendActivateAccountMail sends confirmation email.
diff --git a/internal/route/admin/orgs.go b/internal/route/admin/orgs.go
index fd034027..06aa74be 100644
--- a/internal/route/admin/orgs.go
+++ b/internal/route/admin/orgs.go
@@ -21,7 +21,7 @@ func Organizations(c *context.Context) {
c.Data["PageIsAdminOrganizations"] = true
route.RenderUserSearch(c, &route.UserSearchOptions{
- Type: db.UserOrganization,
+ Type: db.UserTypeOrganization,
Counter: db.CountOrganizations,
Ranger: db.Organizations,
PageSize: conf.UI.Admin.OrgPagingNum,
diff --git a/internal/route/admin/users.go b/internal/route/admin/users.go
index e4e0a6d6..ef890e7d 100644
--- a/internal/route/admin/users.go
+++ b/internal/route/admin/users.go
@@ -30,7 +30,7 @@ func Users(c *context.Context) {
c.Data["PageIsAdminUsers"] = true
route.RenderUserSearch(c, &route.UserSearchOptions{
- Type: db.UserIndividual,
+ Type: db.UserTypeIndividual,
Counter: db.CountUsers,
Ranger: db.ListUsers,
PageSize: conf.UI.Admin.UserPagingNum,
diff --git a/internal/route/api/v1/org/org.go b/internal/route/api/v1/org/org.go
index 7a7de50e..4d987dbf 100644
--- a/internal/route/api/v1/org/org.go
+++ b/internal/route/api/v1/org/org.go
@@ -27,7 +27,7 @@ func CreateOrgForUser(c *context.APIContext, apiForm api.CreateOrgOption, user *
Website: apiForm.Website,
Location: apiForm.Location,
IsActive: true,
- Type: db.UserOrganization,
+ Type: db.UserTypeOrganization,
}
if err := db.CreateOrganization(org, user); err != nil {
if db.IsErrUserAlreadyExist(err) ||
diff --git a/internal/route/api/v1/user/user.go b/internal/route/api/v1/user/user.go
index c57f8fed..2b26a282 100644
--- a/internal/route/api/v1/user/user.go
+++ b/internal/route/api/v1/user/user.go
@@ -19,7 +19,7 @@ import (
func Search(c *context.APIContext) {
opts := &db.SearchUserOptions{
Keyword: c.Query("q"),
- Type: db.UserIndividual,
+ Type: db.UserTypeIndividual,
PageSize: com.StrTo(c.Query("limit")).MustInt(),
}
if opts.PageSize == 0 {
diff --git a/internal/route/home.go b/internal/route/home.go
index b1e2cc7a..51c7b54b 100644
--- a/internal/route/home.go
+++ b/internal/route/home.go
@@ -138,7 +138,7 @@ func ExploreUsers(c *context.Context) {
c.Data["PageIsExploreUsers"] = true
RenderUserSearch(c, &UserSearchOptions{
- Type: db.UserIndividual,
+ Type: db.UserTypeIndividual,
Counter: db.CountUsers,
Ranger: db.ListUsers,
PageSize: conf.UI.ExplorePagingNum,
@@ -153,7 +153,7 @@ func ExploreOrganizations(c *context.Context) {
c.Data["PageIsExploreOrganizations"] = true
RenderUserSearch(c, &UserSearchOptions{
- Type: db.UserOrganization,
+ Type: db.UserTypeOrganization,
Counter: db.CountOrganizations,
Ranger: db.Organizations,
PageSize: conf.UI.ExplorePagingNum,
diff --git a/internal/route/org/org.go b/internal/route/org/org.go
index 424cbd15..badd0d81 100644
--- a/internal/route/org/org.go
+++ b/internal/route/org/org.go
@@ -32,7 +32,7 @@ func CreatePost(c *context.Context, f form.CreateOrg) {
org := &db.User{
Name: f.OrgName,
IsActive: true,
- Type: db.UserOrganization,
+ Type: db.UserTypeOrganization,
}
if err := db.CreateOrganization(org, c.User); err != nil {
diff --git a/internal/route/repo/pull.go b/internal/route/repo/pull.go
index 68389532..d65b1e10 100644
--- a/internal/route/repo/pull.go
+++ b/internal/route/repo/pull.go
@@ -135,7 +135,7 @@ func ForkPost(c *context.Context, f form.CreateRepo) {
c.Data["Err_RepoName"] = true
switch {
case db.IsErrReachLimitOfRepo(err):
- c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", c.User.RepoCreationNum()), FORK, &f)
+ c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(db.ErrReachLimitOfRepo).Limit), FORK, &f)
case db.IsErrRepoAlreadyExist(err):
c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), FORK, &f)
case db.IsErrNameNotAllowed(err):
diff --git a/internal/route/repo/repo.go b/internal/route/repo/repo.go
index c1fb327b..943540c8 100644
--- a/internal/route/repo/repo.go
+++ b/internal/route/repo/repo.go
@@ -86,10 +86,10 @@ func Create(c *context.Context) {
c.Success(CREATE)
}
-func handleCreateError(c *context.Context, owner *db.User, err error, name, tpl string, form interface{}) {
+func handleCreateError(c *context.Context, err error, name, tpl string, form interface{}) {
switch {
case db.IsErrReachLimitOfRepo(err):
- c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", owner.RepoCreationNum()), tpl, form)
+ c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(db.ErrReachLimitOfRepo).Limit), tpl, form)
case db.IsErrRepoAlreadyExist(err):
c.Data["Err_RepoName"] = true
c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tpl, form)
@@ -141,7 +141,7 @@ func CreatePost(c *context.Context, f form.CreateRepo) {
}
}
- handleCreateError(c, ctxUser, err, "CreatePost", CREATE, &f)
+ handleCreateError(c, err, "CreatePost", CREATE, &f)
}
func Migrate(c *context.Context) {
@@ -227,7 +227,7 @@ func MigratePost(c *context.Context, f form.MigrateRepo) {
return
}
- handleCreateError(c, ctxUser, err, "MigratePost", MIGRATE, &f)
+ handleCreateError(c, err, "MigratePost", MIGRATE, &f)
}
func Action(c *context.Context) {
diff --git a/internal/route/repo/setting.go b/internal/route/repo/setting.go
index 8c706981..a977fcc8 100644
--- a/internal/route/repo/setting.go
+++ b/internal/route/repo/setting.go
@@ -22,6 +22,7 @@ import (
"gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/osutil"
"gogs.io/gogs/internal/tool"
+ "gogs.io/gogs/internal/userutil"
)
const (
@@ -269,7 +270,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
log.Trace("Repository deleted: %s/%s", c.Repo.Owner.Name, repo.Name)
c.Flash.Success(c.Tr("repo.settings.deletion_success"))
- c.Redirect(c.Repo.Owner.DashboardLink())
+ c.Redirect(userutil.DashboardURLPath(c.Repo.Owner.Name, c.Repo.Owner.IsOrganization()))
case "delete-wiki":
if !c.Repo.IsOwner() {
diff --git a/internal/route/user/profile.go b/internal/route/user/profile.go
index 25127028..783fb63c 100644
--- a/internal/route/user/profile.go
+++ b/internal/route/user/profile.go
@@ -118,7 +118,7 @@ func Action(c *context.Context, puser *context.ParamsUser) {
redirectTo := c.Query("redirect_to")
if !tool.IsSameSiteURLPath(redirectTo) {
- redirectTo = puser.HomeLink()
+ redirectTo = puser.HomeURLPath()
}
c.Redirect(redirectTo)
}
diff --git a/internal/userutil/userutil.go b/internal/userutil/userutil.go
new file mode 100644
index 00000000..87b8f15b
--- /dev/null
+++ b/internal/userutil/userutil.go
@@ -0,0 +1,36 @@
+// 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 file.
+
+package userutil
+
+import (
+ "encoding/hex"
+ "fmt"
+ "strings"
+
+ "gogs.io/gogs/internal/conf"
+ "gogs.io/gogs/internal/tool"
+)
+
+// DashboardURLPath returns the URL path to the user or organization dashboard.
+func DashboardURLPath(name string, isOrganization bool) string {
+ if isOrganization {
+ return conf.Server.Subpath + "/org/" + name + "/dashboard/"
+ }
+ return conf.Server.Subpath + "/"
+}
+
+// GenerateActivateCode generates an activate code based on user information and
+// the given email.
+func GenerateActivateCode(id int64, email, name, password, rands string) string {
+ code := tool.CreateTimeLimitCode(
+ fmt.Sprintf("%d%s%s%s%s", id, email, strings.ToLower(name), password, rands),
+ conf.Auth.ActivateCodeLives,
+ nil,
+ )
+
+ // Add tailing hex username
+ code += hex.EncodeToString([]byte(strings.ToLower(name)))
+ return code
+}
diff --git a/internal/userutil/userutil_test.go b/internal/userutil/userutil_test.go
new file mode 100644
index 00000000..a62363a5
--- /dev/null
+++ b/internal/userutil/userutil_test.go
@@ -0,0 +1,40 @@
+// 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 file.
+
+package userutil
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "gogs.io/gogs/internal/conf"
+ "gogs.io/gogs/internal/tool"
+)
+
+func TestDashboardURLPath(t *testing.T) {
+ t.Run("user", func(t *testing.T) {
+ got := DashboardURLPath("alice", false)
+ want := "/"
+ assert.Equal(t, want, got)
+ })
+
+ t.Run("organization", func(t *testing.T) {
+ got := DashboardURLPath("acme", true)
+ want := "/org/acme/dashboard/"
+ assert.Equal(t, want, got)
+ })
+}
+
+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)
+}
diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl
index 285aa12c..9849a804 100644
--- a/templates/user/profile.tmpl
+++ b/templates/user/profile.tmpl
@@ -48,7 +48,7 @@
{{/*
<li>
<i class="octicon octicon-star"></i>
- <a href="{{.Owner.HomeLink}}/stars">
+ <a href="{{.Owner.HomeURLPath}}/stars">
{{.Owner.NumStars}} {{.i18n.Tr "user.starred"}}
</a>
</li>
@@ -56,7 +56,7 @@
{{if .Orgs}}
<li>
{{range .Orgs}}
- <a href="{{.HomeLink}}"><img class="ui mini image poping up" src="{{.RelAvatarLink}}" data-content="{{.Name}}" data-position="top center" data-variation="tiny inverted"></a>
+ <a href="{{.HomeURLPath}}"><img class="ui mini image poping up" src="{{.RelAvatarLink}}" data-content="{{.Name}}" data-position="top center" data-variation="tiny inverted"></a>
{{end}}
</li>
{{end}}