// Copyright 2014 The Gogs Authors. All rights reserved.// Copyright 2019 The Gitea Authors. All rights reserved.// Copyright 2024, 2025 The Forgejo Authors. All rights reserved.// SPDX-License-Identifier: MIT package user import ( "context" "crypto/subtle" "encoding/hex" "errors" "fmt" "net/mail" "net/url" "path/filepath" "regexp" "strings" "time" "unicode" _ "image/jpeg" // Needed for jpeg support "forgejo.org/models/auth" "forgejo.org/models/db" "forgejo.org/modules/auth/openid" "forgejo.org/modules/auth/password/hash" "forgejo.org/modules/base" "forgejo.org/modules/container" "forgejo.org/modules/git" "forgejo.org/modules/log" "forgejo.org/modules/optional" "forgejo.org/modules/setting" "forgejo.org/modules/structs" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" "forgejo.org/modules/validation" "golang.org/x/text/runes" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" "xorm.io/builder") // UserType defines the user typetype UserType int //revive:disable-line:exported const ( // UserTypeIndividual defines an individual user UserTypeIndividual UserType = iota // Historic reason to make it starts at 0. // UserTypeOrganization defines an organization UserTypeOrganization // 1 // UserTypeUserReserved reserves a (non-existing) user, i.e. to prevent a spam user from re-registering after being deleted, or to reserve the name until the user is actually created later on UserTypeUserReserved // 2 // UserTypeOrganizationReserved reserves a (non-existing) organization, to be used in combination with UserTypeUserReserved UserTypeOrganizationReserved // 3 // UserTypeBot defines a bot user UserTypeBot // 4 // UserTypeRemoteUser defines a remote user for federated users UserTypeRemoteUser // 5) const ( // EmailNotificationsEnabled indicates that the user would like to receive all email notifications except your own EmailNotificationsEnabled = "enabled" // EmailNotificationsOnMention indicates that the user would like to be notified via email when mentioned. EmailNotificationsOnMention = "onmention" // EmailNotificationsDisabled indicates that the user would not like to be notified via email. EmailNotificationsDisabled = "disabled" // EmailNotificationsAndYourOwn indicates that the user would like to receive all email notifications and your own EmailNotificationsAndYourOwn = "andyourown") // User represents the object of individual and member of organization.type User struct { ID int64 `xorm:"pk autoincr"` LowerName string `xorm:"UNIQUE NOT NULL"` Name string `xorm:"UNIQUE NOT NULL"` FullName string // Email is the primary email address (to be used for communication) Email string `xorm:"NOT NULL"` KeepEmailPrivate bool EmailNotificationsPreference string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'enabled'"` Passwd string `xorm:"NOT NULL"` PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"` // MustChangePassword is an attribute that determines if a user // is to change their password after registration. MustChangePassword bool `xorm:"NOT NULL DEFAULT false"` LoginType auth.Type LoginSource int64 `xorm:"NOT NULL DEFAULT 0"` LoginName string Type UserType Location string Website string Pronouns string Rands string `xorm:"VARCHAR(32)"` Salt string `xorm:"VARCHAR(32)"` Language string `xorm:"VARCHAR(5)"` Description string CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` LastLoginUnix timeutil.TimeStamp `xorm:"INDEX"` // 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"` // IsActive true: primary email is activated, user can access Web UI and Git SSH. // false: an inactive user can only log in Web UI for account operations (ex: activate the account by email), no other access. IsActive bool `xorm:"INDEX"` // the user is a Gitea admin, who can access all repositories and the admin pages. IsAdmin bool // true: the user is only allowed to see organizations/repositories that they has explicit rights to. // (ex: in private Gitea instances user won't be allowed to see even organizations/repositories that are set as public) IsRestricted bool `xorm:"NOT NULL DEFAULT false"` AllowGitHook bool AllowImportLocal bool // Allow migrate repository by local path AllowCreateOrganization bool `xorm:"DEFAULT true"` // true: the user is not allowed to log in Web UI. Git/SSH access could still be allowed (please refer to Git/SSH access related code/documents) ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"` // Avatar Avatar string `xorm:"VARCHAR(2048) NOT NULL"` AvatarEmail string `xorm:"NOT NULL"` UseCustomAvatar bool // Counters NumFollowers int NumFollowing int `xorm:"NOT NULL DEFAULT 0"` NumStars int NumRepos int // For organization NumTeams int NumMembers int Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"` RepoAdminChangeTeamAccess bool `xorm:"NOT NULL DEFAULT false"` // Preferences DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"` Theme string `xorm:"NOT NULL DEFAULT ''"` KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"` KeepPronounsPrivate bool `xorm:"NOT NULL DEFAULT false"` EnableRepoUnitHints bool `xorm:"NOT NULL DEFAULT true"`} func init() { db.RegisterModel(new(User))} // SearchOrganizationsOptions options to filter organizationstype SearchOrganizationsOptions struct { db.ListOptions All bool} func (u *User) LogString() string { if u == nil { return "<User nil>" } return fmt.Sprintf("<User %d:%s>", u.ID, u.Name)} // BeforeUpdate is invoked from XORM before updating this object.func (u *User) BeforeUpdate() { if u.MaxRepoCreation < -1 { u.MaxRepoCreation = -1 } // Organization does not need email u.Email = strings.ToLower(u.Email) if !u.IsOrganization() { if len(u.AvatarEmail) == 0 { u.AvatarEmail = u.Email } } u.LowerName = strings.ToLower(u.Name) u.Location = base.TruncateString(u.Location, 255) u.Website = base.TruncateString(u.Website, 255) u.Description = base.TruncateString(u.Description, 255)} // AfterLoad is invoked from XORM after filling all the fields of this object.func (u *User) AfterLoad() { if u.Theme == "" { u.Theme = setting.UI.DefaultTheme }} // SetLastLogin set time to last loginfunc (u *User) SetLastLogin() { u.LastLoginUnix = timeutil.TimeStampNow()} // GetPlaceholderEmail returns an noreply emailfunc (u *User) GetPlaceholderEmail() string { return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress)} // GetEmail returns an noreply email, if the user has set to keep his// email address private, otherwise the primary email address.func (u *User) GetEmail() string { if u.KeepEmailPrivate { return u.GetPlaceholderEmail() } return u.Email} // GetAllUsers returns a slice of all individual users found in DB.func GetAllUsers(ctx context.Context) ([]*User, error) { users := make([]*User, 0) return users, db.GetEngine(ctx).OrderBy("id").In("type", UserTypeIndividual, UserTypeRemoteUser).Find(&users)} // GetAllAdmins returns a slice of all adminusers found in DB.func GetAllAdmins(ctx context.Context) ([]*User, error) { users := make([]*User, 0) return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).And("is_admin = ?", true).Find(&users)} // IsLocal returns true if user login type is LoginPlain.func (u *User) IsLocal() bool { return u.LoginType <= auth.Plain} // IsOAuth2 returns true if user login type is LoginOAuth2.func (u *User) IsOAuth2() bool { return u.LoginType == auth.OAuth2} // MaxCreationLimit returns the number of repositories a user is allowed to createfunc (u *User) MaxCreationLimit() int { if u.MaxRepoCreation <= -1 { return setting.Repository.MaxCreationLimit } return u.MaxRepoCreation} // CanCreateRepo returns if user login can create a repository// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revisedfunc (u *User) CanCreateRepo() bool { if u.IsAdmin { return true } if u.MaxRepoCreation <= -1 { if setting.Repository.MaxCreationLimit <= -1 { return true } return u.NumRepos < setting.Repository.MaxCreationLimit } return u.NumRepos < u.MaxRepoCreation} // CanCreateOrganization returns true if user can create organisation.func (u *User) CanCreateOrganization() bool { return u.IsAdmin || (u.AllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation)} // CanEditGitHook returns true if user can edit Git hooks.func (u *User) CanEditGitHook() bool { return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)} // CanForkRepo returns if user login can fork a repository// It checks especially that the user can create repos, and potentially morefunc (u *User) CanForkRepo() bool { if setting.Repository.AllowForkWithoutMaximumLimit { return true } return u.CanCreateRepo()} // CanImportLocal returns true if user can migrate repository by local path.func (u *User) CanImportLocal() bool { if !setting.ImportLocalPaths || u == nil { return false } return u.IsAdmin || u.AllowImportLocal} // DashboardLink returns the user dashboard page link.func (u *User) DashboardLink() string { if u.IsOrganization() { return u.OrganisationLink() + "/dashboard" } return setting.AppSubURL + "/"} // HomeLink returns the user or organization home page link.func (u *User) HomeLink() string { return setting.AppSubURL + "/" + url.PathEscape(u.Name)} // HTMLURL returns the user or organization's full link.func (u *User) HTMLURL() string { return setting.AppURL + url.PathEscape(u.Name)} // OrganisationLink returns the organization sub page link.func (u *User) OrganisationLink() string { return setting.AppSubURL + "/org/" + url.PathEscape(u.Name)} // GenerateEmailAuthorizationCode generates an activation code based for the user for the specified purpose.// The standard expiry is ActiveCodeLives minutes.func (u *User) GenerateEmailAuthorizationCode(ctx context.Context, purpose auth.AuthorizationPurpose) (string, error) { lookup, validator, err := auth.GenerateAuthToken(ctx, u.ID, timeutil.TimeStampNow().Add(int64(setting.Service.ActiveCodeLives)*60), purpose) if err != nil { return "", err } return lookup + ":" + validator, nil} // GetUserFollowers returns range of user's followers.func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { sess := db.GetEngine(ctx). Select("`user`.*"). Join("LEFT", "follow", "`user`.id=follow.user_id"). Where("follow.follow_id=?", u.ID). And("`user`.type=?", UserTypeIndividual). And(isUserVisibleToViewerCond(viewer)) if listOptions.Page > 0 { sess = db.SetSessionPagination(sess, &listOptions) users := make([]*User, 0, listOptions.PageSize) count, err := sess.FindAndCount(&users) return users, count, err } users := make([]*User, 0, 8) count, err := sess.FindAndCount(&users) return users, count, err} // GetUserFollowing returns range of user's following.func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { sess := db.GetEngine(ctx). Select("`user`.*"). Join("LEFT", "follow", "`user`.id=follow.follow_id"). Where("follow.user_id=?", u.ID). And("`user`.type IN (?, ?)", UserTypeIndividual, UserTypeOrganization). And(isUserVisibleToViewerCond(viewer)) if listOptions.Page > 0 { sess = db.SetSessionPagination(sess, &listOptions) users := make([]*User, 0, listOptions.PageSize) count, err := sess.FindAndCount(&users) return users, count, err } users := make([]*User, 0, 8) count, err := sess.FindAndCount(&users) return users, count, err} // NewGitSig generates and returns the signature of given user.func (u *User) NewGitSig() *git.Signature { return &git.Signature{ Name: u.GitName(), Email: u.GetEmail(), When: time.Now(), }} // SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO// change passwd, salt and passwd_hash_algo fieldsfunc (u *User) SetPassword(passwd string) (err error) { // Invalidate all authentication tokens for this user. if err := auth.DeleteAuthTokenByUser(db.DefaultContext, u.ID); err != nil { return err } u.Salt = GetUserSalt() if u.Passwd, err = hash.Parse(setting.PasswordHashAlgo).Hash(passwd, u.Salt); err != nil { return err } u.PasswdHashAlgo = setting.PasswordHashAlgo return nil} // ValidatePassword checks if the given password matches the one belonging to the user.func (u *User) ValidatePassword(passwd string) bool { return hash.Parse(u.PasswdHashAlgo).VerifyPassword(passwd, u.Passwd, u.Salt)} // IsPasswordSet checks if the password is set or left emptyfunc (u *User) IsPasswordSet() bool { return len(u.Passwd) != 0} // IsOrganization returns true if user is actually a organization.func (u *User) IsOrganization() bool { return u.Type == UserTypeOrganization} // IsIndividual returns true if user is actually a individual user.func (u *User) IsIndividual() bool { return u.Type == UserTypeIndividual} func (u *User) IsUser() bool { return u.Type == UserTypeIndividual || u.Type == UserTypeBot} // IsBot returns whether or not the user is of type botfunc (u *User) IsBot() bool { return u.Type == UserTypeBot} func (u *User) IsRemote() bool { return u.Type == UserTypeRemoteUser} // DisplayName returns full name if it's not empty,// returns username otherwise.func (u *User) DisplayName() string { trimmed := strings.TrimSpace(u.FullName) if len(trimmed) > 0 { return trimmed } return u.Name} var emailToReplacer = strings.NewReplacer( "\n", "", "\r", "", "<", "", ">", "", ",", "", ":", "", ";", "",) // EmailTo returns a string suitable to be put into a e-mail `To:` header.func (u *User) EmailTo(overrideMail ...string) string { sanitizedDisplayName := emailToReplacer.Replace(u.DisplayName()) email := u.Email if len(overrideMail) > 0 { email = overrideMail[0] } // should be an edge case but nice to have if sanitizedDisplayName == email { return email } address, err := mail.ParseAddress(fmt.Sprintf("%s <%s>", sanitizedDisplayName, email)) if err != nil { return email } return address.String()} // GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set,// returns username otherwise.func (u *User) GetDisplayName() string { if setting.UI.DefaultShowFullName { trimmed := strings.TrimSpace(u.FullName) if len(trimmed) > 0 { return trimmed } } return u.Name} // GetCompleteName returns the full name and username in the form of// "Full Name (username)" if full name is not empty, otherwise it returns// "username".func (u *User) GetCompleteName() string { trimmedFullName := strings.TrimSpace(u.FullName) if len(trimmedFullName) > 0 { return fmt.Sprintf("%s (%s)", trimmedFullName, u.Name) } return u.Name} // GetPronouns returns an empty string, if the user has set to keep his// pronouns private from non-logged in users, otherwise the pronouns// are returned.func (u *User) GetPronouns(signed bool) string { if u.KeepPronounsPrivate && !signed { return "" }Lines 1–500 of 1346. Load more
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2019 The Gitea Authors. All rights reserved. // Copyright 2024, 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package user import ( "context" "crypto/subtle" "encoding/hex" "errors" "fmt" "net/mail" "net/url" "path/filepath" "regexp" "strings" "time" "unicode" _ "image/jpeg" // Needed for jpeg support "forgejo.org/models/auth" "forgejo.org/models/db" "forgejo.org/modules/auth/openid" "forgejo.org/modules/auth/password/hash" "forgejo.org/modules/base" "forgejo.org/modules/container" "forgejo.org/modules/git" "forgejo.org/modules/log" "forgejo.org/modules/optional" "forgejo.org/modules/setting" "forgejo.org/modules/structs" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" "forgejo.org/modules/validation" "golang.org/x/text/runes" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" "xorm.io/builder" ) // UserType defines the user type type UserType int //revive:disable-line:exported const ( // UserTypeIndividual defines an individual user UserTypeIndividual UserType = iota // Historic reason to make it starts at 0. // UserTypeOrganization defines an organization UserTypeOrganization // 1 // UserTypeUserReserved reserves a (non-existing) user, i.e. to prevent a spam user from re-registering after being deleted, or to reserve the name until the user is actually created later on UserTypeUserReserved // 2 // UserTypeOrganizationReserved reserves a (non-existing) organization, to be used in combination with UserTypeUserReserved UserTypeOrganizationReserved // 3 // UserTypeBot defines a bot user UserTypeBot // 4 // UserTypeRemoteUser defines a remote user for federated users UserTypeRemoteUser // 5 ) const ( // EmailNotificationsEnabled indicates that the user would like to receive all email notifications except your own EmailNotificationsEnabled = "enabled" // EmailNotificationsOnMention indicates that the user would like to be notified via email when mentioned. EmailNotificationsOnMention = "onmention" // EmailNotificationsDisabled indicates that the user would not like to be notified via email. EmailNotificationsDisabled = "disabled" // EmailNotificationsAndYourOwn indicates that the user would like to receive all email notifications and your own EmailNotificationsAndYourOwn = "andyourown" ) // User represents the object of individual and member of organization. type User struct { ID int64 `xorm:"pk autoincr"` LowerName string `xorm:"UNIQUE NOT NULL"` Name string `xorm:"UNIQUE NOT NULL"` FullName string // Email is the primary email address (to be used for communication) Email string `xorm:"NOT NULL"` KeepEmailPrivate bool EmailNotificationsPreference string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'enabled'"` Passwd string `xorm:"NOT NULL"` PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"` // MustChangePassword is an attribute that determines if a user // is to change their password after registration. MustChangePassword bool `xorm:"NOT NULL DEFAULT false"` LoginType auth.Type LoginSource int64 `xorm:"NOT NULL DEFAULT 0"` LoginName string Type UserType Location string Website string Pronouns string Rands string `xorm:"VARCHAR(32)"` Salt string `xorm:"VARCHAR(32)"` Language string `xorm:"VARCHAR(5)"` Description string CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` LastLoginUnix timeutil.TimeStamp `xorm:"INDEX"` // 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"` // IsActive true: primary email is activated, user can access Web UI and Git SSH. // false: an inactive user can only log in Web UI for account operations (ex: activate the account by email), no other access. IsActive bool `xorm:"INDEX"` // the user is a Gitea admin, who can access all repositories and the admin pages. IsAdmin bool // true: the user is only allowed to see organizations/repositories that they has explicit rights to. // (ex: in private Gitea instances user won't be allowed to see even organizations/repositories that are set as public) IsRestricted bool `xorm:"NOT NULL DEFAULT false"` AllowGitHook bool AllowImportLocal bool // Allow migrate repository by local path AllowCreateOrganization bool `xorm:"DEFAULT true"` // true: the user is not allowed to log in Web UI. Git/SSH access could still be allowed (please refer to Git/SSH access related code/documents) ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"` // Avatar Avatar string `xorm:"VARCHAR(2048) NOT NULL"` AvatarEmail string `xorm:"NOT NULL"` UseCustomAvatar bool // Counters NumFollowers int NumFollowing int `xorm:"NOT NULL DEFAULT 0"` NumStars int NumRepos int // For organization NumTeams int NumMembers int Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"` RepoAdminChangeTeamAccess bool `xorm:"NOT NULL DEFAULT false"` // Preferences DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"` Theme string `xorm:"NOT NULL DEFAULT ''"` KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"` KeepPronounsPrivate bool `xorm:"NOT NULL DEFAULT false"` EnableRepoUnitHints bool `xorm:"NOT NULL DEFAULT true"` } func init() { db.RegisterModel(new(User)) } // SearchOrganizationsOptions options to filter organizations type SearchOrganizationsOptions struct { db.ListOptions All bool } func (u *User) LogString() string { if u == nil { return "<User nil>" } return fmt.Sprintf("<User %d:%s>", u.ID, u.Name) } // BeforeUpdate is invoked from XORM before updating this object. func (u *User) BeforeUpdate() { if u.MaxRepoCreation < -1 { u.MaxRepoCreation = -1 } // Organization does not need email u.Email = strings.ToLower(u.Email) if !u.IsOrganization() { if len(u.AvatarEmail) == 0 { u.AvatarEmail = u.Email } } u.LowerName = strings.ToLower(u.Name) u.Location = base.TruncateString(u.Location, 255) u.Website = base.TruncateString(u.Website, 255) u.Description = base.TruncateString(u.Description, 255) } // AfterLoad is invoked from XORM after filling all the fields of this object. func (u *User) AfterLoad() { if u.Theme == "" { u.Theme = setting.UI.DefaultTheme } } // SetLastLogin set time to last login func (u *User) SetLastLogin() { u.LastLoginUnix = timeutil.TimeStampNow() } // GetPlaceholderEmail returns an noreply email func (u *User) GetPlaceholderEmail() string { return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress) } // GetEmail returns an noreply email, if the user has set to keep his // email address private, otherwise the primary email address. func (u *User) GetEmail() string { if u.KeepEmailPrivate { return u.GetPlaceholderEmail() } return u.Email } // GetAllUsers returns a slice of all individual users found in DB. func GetAllUsers(ctx context.Context) ([]*User, error) { users := make([]*User, 0) return users, db.GetEngine(ctx).OrderBy("id").In("type", UserTypeIndividual, UserTypeRemoteUser).Find(&users) } // GetAllAdmins returns a slice of all adminusers found in DB. func GetAllAdmins(ctx context.Context) ([]*User, error) { users := make([]*User, 0) return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).And("is_admin = ?", true).Find(&users) } // IsLocal returns true if user login type is LoginPlain. func (u *User) IsLocal() bool { return u.LoginType <= auth.Plain } // IsOAuth2 returns true if user login type is LoginOAuth2. func (u *User) IsOAuth2() bool { return u.LoginType == auth.OAuth2 } // MaxCreationLimit returns the number of repositories a user is allowed to create func (u *User) MaxCreationLimit() int { if u.MaxRepoCreation <= -1 { return setting.Repository.MaxCreationLimit } return u.MaxRepoCreation } // CanCreateRepo returns if user login can create a repository // NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised func (u *User) CanCreateRepo() bool { if u.IsAdmin { return true } if u.MaxRepoCreation <= -1 { if setting.Repository.MaxCreationLimit <= -1 { return true } return u.NumRepos < setting.Repository.MaxCreationLimit } return u.NumRepos < u.MaxRepoCreation } // CanCreateOrganization returns true if user can create organisation. func (u *User) CanCreateOrganization() bool { return u.IsAdmin || (u.AllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation) } // CanEditGitHook returns true if user can edit Git hooks. func (u *User) CanEditGitHook() bool { return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook) } // CanForkRepo returns if user login can fork a repository // It checks especially that the user can create repos, and potentially more func (u *User) CanForkRepo() bool { if setting.Repository.AllowForkWithoutMaximumLimit { return true } return u.CanCreateRepo() } // CanImportLocal returns true if user can migrate repository by local path. func (u *User) CanImportLocal() bool { if !setting.ImportLocalPaths || u == nil { return false } return u.IsAdmin || u.AllowImportLocal } // DashboardLink returns the user dashboard page link. func (u *User) DashboardLink() string { if u.IsOrganization() { return u.OrganisationLink() + "/dashboard" } return setting.AppSubURL + "/" } // HomeLink returns the user or organization home page link. func (u *User) HomeLink() string { return setting.AppSubURL + "/" + url.PathEscape(u.Name) } // HTMLURL returns the user or organization's full link. func (u *User) HTMLURL() string { return setting.AppURL + url.PathEscape(u.Name) } // OrganisationLink returns the organization sub page link. func (u *User) OrganisationLink() string { return setting.AppSubURL + "/org/" + url.PathEscape(u.Name) } // GenerateEmailAuthorizationCode generates an activation code based for the user for the specified purpose. // The standard expiry is ActiveCodeLives minutes. func (u *User) GenerateEmailAuthorizationCode(ctx context.Context, purpose auth.AuthorizationPurpose) (string, error) { lookup, validator, err := auth.GenerateAuthToken(ctx, u.ID, timeutil.TimeStampNow().Add(int64(setting.Service.ActiveCodeLives)*60), purpose) if err != nil { return "", err } return lookup + ":" + validator, nil } // GetUserFollowers returns range of user's followers. func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { sess := db.GetEngine(ctx). Select("`user`.*"). Join("LEFT", "follow", "`user`.id=follow.user_id"). Where("follow.follow_id=?", u.ID). And("`user`.type=?", UserTypeIndividual). And(isUserVisibleToViewerCond(viewer)) if listOptions.Page > 0 { sess = db.SetSessionPagination(sess, &listOptions) users := make([]*User, 0, listOptions.PageSize) count, err := sess.FindAndCount(&users) return users, count, err } users := make([]*User, 0, 8) count, err := sess.FindAndCount(&users) return users, count, err } // GetUserFollowing returns range of user's following. func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { sess := db.GetEngine(ctx). Select("`user`.*"). Join("LEFT", "follow", "`user`.id=follow.follow_id"). Where("follow.user_id=?", u.ID). And("`user`.type IN (?, ?)", UserTypeIndividual, UserTypeOrganization). And(isUserVisibleToViewerCond(viewer)) if listOptions.Page > 0 { sess = db.SetSessionPagination(sess, &listOptions) users := make([]*User, 0, listOptions.PageSize) count, err := sess.FindAndCount(&users) return users, count, err } users := make([]*User, 0, 8) count, err := sess.FindAndCount(&users) return users, count, err } // NewGitSig generates and returns the signature of given user. func (u *User) NewGitSig() *git.Signature { return &git.Signature{ Name: u.GitName(), Email: u.GetEmail(), When: time.Now(), } } // SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO // change passwd, salt and passwd_hash_algo fields func (u *User) SetPassword(passwd string) (err error) { // Invalidate all authentication tokens for this user. if err := auth.DeleteAuthTokenByUser(db.DefaultContext, u.ID); err != nil { return err } u.Salt = GetUserSalt() if u.Passwd, err = hash.Parse(setting.PasswordHashAlgo).Hash(passwd, u.Salt); err != nil { return err } u.PasswdHashAlgo = setting.PasswordHashAlgo return nil } // ValidatePassword checks if the given password matches the one belonging to the user. func (u *User) ValidatePassword(passwd string) bool { return hash.Parse(u.PasswdHashAlgo).VerifyPassword(passwd, u.Passwd, u.Salt) } // IsPasswordSet checks if the password is set or left empty func (u *User) IsPasswordSet() bool { return len(u.Passwd) != 0 } // IsOrganization returns true if user is actually a organization. func (u *User) IsOrganization() bool { return u.Type == UserTypeOrganization } // IsIndividual returns true if user is actually a individual user. func (u *User) IsIndividual() bool { return u.Type == UserTypeIndividual } func (u *User) IsUser() bool { return u.Type == UserTypeIndividual || u.Type == UserTypeBot } // IsBot returns whether or not the user is of type bot func (u *User) IsBot() bool { return u.Type == UserTypeBot } func (u *User) IsRemote() bool { return u.Type == UserTypeRemoteUser } // DisplayName returns full name if it's not empty, // returns username otherwise. func (u *User) DisplayName() string { trimmed := strings.TrimSpace(u.FullName) if len(trimmed) > 0 { return trimmed } return u.Name } var emailToReplacer = strings.NewReplacer( "\n", "", "\r", "", "<", "", ">", "", ",", "", ":", "", ";", "", ) // EmailTo returns a string suitable to be put into a e-mail `To:` header. func (u *User) EmailTo(overrideMail ...string) string { sanitizedDisplayName := emailToReplacer.Replace(u.DisplayName()) email := u.Email if len(overrideMail) > 0 { email = overrideMail[0] } // should be an edge case but nice to have if sanitizedDisplayName == email { return email } address, err := mail.ParseAddress(fmt.Sprintf("%s <%s>", sanitizedDisplayName, email)) if err != nil { return email } return address.String() } // GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set, // returns username otherwise. func (u *User) GetDisplayName() string { if setting.UI.DefaultShowFullName { trimmed := strings.TrimSpace(u.FullName) if len(trimmed) > 0 { return trimmed } } return u.Name } // GetCompleteName returns the full name and username in the form of // "Full Name (username)" if full name is not empty, otherwise it returns // "username". func (u *User) GetCompleteName() string { trimmedFullName := strings.TrimSpace(u.FullName) if len(trimmedFullName) > 0 { return fmt.Sprintf("%s (%s)", trimmedFullName, u.Name) } return u.Name } // GetPronouns returns an empty string, if the user has set to keep his // pronouns private from non-logged in users, otherwise the pronouns // are returned. func (u *User) GetPronouns(signed bool) string { if u.KeepPronounsPrivate && !signed { return "" }