gitea源码

user.go 43KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package user
  5. import (
  6. "context"
  7. "encoding/hex"
  8. "fmt"
  9. "mime"
  10. "net/mail"
  11. "net/url"
  12. "path/filepath"
  13. "regexp"
  14. "strings"
  15. "sync"
  16. "time"
  17. "unicode"
  18. _ "image/jpeg" // Needed for jpeg support
  19. "code.gitea.io/gitea/models/auth"
  20. "code.gitea.io/gitea/models/db"
  21. "code.gitea.io/gitea/modules/auth/openid"
  22. "code.gitea.io/gitea/modules/auth/password/hash"
  23. "code.gitea.io/gitea/modules/base"
  24. "code.gitea.io/gitea/modules/container"
  25. "code.gitea.io/gitea/modules/git"
  26. "code.gitea.io/gitea/modules/httplib"
  27. "code.gitea.io/gitea/modules/log"
  28. "code.gitea.io/gitea/modules/optional"
  29. "code.gitea.io/gitea/modules/setting"
  30. "code.gitea.io/gitea/modules/structs"
  31. "code.gitea.io/gitea/modules/timeutil"
  32. "code.gitea.io/gitea/modules/util"
  33. "code.gitea.io/gitea/modules/validation"
  34. "golang.org/x/text/runes"
  35. "golang.org/x/text/transform"
  36. "golang.org/x/text/unicode/norm"
  37. "xorm.io/builder"
  38. )
  39. // UserType defines the user type
  40. type UserType int //revive:disable-line:exported
  41. const (
  42. // UserTypeIndividual defines an individual user
  43. UserTypeIndividual UserType = iota // Historic reason to make it starts at 0.
  44. // UserTypeOrganization defines an organization
  45. UserTypeOrganization // 1
  46. // 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
  47. UserTypeUserReserved // 2
  48. // UserTypeOrganizationReserved reserves a (non-existing) organization, to be used in combination with UserTypeUserReserved
  49. UserTypeOrganizationReserved // 3
  50. // UserTypeBot defines a bot user
  51. UserTypeBot // 4
  52. // UserTypeRemoteUser defines a remote user for federated users
  53. UserTypeRemoteUser // 5
  54. )
  55. const (
  56. // EmailNotificationsEnabled indicates that the user would like to receive all email notifications except your own
  57. EmailNotificationsEnabled = "enabled"
  58. // EmailNotificationsOnMention indicates that the user would like to be notified via email when mentioned.
  59. EmailNotificationsOnMention = "onmention"
  60. // EmailNotificationsDisabled indicates that the user would not like to be notified via email.
  61. EmailNotificationsDisabled = "disabled"
  62. // EmailNotificationsAndYourOwn indicates that the user would like to receive all email notifications and your own
  63. EmailNotificationsAndYourOwn = "andyourown"
  64. )
  65. // User represents the object of individual and member of organization.
  66. type User struct {
  67. ID int64 `xorm:"pk autoincr"`
  68. LowerName string `xorm:"UNIQUE NOT NULL"`
  69. Name string `xorm:"UNIQUE NOT NULL"`
  70. FullName string
  71. // Email is the primary email address (to be used for communication)
  72. Email string `xorm:"NOT NULL"`
  73. KeepEmailPrivate bool
  74. EmailNotificationsPreference string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'enabled'"`
  75. Passwd string `xorm:"NOT NULL"`
  76. PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"`
  77. // MustChangePassword is an attribute that determines if a user
  78. // is to change their password after registration.
  79. MustChangePassword bool `xorm:"NOT NULL DEFAULT false"`
  80. LoginType auth.Type
  81. LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
  82. LoginName string
  83. Type UserType
  84. Location string
  85. Website string
  86. Rands string `xorm:"VARCHAR(32)"`
  87. Salt string `xorm:"VARCHAR(32)"`
  88. Language string `xorm:"VARCHAR(5)"`
  89. Description string
  90. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  91. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
  92. LastLoginUnix timeutil.TimeStamp `xorm:"INDEX"`
  93. // Remember visibility choice for convenience, true for private
  94. LastRepoVisibility bool
  95. // Maximum repository creation limit, -1 means use global default
  96. MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
  97. // IsActive true: primary email is activated, user can access Web UI and Git SSH.
  98. // false: an inactive user can only log in Web UI for account operations (ex: activate the account by email), no other access.
  99. IsActive bool `xorm:"INDEX"`
  100. // the user is a Gitea admin, who can access all repositories and the admin pages.
  101. IsAdmin bool
  102. // true: the user is only allowed to see organizations/repositories that they has explicit rights to.
  103. // (ex: in private Gitea instances user won't be allowed to see even organizations/repositories that are set as public)
  104. IsRestricted bool `xorm:"NOT NULL DEFAULT false"`
  105. AllowGitHook bool
  106. AllowImportLocal bool // Allow migrate repository by local path
  107. AllowCreateOrganization bool `xorm:"DEFAULT true"`
  108. // 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)
  109. ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"`
  110. // Avatar
  111. Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
  112. AvatarEmail string `xorm:"NOT NULL"`
  113. UseCustomAvatar bool
  114. // Counters
  115. NumFollowers int
  116. NumFollowing int `xorm:"NOT NULL DEFAULT 0"`
  117. NumStars int
  118. NumRepos int
  119. // For organization
  120. NumTeams int
  121. NumMembers int
  122. Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"`
  123. RepoAdminChangeTeamAccess bool `xorm:"NOT NULL DEFAULT false"`
  124. // Preferences
  125. DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
  126. Theme string `xorm:"NOT NULL DEFAULT ''"`
  127. KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"`
  128. }
  129. // Meta defines the meta information of a user, to be stored in the K/V table
  130. type Meta struct {
  131. // Store the initial registration of the user, to aid in spam prevention
  132. // Ensure that one IP isn't creating many accounts (following mediawiki approach)
  133. InitialIP string
  134. InitialUserAgent string
  135. }
  136. func init() {
  137. db.RegisterModel(new(User))
  138. }
  139. // SearchOrganizationsOptions options to filter organizations
  140. type SearchOrganizationsOptions struct {
  141. db.ListOptions
  142. All bool
  143. }
  144. func (u *User) LogString() string {
  145. if u == nil {
  146. return "<User nil>"
  147. }
  148. return fmt.Sprintf("<User %d:%s>", u.ID, u.Name)
  149. }
  150. // BeforeUpdate is invoked from XORM before updating this object.
  151. func (u *User) BeforeUpdate() {
  152. if u.MaxRepoCreation < -1 {
  153. u.MaxRepoCreation = -1
  154. }
  155. // FIXME: this email doesn't need to be in lowercase, because the emails are mainly managed by the email table with lower_email field
  156. // This trick could be removed in new releases to display the user inputed email as-is.
  157. u.Email = strings.ToLower(u.Email)
  158. if !u.IsOrganization() {
  159. if len(u.AvatarEmail) == 0 {
  160. u.AvatarEmail = u.Email
  161. }
  162. }
  163. u.LowerName = strings.ToLower(u.Name)
  164. u.Location = util.TruncateRunes(u.Location, 255)
  165. u.Website = util.TruncateRunes(u.Website, 255)
  166. u.Description = util.TruncateRunes(u.Description, 255)
  167. }
  168. // AfterLoad is invoked from XORM after filling all the fields of this object.
  169. func (u *User) AfterLoad() {
  170. if u.Theme == "" {
  171. u.Theme = setting.UI.DefaultTheme
  172. }
  173. }
  174. // SetLastLogin set time to last login
  175. func (u *User) SetLastLogin() {
  176. u.LastLoginUnix = timeutil.TimeStampNow()
  177. }
  178. // GetPlaceholderEmail returns an noreply email
  179. func (u *User) GetPlaceholderEmail() string {
  180. return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress)
  181. }
  182. // GetEmail returns a noreply email, if the user has set to keep his
  183. // email address private, otherwise the primary email address.
  184. func (u *User) GetEmail() string {
  185. if u.KeepEmailPrivate {
  186. return u.GetPlaceholderEmail()
  187. }
  188. return u.Email
  189. }
  190. // GetAllUsers returns a slice of all individual users found in DB.
  191. func GetAllUsers(ctx context.Context) ([]*User, error) {
  192. users := make([]*User, 0)
  193. return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users)
  194. }
  195. // IsLocal returns true if user login type is LoginPlain.
  196. func (u *User) IsLocal() bool {
  197. return u.LoginType <= auth.Plain
  198. }
  199. // IsOAuth2 returns true if user login type is LoginOAuth2.
  200. func (u *User) IsOAuth2() bool {
  201. return u.LoginType == auth.OAuth2
  202. }
  203. // MaxCreationLimit returns the number of repositories a user is allowed to create
  204. func (u *User) MaxCreationLimit() int {
  205. if u.MaxRepoCreation <= -1 {
  206. return setting.Repository.MaxCreationLimit
  207. }
  208. return u.MaxRepoCreation
  209. }
  210. // CanCreateRepoIn checks whether the doer(u) can create a repository in the owner
  211. // NOTE: functions calling this assume a failure due to repository count limit; it ONLY checks the repo number LIMIT, if new checks are added, those functions should be revised
  212. func (u *User) CanCreateRepoIn(owner *User) bool {
  213. if u.IsAdmin {
  214. return true
  215. }
  216. const noLimit = -1
  217. if owner.MaxRepoCreation == noLimit {
  218. if setting.Repository.MaxCreationLimit == noLimit {
  219. return true
  220. }
  221. return owner.NumRepos < setting.Repository.MaxCreationLimit
  222. }
  223. return owner.NumRepos < owner.MaxRepoCreation
  224. }
  225. // CanCreateOrganization returns true if user can create organisation.
  226. func (u *User) CanCreateOrganization() bool {
  227. return u.IsAdmin || (u.AllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation)
  228. }
  229. // CanEditGitHook returns true if user can edit Git hooks.
  230. func (u *User) CanEditGitHook() bool {
  231. return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)
  232. }
  233. // CanForkRepoIn ONLY checks repository count limit
  234. func (u *User) CanForkRepoIn(owner *User) bool {
  235. if setting.Repository.AllowForkWithoutMaximumLimit {
  236. return true
  237. }
  238. return u.CanCreateRepoIn(owner)
  239. }
  240. // CanImportLocal returns true if user can migrate repository by local path.
  241. func (u *User) CanImportLocal() bool {
  242. if !setting.ImportLocalPaths || u == nil {
  243. return false
  244. }
  245. return u.IsAdmin || u.AllowImportLocal
  246. }
  247. // DashboardLink returns the user dashboard page link.
  248. func (u *User) DashboardLink() string {
  249. if u.IsOrganization() {
  250. return u.OrganisationLink() + "/dashboard"
  251. }
  252. return setting.AppSubURL + "/"
  253. }
  254. // HomeLink returns the user or organization home page link.
  255. func (u *User) HomeLink() string {
  256. return setting.AppSubURL + "/" + url.PathEscape(u.Name)
  257. }
  258. // HTMLURL returns the user or organization's full link.
  259. func (u *User) HTMLURL(ctx context.Context) string {
  260. return httplib.MakeAbsoluteURL(ctx, u.HomeLink())
  261. }
  262. // OrganisationLink returns the organization sub page link.
  263. func (u *User) OrganisationLink() string {
  264. return setting.AppSubURL + "/org/" + url.PathEscape(u.Name)
  265. }
  266. // GetUserFollowers returns range of user's followers.
  267. func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) {
  268. sess := db.GetEngine(ctx).
  269. Select("`user`.*").
  270. Join("LEFT", "follow", "`user`.id=follow.user_id").
  271. Where("follow.follow_id=?", u.ID).
  272. And("`user`.type=?", UserTypeIndividual).
  273. And(isUserVisibleToViewerCond(viewer))
  274. if listOptions.Page > 0 {
  275. sess = db.SetSessionPagination(sess, &listOptions)
  276. users := make([]*User, 0, listOptions.PageSize)
  277. count, err := sess.FindAndCount(&users)
  278. return users, count, err
  279. }
  280. users := make([]*User, 0, 8)
  281. count, err := sess.FindAndCount(&users)
  282. return users, count, err
  283. }
  284. // GetUserFollowing returns range of user's following.
  285. func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) {
  286. sess := db.GetEngine(ctx).
  287. Select("`user`.*").
  288. Join("LEFT", "follow", "`user`.id=follow.follow_id").
  289. Where("follow.user_id=?", u.ID).
  290. And("`user`.type IN (?, ?)", UserTypeIndividual, UserTypeOrganization).
  291. And(isUserVisibleToViewerCond(viewer))
  292. if listOptions.Page > 0 {
  293. sess = db.SetSessionPagination(sess, &listOptions)
  294. users := make([]*User, 0, listOptions.PageSize)
  295. count, err := sess.FindAndCount(&users)
  296. return users, count, err
  297. }
  298. users := make([]*User, 0, 8)
  299. count, err := sess.FindAndCount(&users)
  300. return users, count, err
  301. }
  302. // NewGitSig generates and returns the signature of given user.
  303. func (u *User) NewGitSig() *git.Signature {
  304. return &git.Signature{
  305. Name: u.GitName(),
  306. Email: u.GetEmail(),
  307. When: time.Now(),
  308. }
  309. }
  310. // SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO
  311. // change passwd, salt and passwd_hash_algo fields
  312. func (u *User) SetPassword(passwd string) (err error) {
  313. if u.Salt, err = GetUserSalt(); err != nil {
  314. return err
  315. }
  316. if u.Passwd, err = hash.Parse(setting.PasswordHashAlgo).Hash(passwd, u.Salt); err != nil {
  317. return err
  318. }
  319. u.PasswdHashAlgo = setting.PasswordHashAlgo
  320. return nil
  321. }
  322. // ValidatePassword checks if the given password matches the one belonging to the user.
  323. func (u *User) ValidatePassword(passwd string) bool {
  324. return hash.Parse(u.PasswdHashAlgo).VerifyPassword(passwd, u.Passwd, u.Salt)
  325. }
  326. // IsPasswordSet checks if the password is set or left empty
  327. // TODO: It's better to clarify the "password" behavior for different types (individual, bot)
  328. func (u *User) IsPasswordSet() bool {
  329. return u.Passwd != ""
  330. }
  331. // IsOrganization returns true if user is actually an organization.
  332. func (u *User) IsOrganization() bool {
  333. return u.Type == UserTypeOrganization
  334. }
  335. // IsIndividual returns true if user is actually a individual user.
  336. func (u *User) IsIndividual() bool {
  337. return u.Type == UserTypeIndividual
  338. }
  339. // IsTypeBot returns whether the user is of type bot
  340. func (u *User) IsTypeBot() bool {
  341. return u.Type == UserTypeBot
  342. }
  343. // IsTokenAccessAllowed returns whether the user is an individual or a bot (which allows for token access)
  344. func (u *User) IsTokenAccessAllowed() bool {
  345. return u.Type == UserTypeIndividual || u.Type == UserTypeBot
  346. }
  347. // DisplayName returns full name if it's not empty,
  348. // returns username otherwise.
  349. func (u *User) DisplayName() string {
  350. trimmed := strings.TrimSpace(u.FullName)
  351. if len(trimmed) > 0 {
  352. return trimmed
  353. }
  354. return u.Name
  355. }
  356. // EmailTo returns a string suitable to be put into a e-mail `To:` header.
  357. func (u *User) EmailTo() string {
  358. sanitizedDisplayName := globalVars().emailToReplacer.Replace(u.DisplayName())
  359. // should be an edge case but nice to have
  360. if sanitizedDisplayName == u.Email {
  361. return u.Email
  362. }
  363. to := fmt.Sprintf("%s <%s>", sanitizedDisplayName, u.Email)
  364. add, err := mail.ParseAddress(to)
  365. if err != nil {
  366. return u.Email
  367. }
  368. return fmt.Sprintf("%s <%s>", mime.QEncoding.Encode("utf-8", add.Name), add.Address)
  369. }
  370. // GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set,
  371. // returns username otherwise.
  372. func (u *User) GetDisplayName() string {
  373. if setting.UI.DefaultShowFullName {
  374. trimmed := strings.TrimSpace(u.FullName)
  375. if len(trimmed) > 0 {
  376. return trimmed
  377. }
  378. }
  379. return u.Name
  380. }
  381. // GetCompleteName returns the full name and username in the form of
  382. // "Full Name (username)" if full name is not empty, otherwise it returns
  383. // "username".
  384. func (u *User) GetCompleteName() string {
  385. trimmedFullName := strings.TrimSpace(u.FullName)
  386. if len(trimmedFullName) > 0 {
  387. return fmt.Sprintf("%s (%s)", trimmedFullName, u.Name)
  388. }
  389. return u.Name
  390. }
  391. func gitSafeName(name string) string {
  392. return strings.TrimSpace(strings.NewReplacer("\n", "", "<", "", ">", "").Replace(name))
  393. }
  394. // GitName returns a git safe name
  395. func (u *User) GitName() string {
  396. gitName := gitSafeName(u.FullName)
  397. if len(gitName) > 0 {
  398. return gitName
  399. }
  400. // Although u.Name should be safe if created in our system
  401. // LDAP users may have bad names
  402. gitName = gitSafeName(u.Name)
  403. if len(gitName) > 0 {
  404. return gitName
  405. }
  406. // Totally pathological name so it's got to be:
  407. return fmt.Sprintf("user-%d", u.ID)
  408. }
  409. // ShortName ellipses username to length
  410. func (u *User) ShortName(length int) string {
  411. if setting.UI.DefaultShowFullName && len(u.FullName) > 0 {
  412. return util.EllipsisDisplayString(u.FullName, length)
  413. }
  414. return util.EllipsisDisplayString(u.Name, length)
  415. }
  416. // IsMailable checks if a user is eligible
  417. // to receive emails.
  418. func (u *User) IsMailable() bool {
  419. return u.IsActive
  420. }
  421. // IsUserExist checks if given username exist,
  422. // the username should be non-cased unique.
  423. // If uid is presented, then check will rule out that one,
  424. // it is used when update a username in settings page.
  425. func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
  426. if len(name) == 0 {
  427. return false, nil
  428. }
  429. return db.GetEngine(ctx).
  430. Where("id!=?", uid).
  431. Get(&User{LowerName: strings.ToLower(name)})
  432. }
  433. // SaltByteLength as of the beginning of 2022, it is recommended to use at least
  434. // 64 bits of salt, but NIST is already recommending to use to 128 bits.
  435. // (16 bytes = 16 * 8 = 128 bits)
  436. const SaltByteLength = 16
  437. // GetUserSalt returns a random user salt token.
  438. func GetUserSalt() (string, error) {
  439. rBytes, err := util.CryptoRandomBytes(SaltByteLength)
  440. if err != nil {
  441. return "", err
  442. }
  443. // Returns a 32-byte long string.
  444. return hex.EncodeToString(rBytes), nil
  445. }
  446. type globalVarsStruct struct {
  447. customCharsReplacement *strings.Replacer
  448. removeCharsRE *regexp.Regexp
  449. transformDiacritics transform.Transformer
  450. replaceCharsHyphenRE *regexp.Regexp
  451. emailToReplacer *strings.Replacer
  452. emailRegexp *regexp.Regexp
  453. systemUserNewFuncs map[int64]func() *User
  454. }
  455. var globalVars = sync.OnceValue(func() *globalVarsStruct {
  456. return &globalVarsStruct{
  457. // Note: The set of characters here can safely expand without a breaking change,
  458. // but characters removed from this set can cause user account linking to break
  459. customCharsReplacement: strings.NewReplacer("Æ", "AE"),
  460. removeCharsRE: regexp.MustCompile("['`´]"),
  461. transformDiacritics: transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC),
  462. replaceCharsHyphenRE: regexp.MustCompile(`[\s~+]`),
  463. emailToReplacer: strings.NewReplacer(
  464. "\n", "",
  465. "\r", "",
  466. "<", "",
  467. ">", "",
  468. ",", "",
  469. ":", "",
  470. ";", "",
  471. ),
  472. emailRegexp: regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"),
  473. systemUserNewFuncs: map[int64]func() *User{
  474. GhostUserID: NewGhostUser,
  475. ActionsUserID: NewActionsUser,
  476. },
  477. }
  478. })
  479. // NormalizeUserName only takes the name part if it is an email address, transforms it diacritics to ASCII characters.
  480. // It returns a string with the single-quotes removed, and any other non-supported username characters are replaced with a `-` character
  481. func NormalizeUserName(s string) (string, error) {
  482. vars := globalVars()
  483. s, _, _ = strings.Cut(s, "@")
  484. strDiacriticsRemoved, n, err := transform.String(vars.transformDiacritics, vars.customCharsReplacement.Replace(s))
  485. if err != nil {
  486. return "", fmt.Errorf("failed to normalize the string of provided username %q at position %d", s, n)
  487. }
  488. return vars.replaceCharsHyphenRE.ReplaceAllLiteralString(vars.removeCharsRE.ReplaceAllLiteralString(strDiacriticsRemoved, ""), "-"), nil
  489. }
  490. var (
  491. reservedUsernames = []string{
  492. ".",
  493. "..",
  494. ".well-known",
  495. "api", // gitea api
  496. "metrics", // prometheus metrics api
  497. "v2", // container registry api
  498. "assets", // static asset files
  499. "attachments", // issue attachments
  500. "avatar", // avatar by email hash
  501. "avatars", // user avatars by file name
  502. "repo-avatars",
  503. "captcha",
  504. "login", // oauth2 login
  505. "org", // org create/manage, or "/org/{org}", BUT if an org is named as "invite" then it goes wrong
  506. "repo", // repo create/migrate, etc
  507. "user", // user login/activate/settings, etc
  508. "explore",
  509. "issues",
  510. "pulls",
  511. "milestones",
  512. "notifications",
  513. "favicon.ico",
  514. "manifest.json", // web app manifests
  515. "robots.txt", // search engine robots
  516. "sitemap.xml", // search engine sitemap
  517. "ssh_info", // agit info
  518. "swagger.v1.json",
  519. "ghost", // reserved name for deleted users (id: -1)
  520. "gitea-actions", // gitea builtin user (id: -2)
  521. }
  522. // These names are reserved for user accounts: user's keys, user's rss feed, user's avatar, etc.
  523. // DO NOT add any new stuff! The paths with these names are processed by `/{username}` handler (UsernameSubRoute) manually.
  524. reservedUserPatterns = []string{"*.keys", "*.gpg", "*.rss", "*.atom", "*.png"}
  525. )
  526. // IsUsableUsername returns an error when a username is reserved
  527. func IsUsableUsername(name string) error {
  528. // Validate username make sure it satisfies requirement.
  529. if !validation.IsValidUsername(name) {
  530. // Note: usually this error is normally caught up earlier in the UI
  531. return db.ErrNameCharsNotAllowed{Name: name}
  532. }
  533. return db.IsUsableName(reservedUsernames, reservedUserPatterns, name)
  534. }
  535. // CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation
  536. type CreateUserOverwriteOptions struct {
  537. KeepEmailPrivate optional.Option[bool]
  538. Visibility *structs.VisibleType
  539. AllowCreateOrganization optional.Option[bool]
  540. EmailNotificationsPreference *string
  541. MaxRepoCreation *int
  542. Theme *string
  543. IsRestricted optional.Option[bool]
  544. IsActive optional.Option[bool]
  545. }
  546. // CreateUser creates record of a new user.
  547. func CreateUser(ctx context.Context, u *User, meta *Meta, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
  548. return createUser(ctx, u, meta, false, overwriteDefault...)
  549. }
  550. // AdminCreateUser is used by admins to manually create users
  551. func AdminCreateUser(ctx context.Context, u *User, meta *Meta, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
  552. return createUser(ctx, u, meta, true, overwriteDefault...)
  553. }
  554. // createUser creates record of a new user.
  555. func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
  556. if err = IsUsableUsername(u.Name); err != nil {
  557. return err
  558. }
  559. // set system defaults
  560. u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
  561. u.Visibility = setting.Service.DefaultUserVisibilityMode
  562. u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation
  563. u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
  564. u.MaxRepoCreation = -1
  565. u.Theme = setting.UI.DefaultTheme
  566. u.IsRestricted = setting.Service.DefaultUserIsRestricted
  567. u.IsActive = !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm)
  568. // Ensure consistency of the dates.
  569. if u.UpdatedUnix < u.CreatedUnix {
  570. u.UpdatedUnix = u.CreatedUnix
  571. }
  572. // overwrite defaults if set
  573. if len(overwriteDefault) != 0 && overwriteDefault[0] != nil {
  574. overwrite := overwriteDefault[0]
  575. if overwrite.KeepEmailPrivate.Has() {
  576. u.KeepEmailPrivate = overwrite.KeepEmailPrivate.Value()
  577. }
  578. if overwrite.Visibility != nil {
  579. u.Visibility = *overwrite.Visibility
  580. }
  581. if overwrite.AllowCreateOrganization.Has() {
  582. u.AllowCreateOrganization = overwrite.AllowCreateOrganization.Value()
  583. }
  584. if overwrite.EmailNotificationsPreference != nil {
  585. u.EmailNotificationsPreference = *overwrite.EmailNotificationsPreference
  586. }
  587. if overwrite.MaxRepoCreation != nil {
  588. u.MaxRepoCreation = *overwrite.MaxRepoCreation
  589. }
  590. if overwrite.Theme != nil {
  591. u.Theme = *overwrite.Theme
  592. }
  593. if overwrite.IsRestricted.Has() {
  594. u.IsRestricted = overwrite.IsRestricted.Value()
  595. }
  596. if overwrite.IsActive.Has() {
  597. u.IsActive = overwrite.IsActive.Value()
  598. }
  599. }
  600. // validate data
  601. if err := ValidateUser(u); err != nil {
  602. return err
  603. }
  604. if createdByAdmin {
  605. if err := ValidateEmailForAdmin(u.Email); err != nil {
  606. return err
  607. }
  608. } else {
  609. if err := ValidateEmail(u.Email); err != nil {
  610. return err
  611. }
  612. }
  613. return db.WithTx(ctx, func(ctx context.Context) error {
  614. isExist, err := IsUserExist(ctx, 0, u.Name)
  615. if err != nil {
  616. return err
  617. } else if isExist {
  618. return ErrUserAlreadyExist{u.Name}
  619. }
  620. isExist, err = IsEmailUsed(ctx, u.Email)
  621. if err != nil {
  622. return err
  623. } else if isExist {
  624. return ErrEmailAlreadyUsed{
  625. Email: u.Email,
  626. }
  627. }
  628. // prepare for database
  629. u.LowerName = strings.ToLower(u.Name)
  630. u.AvatarEmail = u.Email
  631. if u.Rands, err = GetUserSalt(); err != nil {
  632. return err
  633. }
  634. if u.Passwd != "" {
  635. if err = u.SetPassword(u.Passwd); err != nil {
  636. return err
  637. }
  638. } else {
  639. u.Salt = ""
  640. u.PasswdHashAlgo = ""
  641. }
  642. // save changes to database
  643. if err = DeleteUserRedirect(ctx, u.Name); err != nil {
  644. return err
  645. }
  646. if u.CreatedUnix == 0 {
  647. // Caller expects auto-time for creation & update timestamps.
  648. err = db.Insert(ctx, u)
  649. } else {
  650. // Caller sets the timestamps themselves. They are responsible for ensuring
  651. // both `CreatedUnix` and `UpdatedUnix` are set appropriately.
  652. _, err = db.GetEngine(ctx).NoAutoTime().Insert(u)
  653. }
  654. if err != nil {
  655. return err
  656. }
  657. if setting.RecordUserSignupMetadata {
  658. // insert initial IP and UserAgent
  659. if err = SetUserSetting(ctx, u.ID, SignupIP, meta.InitialIP); err != nil {
  660. return err
  661. }
  662. // trim user agent string to a reasonable length, if necessary
  663. userAgent := strings.TrimSpace(meta.InitialUserAgent)
  664. if len(userAgent) > 255 {
  665. userAgent = userAgent[:255]
  666. }
  667. if err = SetUserSetting(ctx, u.ID, SignupUserAgent, userAgent); err != nil {
  668. return err
  669. }
  670. }
  671. // insert email address
  672. return db.Insert(ctx, &EmailAddress{
  673. UID: u.ID,
  674. Email: u.Email,
  675. LowerEmail: strings.ToLower(u.Email),
  676. IsActivated: u.IsActive,
  677. IsPrimary: true,
  678. })
  679. })
  680. }
  681. // ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
  682. type ErrDeleteLastAdminUser struct {
  683. UID int64
  684. }
  685. // IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
  686. func IsErrDeleteLastAdminUser(err error) bool {
  687. _, ok := err.(ErrDeleteLastAdminUser)
  688. return ok
  689. }
  690. func (err ErrDeleteLastAdminUser) Error() string {
  691. return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
  692. }
  693. // IsLastAdminUser check whether user is the last admin
  694. func IsLastAdminUser(ctx context.Context, user *User) bool {
  695. if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: optional.Some(true)}) <= 1 {
  696. return true
  697. }
  698. return false
  699. }
  700. // CountUserFilter represent optional filters for CountUsers
  701. type CountUserFilter struct {
  702. LastLoginSince *int64
  703. IsAdmin optional.Option[bool]
  704. IsActive optional.Option[bool]
  705. }
  706. // HasUsers checks whether there are any users in the database, or only one user exists.
  707. func HasUsers(ctx context.Context) (ret struct {
  708. HasAnyUser, HasOnlyOneUser bool
  709. }, err error,
  710. ) {
  711. res, err := db.GetEngine(ctx).Table(&User{}).Cols("id").Limit(2).Query()
  712. if err != nil {
  713. return ret, fmt.Errorf("error checking user existence: %w", err)
  714. }
  715. ret.HasAnyUser = len(res) != 0
  716. ret.HasOnlyOneUser = len(res) == 1
  717. return ret, nil
  718. }
  719. // CountUsers returns number of users.
  720. func CountUsers(ctx context.Context, opts *CountUserFilter) int64 {
  721. return countUsers(ctx, opts)
  722. }
  723. func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
  724. sess := db.GetEngine(ctx)
  725. cond := builder.NewCond()
  726. cond = cond.And(builder.Eq{"type": UserTypeIndividual})
  727. if opts != nil {
  728. if opts.LastLoginSince != nil {
  729. cond = cond.And(builder.Gte{"last_login_unix": *opts.LastLoginSince})
  730. }
  731. if opts.IsAdmin.Has() {
  732. cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()})
  733. }
  734. if opts.IsActive.Has() {
  735. cond = cond.And(builder.Eq{"is_active": opts.IsActive.Value()})
  736. }
  737. }
  738. count, err := sess.Where(cond).Count(new(User))
  739. if err != nil {
  740. log.Error("user.countUsers: %v", err)
  741. }
  742. return count
  743. }
  744. // GetVerifyUser get user by verify code
  745. func GetVerifyUser(ctx context.Context, code string) (user *User) {
  746. if len(code) <= base.TimeLimitCodeLength {
  747. return nil
  748. }
  749. // use tail hex username query user
  750. hexStr := code[base.TimeLimitCodeLength:]
  751. if b, err := hex.DecodeString(hexStr); err == nil {
  752. if user, err = GetUserByName(ctx, string(b)); user != nil {
  753. return user
  754. }
  755. log.Error("user.getVerifyUser: %v", err)
  756. }
  757. return nil
  758. }
  759. type TimeLimitCodePurpose string
  760. const (
  761. TimeLimitCodeActivateAccount TimeLimitCodePurpose = "activate_account"
  762. TimeLimitCodeActivateEmail TimeLimitCodePurpose = "activate_email"
  763. TimeLimitCodeResetPassword TimeLimitCodePurpose = "reset_password"
  764. )
  765. type TimeLimitCodeOptions struct {
  766. Purpose TimeLimitCodePurpose
  767. NewEmail string
  768. }
  769. func makeTimeLimitCodeHashData(opts *TimeLimitCodeOptions, u *User) string {
  770. return fmt.Sprintf("%s|%d|%s|%s|%s|%s", opts.Purpose, u.ID, strings.ToLower(util.IfZero(opts.NewEmail, u.Email)), u.LowerName, u.Passwd, u.Rands)
  771. }
  772. // GenerateUserTimeLimitCode generates a time-limit code based on user information and given e-mail.
  773. // TODO: need to use cache or db to store it to make sure a code can only be consumed once
  774. func GenerateUserTimeLimitCode(opts *TimeLimitCodeOptions, u *User) string {
  775. data := makeTimeLimitCodeHashData(opts, u)
  776. code := base.CreateTimeLimitCode(data, setting.Service.ActiveCodeLives, time.Now(), nil)
  777. code += hex.EncodeToString([]byte(u.LowerName)) // Add tail hex username
  778. return code
  779. }
  780. // VerifyUserTimeLimitCode verifies the time-limit code
  781. func VerifyUserTimeLimitCode(ctx context.Context, opts *TimeLimitCodeOptions, code string) (user *User) {
  782. if user = GetVerifyUser(ctx, code); user != nil {
  783. // time limit code
  784. prefix := code[:base.TimeLimitCodeLength]
  785. data := makeTimeLimitCodeHashData(opts, user)
  786. if base.VerifyTimeLimitCode(time.Now(), data, setting.Service.ActiveCodeLives, prefix) {
  787. return user
  788. }
  789. }
  790. return nil
  791. }
  792. // ValidateUser check if user is valid to insert / update into database
  793. func ValidateUser(u *User, cols ...string) error {
  794. if len(cols) == 0 || util.SliceContainsString(cols, "visibility", true) {
  795. if !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(u.Visibility) && !u.IsOrganization() {
  796. return fmt.Errorf("visibility Mode not allowed: %s", u.Visibility.String())
  797. }
  798. }
  799. return nil
  800. }
  801. // UpdateUserCols update user according special columns
  802. func UpdateUserCols(ctx context.Context, u *User, cols ...string) error {
  803. if err := ValidateUser(u, cols...); err != nil {
  804. return err
  805. }
  806. _, err := db.GetEngine(ctx).ID(u.ID).Cols(cols...).Update(u)
  807. return err
  808. }
  809. // UpdateUserColsNoAutoTime update user according special columns
  810. func UpdateUserColsNoAutoTime(ctx context.Context, u *User, cols ...string) error {
  811. if err := ValidateUser(u, cols...); err != nil {
  812. return err
  813. }
  814. _, err := db.GetEngine(ctx).ID(u.ID).Cols(cols...).NoAutoTime().Update(u)
  815. return err
  816. }
  817. // GetInactiveUsers gets all inactive users
  818. func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, error) {
  819. cond := builder.And(
  820. builder.Eq{"is_active": false},
  821. builder.Or( // only plain user
  822. builder.Eq{"`type`": UserTypeIndividual},
  823. builder.Eq{"`type`": UserTypeUserReserved},
  824. ),
  825. )
  826. if olderThan > 0 {
  827. cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-olderThan).Unix()})
  828. }
  829. users := make([]*User, 0, 10)
  830. return users, db.GetEngine(ctx).
  831. Where(cond).
  832. Find(&users)
  833. }
  834. // UserPath returns the path absolute path of user repositories.
  835. func UserPath(userName string) string { //revive:disable-line:exported
  836. return filepath.Join(setting.RepoRootPath, strings.ToLower(userName))
  837. }
  838. // GetUserByID returns the user object by given ID if exists.
  839. func GetUserByID(ctx context.Context, id int64) (*User, error) {
  840. u := new(User)
  841. has, err := db.GetEngine(ctx).ID(id).Get(u)
  842. if err != nil {
  843. return nil, err
  844. } else if !has {
  845. return nil, ErrUserNotExist{UID: id}
  846. }
  847. return u, nil
  848. }
  849. // GetUserByIDs returns the user objects by given IDs if exists.
  850. func GetUserByIDs(ctx context.Context, ids []int64) ([]*User, error) {
  851. if len(ids) == 0 {
  852. return nil, nil
  853. }
  854. users := make([]*User, 0, len(ids))
  855. err := db.GetEngine(ctx).In("id", ids).
  856. Table("user").
  857. Find(&users)
  858. return users, err
  859. }
  860. // GetPossibleUserByID returns the user if id > 0 or returns system user if id < 0
  861. func GetPossibleUserByID(ctx context.Context, id int64) (*User, error) {
  862. if id < 0 {
  863. if newFunc, ok := globalVars().systemUserNewFuncs[id]; ok {
  864. return newFunc(), nil
  865. }
  866. return nil, ErrUserNotExist{UID: id}
  867. } else if id == 0 {
  868. return nil, ErrUserNotExist{}
  869. }
  870. return GetUserByID(ctx, id)
  871. }
  872. // GetPossibleUserByIDs returns the users if id > 0 or returns system users if id < 0
  873. func GetPossibleUserByIDs(ctx context.Context, ids []int64) ([]*User, error) {
  874. uniqueIDs := container.SetOf(ids...)
  875. users := make([]*User, 0, len(ids))
  876. _ = uniqueIDs.Remove(0)
  877. for systemUID, newFunc := range globalVars().systemUserNewFuncs {
  878. if uniqueIDs.Remove(systemUID) {
  879. users = append(users, newFunc())
  880. }
  881. }
  882. res, err := GetUserByIDs(ctx, uniqueIDs.Values())
  883. if err != nil {
  884. return nil, err
  885. }
  886. users = append(users, res...)
  887. return users, nil
  888. }
  889. // GetUserByName returns user by given name.
  890. func GetUserByName(ctx context.Context, name string) (*User, error) {
  891. if len(name) == 0 {
  892. return nil, ErrUserNotExist{Name: name}
  893. }
  894. u := &User{LowerName: strings.ToLower(name), Type: UserTypeIndividual}
  895. has, err := db.GetEngine(ctx).Get(u)
  896. if err != nil {
  897. return nil, err
  898. } else if !has {
  899. return nil, ErrUserNotExist{Name: name}
  900. }
  901. return u, nil
  902. }
  903. // GetUserEmailsByNames returns a list of e-mails corresponds to names of users
  904. // that have their email notifications set to enabled or onmention.
  905. func GetUserEmailsByNames(ctx context.Context, names []string) []string {
  906. mails := make([]string, 0, len(names))
  907. for _, name := range names {
  908. u, err := GetUserByName(ctx, name)
  909. if err != nil {
  910. continue
  911. }
  912. if u.IsMailable() && u.EmailNotificationsPreference != EmailNotificationsDisabled {
  913. mails = append(mails, u.Email)
  914. }
  915. }
  916. return mails
  917. }
  918. // GetMailableUsersByIDs gets users from ids, but only if they can receive mails
  919. func GetMailableUsersByIDs(ctx context.Context, ids []int64, isMention bool) ([]*User, error) {
  920. if len(ids) == 0 {
  921. return nil, nil
  922. }
  923. ous := make([]*User, 0, len(ids))
  924. if isMention {
  925. return ous, db.GetEngine(ctx).
  926. In("id", ids).
  927. Where("`type` = ?", UserTypeIndividual).
  928. And("`prohibit_login` = ?", false).
  929. And("`is_active` = ?", true).
  930. In("`email_notifications_preference`", EmailNotificationsEnabled, EmailNotificationsOnMention, EmailNotificationsAndYourOwn).
  931. Find(&ous)
  932. }
  933. return ous, db.GetEngine(ctx).
  934. In("id", ids).
  935. Where("`type` = ?", UserTypeIndividual).
  936. And("`prohibit_login` = ?", false).
  937. And("`is_active` = ?", true).
  938. In("`email_notifications_preference`", EmailNotificationsEnabled, EmailNotificationsAndYourOwn).
  939. Find(&ous)
  940. }
  941. // GetUserNameByID returns username for the id
  942. func GetUserNameByID(ctx context.Context, id int64) (string, error) {
  943. var name string
  944. has, err := db.GetEngine(ctx).Table("user").Where("id = ?", id).Cols("name").Get(&name)
  945. if err != nil {
  946. return "", err
  947. }
  948. if has {
  949. return name, nil
  950. }
  951. return "", nil
  952. }
  953. // GetUserIDsByNames returns a slice of ids corresponds to names.
  954. func GetUserIDsByNames(ctx context.Context, names []string, ignoreNonExistent bool) ([]int64, error) {
  955. ids := make([]int64, 0, len(names))
  956. for _, name := range names {
  957. u, err := GetUserByName(ctx, name)
  958. if err != nil {
  959. if ignoreNonExistent {
  960. continue
  961. }
  962. return nil, err
  963. }
  964. ids = append(ids, u.ID)
  965. }
  966. return ids, nil
  967. }
  968. // GetUsersBySource returns a list of Users for a login source
  969. func GetUsersBySource(ctx context.Context, s *auth.Source) ([]*User, error) {
  970. var users []*User
  971. err := db.GetEngine(ctx).Where("login_type = ? AND login_source = ?", s.Type, s.ID).Find(&users)
  972. return users, err
  973. }
  974. // UserCommit represents a commit with validation of user.
  975. type UserCommit struct { //revive:disable-line:exported
  976. User *User
  977. *git.Commit
  978. }
  979. // ValidateCommitWithEmail check if author's e-mail of commit is corresponding to a user.
  980. func ValidateCommitWithEmail(ctx context.Context, c *git.Commit) *User {
  981. if c.Author == nil {
  982. return nil
  983. }
  984. u, err := GetUserByEmail(ctx, c.Author.Email)
  985. if err != nil {
  986. return nil
  987. }
  988. return u
  989. }
  990. // ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
  991. func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) ([]*UserCommit, error) {
  992. var (
  993. newCommits = make([]*UserCommit, 0, len(oldCommits))
  994. emailSet = make(container.Set[string])
  995. )
  996. for _, c := range oldCommits {
  997. if c.Author != nil {
  998. emailSet.Add(c.Author.Email)
  999. }
  1000. }
  1001. emailUserMap, err := GetUsersByEmails(ctx, emailSet.Values())
  1002. if err != nil {
  1003. return nil, err
  1004. }
  1005. for _, c := range oldCommits {
  1006. user := emailUserMap.GetByEmail(c.Author.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"?
  1007. newCommits = append(newCommits, &UserCommit{
  1008. User: user,
  1009. Commit: c,
  1010. })
  1011. }
  1012. return newCommits, nil
  1013. }
  1014. type EmailUserMap struct {
  1015. m map[string]*User
  1016. }
  1017. func (eum *EmailUserMap) GetByEmail(email string) *User {
  1018. return eum.m[strings.ToLower(email)]
  1019. }
  1020. func GetUsersByEmails(ctx context.Context, emails []string) (*EmailUserMap, error) {
  1021. if len(emails) == 0 {
  1022. return nil, nil
  1023. }
  1024. needCheckEmails := make(container.Set[string])
  1025. needCheckUserNames := make(container.Set[string])
  1026. noReplyAddressSuffix := "@" + strings.ToLower(setting.Service.NoReplyAddress)
  1027. for _, email := range emails {
  1028. emailLower := strings.ToLower(email)
  1029. if noReplyUserNameLower, ok := strings.CutSuffix(emailLower, noReplyAddressSuffix); ok {
  1030. needCheckUserNames.Add(noReplyUserNameLower)
  1031. needCheckEmails.Add(emailLower)
  1032. } else {
  1033. needCheckEmails.Add(emailLower)
  1034. }
  1035. }
  1036. emailAddresses := make([]*EmailAddress, 0, len(needCheckEmails))
  1037. if err := db.GetEngine(ctx).In("lower_email", needCheckEmails.Values()).
  1038. And("is_activated=?", true).
  1039. Find(&emailAddresses); err != nil {
  1040. return nil, err
  1041. }
  1042. userIDs := make(container.Set[int64])
  1043. for _, email := range emailAddresses {
  1044. userIDs.Add(email.UID)
  1045. }
  1046. results := make(map[string]*User, len(emails))
  1047. if len(userIDs) > 0 {
  1048. users, err := GetUsersMapByIDs(ctx, userIDs.Values())
  1049. if err != nil {
  1050. return nil, err
  1051. }
  1052. for _, email := range emailAddresses {
  1053. user := users[email.UID]
  1054. if user != nil {
  1055. results[email.LowerEmail] = user
  1056. }
  1057. }
  1058. }
  1059. users := make(map[int64]*User, len(needCheckUserNames))
  1060. if err := db.GetEngine(ctx).In("lower_name", needCheckUserNames.Values()).Find(&users); err != nil {
  1061. return nil, err
  1062. }
  1063. for _, user := range users {
  1064. results[strings.ToLower(user.GetPlaceholderEmail())] = user
  1065. }
  1066. return &EmailUserMap{results}, nil
  1067. }
  1068. // GetUserByEmail returns the user object by given e-mail if exists.
  1069. func GetUserByEmail(ctx context.Context, email string) (*User, error) {
  1070. if len(email) == 0 {
  1071. return nil, ErrUserNotExist{Name: email}
  1072. }
  1073. email = strings.ToLower(email)
  1074. // Otherwise, check in alternative list for activated email addresses
  1075. emailAddress := &EmailAddress{LowerEmail: email, IsActivated: true}
  1076. has, err := db.GetEngine(ctx).Get(emailAddress)
  1077. if err != nil {
  1078. return nil, err
  1079. }
  1080. if has {
  1081. return GetUserByID(ctx, emailAddress.UID)
  1082. }
  1083. // Finally, if email address is the protected email address:
  1084. if strings.HasSuffix(email, "@"+setting.Service.NoReplyAddress) {
  1085. username := strings.TrimSuffix(email, "@"+setting.Service.NoReplyAddress)
  1086. user := &User{}
  1087. has, err := db.GetEngine(ctx).Where("lower_name=?", username).Get(user)
  1088. if err != nil {
  1089. return nil, err
  1090. }
  1091. if has {
  1092. return user, nil
  1093. }
  1094. }
  1095. return nil, ErrUserNotExist{Name: email}
  1096. }
  1097. // GetUser checks if a user already exists
  1098. func GetUser(ctx context.Context, user *User) (bool, error) {
  1099. return db.GetEngine(ctx).Get(user)
  1100. }
  1101. // GetUserByOpenID returns the user object by given OpenID if exists.
  1102. func GetUserByOpenID(ctx context.Context, uri string) (*User, error) {
  1103. if len(uri) == 0 {
  1104. return nil, ErrUserNotExist{Name: uri}
  1105. }
  1106. uri, err := openid.Normalize(uri)
  1107. if err != nil {
  1108. return nil, err
  1109. }
  1110. log.Trace("Normalized OpenID URI: " + uri)
  1111. // Otherwise, check in openid table
  1112. oid := &UserOpenID{}
  1113. has, err := db.GetEngine(ctx).Where("uri=?", uri).Get(oid)
  1114. if err != nil {
  1115. return nil, err
  1116. }
  1117. if has {
  1118. return GetUserByID(ctx, oid.UID)
  1119. }
  1120. return nil, ErrUserNotExist{Name: uri}
  1121. }
  1122. // GetAdminUser returns the first administrator
  1123. func GetAdminUser(ctx context.Context) (*User, error) {
  1124. var admin User
  1125. has, err := db.GetEngine(ctx).
  1126. Where("is_admin=?", true).
  1127. Asc("id"). // Reliably get the admin with the lowest ID.
  1128. Get(&admin)
  1129. if err != nil {
  1130. return nil, err
  1131. } else if !has {
  1132. return nil, ErrUserNotExist{}
  1133. }
  1134. return &admin, nil
  1135. }
  1136. func isUserVisibleToViewerCond(viewer *User) builder.Cond {
  1137. if viewer != nil && viewer.IsAdmin {
  1138. return builder.NewCond()
  1139. }
  1140. if viewer == nil || viewer.IsRestricted {
  1141. return builder.Eq{
  1142. "`user`.visibility": structs.VisibleTypePublic,
  1143. }
  1144. }
  1145. return builder.Neq{
  1146. "`user`.visibility": structs.VisibleTypePrivate,
  1147. }.Or(
  1148. // viewer self
  1149. builder.Eq{"`user`.id": viewer.ID},
  1150. // viewer's following
  1151. builder.In("`user`.id",
  1152. builder.
  1153. Select("`follow`.user_id").
  1154. From("follow").
  1155. Where(builder.Eq{"`follow`.follow_id": viewer.ID})),
  1156. // viewer's org user
  1157. builder.In("`user`.id",
  1158. builder.
  1159. Select("`team_user`.uid").
  1160. From("team_user").
  1161. Join("INNER", "`team_user` AS t2", "`team_user`.org_id = `t2`.org_id").
  1162. Where(builder.Eq{"`t2`.uid": viewer.ID})),
  1163. // viewer's org
  1164. builder.In("`user`.id",
  1165. builder.
  1166. Select("`team_user`.org_id").
  1167. From("team_user").
  1168. Where(builder.Eq{"`team_user`.uid": viewer.ID})))
  1169. }
  1170. // IsUserVisibleToViewer check if viewer is able to see user profile
  1171. func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
  1172. if viewer != nil && (viewer.IsAdmin || viewer.ID == u.ID) {
  1173. return true
  1174. }
  1175. switch u.Visibility {
  1176. case structs.VisibleTypePublic:
  1177. return true
  1178. case structs.VisibleTypeLimited:
  1179. if viewer == nil || viewer.IsRestricted {
  1180. return false
  1181. }
  1182. return true
  1183. case structs.VisibleTypePrivate:
  1184. if viewer == nil || viewer.IsRestricted {
  1185. return false
  1186. }
  1187. // If they follow - they see each other
  1188. follower := IsFollowing(ctx, u.ID, viewer.ID)
  1189. if follower {
  1190. return true
  1191. }
  1192. // Now we need to check if they in some organization together
  1193. count, err := db.GetEngine(ctx).Table("team_user").
  1194. Where(
  1195. builder.And(
  1196. builder.Eq{"uid": viewer.ID},
  1197. builder.Or(
  1198. builder.Eq{"org_id": u.ID},
  1199. builder.In("org_id",
  1200. builder.Select("org_id").
  1201. From("team_user", "t2").
  1202. Where(builder.Eq{"uid": u.ID}))))).
  1203. Count()
  1204. if err != nil {
  1205. return false
  1206. }
  1207. if count == 0 {
  1208. // No common organization
  1209. return false
  1210. }
  1211. // they are in an organization together
  1212. return true
  1213. }
  1214. return false
  1215. }
  1216. // CountWrongUserType count OrgUser who have wrong type
  1217. func CountWrongUserType(ctx context.Context) (int64, error) {
  1218. return db.GetEngine(ctx).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Count(new(User))
  1219. }
  1220. // FixWrongUserType fix OrgUser who have wrong type
  1221. func FixWrongUserType(ctx context.Context) (int64, error) {
  1222. return db.GetEngine(ctx).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&User{Type: 1})
  1223. }
  1224. func GetOrderByName() string {
  1225. if setting.UI.DefaultShowFullName {
  1226. return "full_name, name"
  1227. }
  1228. return "name"
  1229. }
  1230. // IsFeatureDisabledWithLoginType checks if a user features are disabled, taking into account the login type of the
  1231. // user if applicable
  1232. func IsFeatureDisabledWithLoginType(user *User, features ...string) bool {
  1233. // NOTE: in the long run it may be better to check the ExternalLoginUser table rather than user.LoginType
  1234. if user != nil && user.LoginType > auth.Plain {
  1235. return setting.Admin.ExternalUserDisableFeatures.Contains(features...)
  1236. }
  1237. return setting.Admin.UserDisabledFeatures.Contains(features...)
  1238. }
  1239. // DisabledFeaturesWithLoginType returns the set of user features disabled, taking into account the login type
  1240. // of the user if applicable
  1241. func DisabledFeaturesWithLoginType(user *User) *container.Set[string] {
  1242. // NOTE: in the long run it may be better to check the ExternalLoginUser table rather than user.LoginType
  1243. if user != nil && user.LoginType > auth.Plain {
  1244. return &setting.Admin.ExternalUserDisableFeatures
  1245. }
  1246. return &setting.Admin.UserDisabledFeatures
  1247. }