You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

org.go 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846
  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 organization
  5. import (
  6. "context"
  7. "fmt"
  8. "strings"
  9. "code.gitea.io/gitea/models/db"
  10. "code.gitea.io/gitea/models/perm"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. secret_model "code.gitea.io/gitea/models/secret"
  13. "code.gitea.io/gitea/models/unit"
  14. user_model "code.gitea.io/gitea/models/user"
  15. "code.gitea.io/gitea/modules/log"
  16. "code.gitea.io/gitea/modules/setting"
  17. "code.gitea.io/gitea/modules/structs"
  18. "code.gitea.io/gitea/modules/util"
  19. "xorm.io/builder"
  20. )
  21. // ________ .__ __ .__
  22. // \_____ \_______ _________ ____ |__|____________ _/ |_|__| ____ ____
  23. // / | \_ __ \/ ___\__ \ / \| \___ /\__ \\ __\ |/ _ \ / \
  24. // / | \ | \/ /_/ > __ \| | \ |/ / / __ \| | | ( <_> ) | \
  25. // \_______ /__| \___ (____ /___| /__/_____ \(____ /__| |__|\____/|___| /
  26. // \/ /_____/ \/ \/ \/ \/ \/
  27. // ErrOrgNotExist represents a "OrgNotExist" kind of error.
  28. type ErrOrgNotExist struct {
  29. ID int64
  30. Name string
  31. }
  32. // IsErrOrgNotExist checks if an error is a ErrOrgNotExist.
  33. func IsErrOrgNotExist(err error) bool {
  34. _, ok := err.(ErrOrgNotExist)
  35. return ok
  36. }
  37. func (err ErrOrgNotExist) Error() string {
  38. return fmt.Sprintf("org does not exist [id: %d, name: %s]", err.ID, err.Name)
  39. }
  40. func (err ErrOrgNotExist) Unwrap() error {
  41. return util.ErrNotExist
  42. }
  43. // ErrLastOrgOwner represents a "LastOrgOwner" kind of error.
  44. type ErrLastOrgOwner struct {
  45. UID int64
  46. }
  47. // IsErrLastOrgOwner checks if an error is a ErrLastOrgOwner.
  48. func IsErrLastOrgOwner(err error) bool {
  49. _, ok := err.(ErrLastOrgOwner)
  50. return ok
  51. }
  52. func (err ErrLastOrgOwner) Error() string {
  53. return fmt.Sprintf("user is the last member of owner team [uid: %d]", err.UID)
  54. }
  55. // ErrUserNotAllowedCreateOrg represents a "UserNotAllowedCreateOrg" kind of error.
  56. type ErrUserNotAllowedCreateOrg struct{}
  57. // IsErrUserNotAllowedCreateOrg checks if an error is an ErrUserNotAllowedCreateOrg.
  58. func IsErrUserNotAllowedCreateOrg(err error) bool {
  59. _, ok := err.(ErrUserNotAllowedCreateOrg)
  60. return ok
  61. }
  62. func (err ErrUserNotAllowedCreateOrg) Error() string {
  63. return "user is not allowed to create organizations"
  64. }
  65. func (err ErrUserNotAllowedCreateOrg) Unwrap() error {
  66. return util.ErrPermissionDenied
  67. }
  68. // Organization represents an organization
  69. type Organization user_model.User
  70. // OrgFromUser converts user to organization
  71. func OrgFromUser(user *user_model.User) *Organization {
  72. return (*Organization)(user)
  73. }
  74. // TableName represents the real table name of Organization
  75. func (Organization) TableName() string {
  76. return "user"
  77. }
  78. // IsOwnedBy returns true if given user is in the owner team.
  79. func (org *Organization) IsOwnedBy(uid int64) (bool, error) {
  80. return IsOrganizationOwner(db.DefaultContext, org.ID, uid)
  81. }
  82. // IsOrgAdmin returns true if given user is in the owner team or an admin team.
  83. func (org *Organization) IsOrgAdmin(uid int64) (bool, error) {
  84. return IsOrganizationAdmin(db.DefaultContext, org.ID, uid)
  85. }
  86. // IsOrgMember returns true if given user is member of organization.
  87. func (org *Organization) IsOrgMember(uid int64) (bool, error) {
  88. return IsOrganizationMember(db.DefaultContext, org.ID, uid)
  89. }
  90. // CanCreateOrgRepo returns true if given user can create repo in organization
  91. func (org *Organization) CanCreateOrgRepo(uid int64) (bool, error) {
  92. return CanCreateOrgRepo(db.DefaultContext, org.ID, uid)
  93. }
  94. // GetTeam returns named team of organization.
  95. func (org *Organization) GetTeam(ctx context.Context, name string) (*Team, error) {
  96. return GetTeam(ctx, org.ID, name)
  97. }
  98. // GetOwnerTeam returns owner team of organization.
  99. func (org *Organization) GetOwnerTeam(ctx context.Context) (*Team, error) {
  100. return org.GetTeam(ctx, OwnerTeamName)
  101. }
  102. // FindOrgTeams returns all teams of a given organization
  103. func FindOrgTeams(ctx context.Context, orgID int64) ([]*Team, error) {
  104. var teams []*Team
  105. return teams, db.GetEngine(ctx).
  106. Where("org_id=?", orgID).
  107. OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
  108. Find(&teams)
  109. }
  110. // LoadTeams load teams if not loaded.
  111. func (org *Organization) LoadTeams() ([]*Team, error) {
  112. return FindOrgTeams(db.DefaultContext, org.ID)
  113. }
  114. // GetMembers returns all members of organization.
  115. func (org *Organization) GetMembers(ctx context.Context) (user_model.UserList, map[int64]bool, error) {
  116. return FindOrgMembers(ctx, &FindOrgMembersOpts{
  117. OrgID: org.ID,
  118. })
  119. }
  120. // HasMemberWithUserID returns true if user with userID is part of the u organisation.
  121. func (org *Organization) HasMemberWithUserID(userID int64) bool {
  122. return org.hasMemberWithUserID(db.DefaultContext, userID)
  123. }
  124. func (org *Organization) hasMemberWithUserID(ctx context.Context, userID int64) bool {
  125. isMember, err := IsOrganizationMember(ctx, org.ID, userID)
  126. if err != nil {
  127. log.Error("IsOrganizationMember: %v", err)
  128. return false
  129. }
  130. return isMember
  131. }
  132. // AvatarLink returns the full avatar link with http host
  133. func (org *Organization) AvatarLink(ctx context.Context) string {
  134. return org.AsUser().AvatarLink(ctx)
  135. }
  136. // HTMLURL returns the organization's full link.
  137. func (org *Organization) HTMLURL() string {
  138. return org.AsUser().HTMLURL()
  139. }
  140. // OrganisationLink returns the organization sub page link.
  141. func (org *Organization) OrganisationLink() string {
  142. return org.AsUser().OrganisationLink()
  143. }
  144. // ShortName ellipses username to length
  145. func (org *Organization) ShortName(length int) string {
  146. return org.AsUser().ShortName(length)
  147. }
  148. // HomeLink returns the user or organization home page link.
  149. func (org *Organization) HomeLink() string {
  150. return org.AsUser().HomeLink()
  151. }
  152. // CanCreateRepo returns if user login can create a repository
  153. // NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
  154. func (org *Organization) CanCreateRepo() bool {
  155. return org.AsUser().CanCreateRepo()
  156. }
  157. // FindOrgMembersOpts represensts find org members conditions
  158. type FindOrgMembersOpts struct {
  159. db.ListOptions
  160. OrgID int64
  161. PublicOnly bool
  162. }
  163. // CountOrgMembers counts the organization's members
  164. func CountOrgMembers(opts *FindOrgMembersOpts) (int64, error) {
  165. sess := db.GetEngine(db.DefaultContext).Where("org_id=?", opts.OrgID)
  166. if opts.PublicOnly {
  167. sess.And("is_public = ?", true)
  168. }
  169. return sess.Count(new(OrgUser))
  170. }
  171. // FindOrgMembers loads organization members according conditions
  172. func FindOrgMembers(ctx context.Context, opts *FindOrgMembersOpts) (user_model.UserList, map[int64]bool, error) {
  173. ous, err := GetOrgUsersByOrgID(ctx, opts)
  174. if err != nil {
  175. return nil, nil, err
  176. }
  177. ids := make([]int64, len(ous))
  178. idsIsPublic := make(map[int64]bool, len(ous))
  179. for i, ou := range ous {
  180. ids[i] = ou.UID
  181. idsIsPublic[ou.UID] = ou.IsPublic
  182. }
  183. users, err := user_model.GetUsersByIDs(ctx, ids)
  184. if err != nil {
  185. return nil, nil, err
  186. }
  187. return users, idsIsPublic, nil
  188. }
  189. // AsUser returns the org as user object
  190. func (org *Organization) AsUser() *user_model.User {
  191. return (*user_model.User)(org)
  192. }
  193. // DisplayName returns full name if it's not empty,
  194. // returns username otherwise.
  195. func (org *Organization) DisplayName() string {
  196. return org.AsUser().DisplayName()
  197. }
  198. // CustomAvatarRelativePath returns user custom avatar relative path.
  199. func (org *Organization) CustomAvatarRelativePath() string {
  200. return org.Avatar
  201. }
  202. // UnitPermission returns unit permission
  203. func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.User, unitType unit.Type) perm.AccessMode {
  204. if doer != nil {
  205. teams, err := GetUserOrgTeams(ctx, org.ID, doer.ID)
  206. if err != nil {
  207. log.Error("GetUserOrgTeams: %v", err)
  208. return perm.AccessModeNone
  209. }
  210. if err := teams.LoadUnits(ctx); err != nil {
  211. log.Error("LoadUnits: %v", err)
  212. return perm.AccessModeNone
  213. }
  214. if len(teams) > 0 {
  215. return teams.UnitMaxAccess(unitType)
  216. }
  217. }
  218. if org.Visibility.IsPublic() {
  219. return perm.AccessModeRead
  220. }
  221. return perm.AccessModeNone
  222. }
  223. // CreateOrganization creates record of a new organization.
  224. func CreateOrganization(org *Organization, owner *user_model.User) (err error) {
  225. if !owner.CanCreateOrganization() {
  226. return ErrUserNotAllowedCreateOrg{}
  227. }
  228. if err = user_model.IsUsableUsername(org.Name); err != nil {
  229. return err
  230. }
  231. isExist, err := user_model.IsUserExist(db.DefaultContext, 0, org.Name)
  232. if err != nil {
  233. return err
  234. } else if isExist {
  235. return user_model.ErrUserAlreadyExist{Name: org.Name}
  236. }
  237. org.LowerName = strings.ToLower(org.Name)
  238. if org.Rands, err = user_model.GetUserSalt(); err != nil {
  239. return err
  240. }
  241. if org.Salt, err = user_model.GetUserSalt(); err != nil {
  242. return err
  243. }
  244. org.UseCustomAvatar = true
  245. org.MaxRepoCreation = -1
  246. org.NumTeams = 1
  247. org.NumMembers = 1
  248. org.Type = user_model.UserTypeOrganization
  249. ctx, committer, err := db.TxContext(db.DefaultContext)
  250. if err != nil {
  251. return err
  252. }
  253. defer committer.Close()
  254. if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
  255. return err
  256. }
  257. if err = db.Insert(ctx, org); err != nil {
  258. return fmt.Errorf("insert organization: %w", err)
  259. }
  260. if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil {
  261. return fmt.Errorf("generate random avatar: %w", err)
  262. }
  263. // Add initial creator to organization and owner team.
  264. if err = db.Insert(ctx, &OrgUser{
  265. UID: owner.ID,
  266. OrgID: org.ID,
  267. }); err != nil {
  268. return fmt.Errorf("insert org-user relation: %w", err)
  269. }
  270. // Create default owner team.
  271. t := &Team{
  272. OrgID: org.ID,
  273. LowerName: strings.ToLower(OwnerTeamName),
  274. Name: OwnerTeamName,
  275. AccessMode: perm.AccessModeOwner,
  276. NumMembers: 1,
  277. IncludesAllRepositories: true,
  278. CanCreateOrgRepo: true,
  279. }
  280. if err = db.Insert(ctx, t); err != nil {
  281. return fmt.Errorf("insert owner team: %w", err)
  282. }
  283. // insert units for team
  284. units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
  285. for _, tp := range unit.AllRepoUnitTypes {
  286. up := perm.AccessModeOwner
  287. if tp == unit.TypeExternalTracker || tp == unit.TypeExternalWiki {
  288. up = perm.AccessModeRead
  289. }
  290. units = append(units, TeamUnit{
  291. OrgID: org.ID,
  292. TeamID: t.ID,
  293. Type: tp,
  294. AccessMode: up,
  295. })
  296. }
  297. if err = db.Insert(ctx, &units); err != nil {
  298. return err
  299. }
  300. if err = db.Insert(ctx, &TeamUser{
  301. UID: owner.ID,
  302. OrgID: org.ID,
  303. TeamID: t.ID,
  304. }); err != nil {
  305. return fmt.Errorf("insert team-user relation: %w", err)
  306. }
  307. return committer.Commit()
  308. }
  309. // GetOrgByName returns organization by given name.
  310. func GetOrgByName(ctx context.Context, name string) (*Organization, error) {
  311. if len(name) == 0 {
  312. return nil, ErrOrgNotExist{0, name}
  313. }
  314. u := &Organization{
  315. LowerName: strings.ToLower(name),
  316. Type: user_model.UserTypeOrganization,
  317. }
  318. has, err := db.GetEngine(ctx).Get(u)
  319. if err != nil {
  320. return nil, err
  321. } else if !has {
  322. return nil, ErrOrgNotExist{0, name}
  323. }
  324. return u, nil
  325. }
  326. // DeleteOrganization deletes models associated to an organization.
  327. func DeleteOrganization(ctx context.Context, org *Organization) error {
  328. if org.Type != user_model.UserTypeOrganization {
  329. return fmt.Errorf("%s is a user not an organization", org.Name)
  330. }
  331. if err := db.DeleteBeans(ctx,
  332. &Team{OrgID: org.ID},
  333. &OrgUser{OrgID: org.ID},
  334. &TeamUser{OrgID: org.ID},
  335. &TeamUnit{OrgID: org.ID},
  336. &TeamInvite{OrgID: org.ID},
  337. &secret_model.Secret{OwnerID: org.ID},
  338. ); err != nil {
  339. return fmt.Errorf("DeleteBeans: %w", err)
  340. }
  341. if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil {
  342. return fmt.Errorf("Delete: %w", err)
  343. }
  344. return nil
  345. }
  346. // GetOrgUserMaxAuthorizeLevel returns highest authorize level of user in an organization
  347. func (org *Organization) GetOrgUserMaxAuthorizeLevel(uid int64) (perm.AccessMode, error) {
  348. var authorize perm.AccessMode
  349. _, err := db.GetEngine(db.DefaultContext).
  350. Select("max(team.authorize)").
  351. Table("team").
  352. Join("INNER", "team_user", "team_user.team_id = team.id").
  353. Where("team_user.uid = ?", uid).
  354. And("team_user.org_id = ?", org.ID).
  355. Get(&authorize)
  356. return authorize, err
  357. }
  358. // GetUsersWhoCanCreateOrgRepo returns users which are able to create repo in organization
  359. func GetUsersWhoCanCreateOrgRepo(ctx context.Context, orgID int64) (map[int64]*user_model.User, error) {
  360. // Use a map, in order to de-duplicate users.
  361. users := make(map[int64]*user_model.User)
  362. return users, db.GetEngine(ctx).
  363. Join("INNER", "`team_user`", "`team_user`.uid=`user`.id").
  364. Join("INNER", "`team`", "`team`.id=`team_user`.team_id").
  365. Where(builder.Eq{"team.can_create_org_repo": true}.Or(builder.Eq{"team.authorize": perm.AccessModeOwner})).
  366. And("team_user.org_id = ?", orgID).Find(&users)
  367. }
  368. // SearchOrganizationsOptions options to filter organizations
  369. type SearchOrganizationsOptions struct {
  370. db.ListOptions
  371. All bool
  372. }
  373. // FindOrgOptions finds orgs options
  374. type FindOrgOptions struct {
  375. db.ListOptions
  376. UserID int64
  377. IncludePrivate bool
  378. }
  379. func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
  380. cond := builder.Eq{"uid": userID}
  381. if !includePrivate {
  382. cond["is_public"] = true
  383. }
  384. return builder.Select("org_id").From("org_user").Where(cond)
  385. }
  386. func (opts FindOrgOptions) toConds() builder.Cond {
  387. var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
  388. if opts.UserID > 0 {
  389. cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
  390. }
  391. if !opts.IncludePrivate {
  392. cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
  393. }
  394. return cond
  395. }
  396. // FindOrgs returns a list of organizations according given conditions
  397. func FindOrgs(opts FindOrgOptions) ([]*Organization, error) {
  398. orgs := make([]*Organization, 0, 10)
  399. sess := db.GetEngine(db.DefaultContext).
  400. Where(opts.toConds()).
  401. Asc("`user`.name")
  402. if opts.Page > 0 && opts.PageSize > 0 {
  403. sess.Limit(opts.PageSize, opts.PageSize*(opts.Page-1))
  404. }
  405. return orgs, sess.Find(&orgs)
  406. }
  407. // CountOrgs returns total count organizations according options
  408. func CountOrgs(opts FindOrgOptions) (int64, error) {
  409. return db.GetEngine(db.DefaultContext).
  410. Where(opts.toConds()).
  411. Count(new(Organization))
  412. }
  413. // HasOrgOrUserVisible tells if the given user can see the given org or user
  414. func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
  415. // If user is nil, it's an anonymous user/request.
  416. // The Ghost user is handled like an anonymous user.
  417. if user == nil || user.IsGhost() {
  418. return orgOrUser.Visibility == structs.VisibleTypePublic
  419. }
  420. if user.IsAdmin || orgOrUser.ID == user.ID {
  421. return true
  422. }
  423. if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
  424. return false
  425. }
  426. return true
  427. }
  428. // HasOrgsVisible tells if the given user can see at least one of the orgs provided
  429. func HasOrgsVisible(orgs []*Organization, user *user_model.User) bool {
  430. if len(orgs) == 0 {
  431. return false
  432. }
  433. for _, org := range orgs {
  434. if HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), user) {
  435. return true
  436. }
  437. }
  438. return false
  439. }
  440. // GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
  441. // are allowed to create repos.
  442. func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) {
  443. orgs := make([]*Organization, 0, 10)
  444. return orgs, db.GetEngine(ctx).Where(builder.In("id", builder.Select("`user`.id").From("`user`").
  445. Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id").
  446. Join("INNER", "`team`", "`team`.id = `team_user`.team_id").
  447. Where(builder.Eq{"`team_user`.uid": userID}).
  448. And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))).
  449. Asc("`user`.name").
  450. Find(&orgs)
  451. }
  452. // GetOrgUsersByOrgID returns all organization-user relations by organization ID.
  453. func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
  454. sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
  455. if opts.PublicOnly {
  456. sess.And("is_public = ?", true)
  457. }
  458. if opts.ListOptions.PageSize > 0 {
  459. sess = db.SetSessionPagination(sess, opts)
  460. ous := make([]*OrgUser, 0, opts.PageSize)
  461. return ous, sess.Find(&ous)
  462. }
  463. var ous []*OrgUser
  464. return ous, sess.Find(&ous)
  465. }
  466. // ChangeOrgUserStatus changes public or private membership status.
  467. func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
  468. ou := new(OrgUser)
  469. has, err := db.GetEngine(db.DefaultContext).
  470. Where("uid=?", uid).
  471. And("org_id=?", orgID).
  472. Get(ou)
  473. if err != nil {
  474. return err
  475. } else if !has {
  476. return nil
  477. }
  478. ou.IsPublic = public
  479. _, err = db.GetEngine(db.DefaultContext).ID(ou.ID).Cols("is_public").Update(ou)
  480. return err
  481. }
  482. // AddOrgUser adds new user to given organization.
  483. func AddOrgUser(orgID, uid int64) error {
  484. isAlreadyMember, err := IsOrganizationMember(db.DefaultContext, orgID, uid)
  485. if err != nil || isAlreadyMember {
  486. return err
  487. }
  488. ctx, committer, err := db.TxContext(db.DefaultContext)
  489. if err != nil {
  490. return err
  491. }
  492. defer committer.Close()
  493. // check in transaction
  494. isAlreadyMember, err = IsOrganizationMember(ctx, orgID, uid)
  495. if err != nil || isAlreadyMember {
  496. return err
  497. }
  498. ou := &OrgUser{
  499. UID: uid,
  500. OrgID: orgID,
  501. IsPublic: setting.Service.DefaultOrgMemberVisible,
  502. }
  503. if err := db.Insert(ctx, ou); err != nil {
  504. return err
  505. } else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
  506. return err
  507. }
  508. return committer.Commit()
  509. }
  510. // GetOrgByID returns the user object by given ID if exists.
  511. func GetOrgByID(ctx context.Context, id int64) (*Organization, error) {
  512. u := new(Organization)
  513. has, err := db.GetEngine(ctx).ID(id).Get(u)
  514. if err != nil {
  515. return nil, err
  516. } else if !has {
  517. return nil, user_model.ErrUserNotExist{
  518. UID: id,
  519. Name: "",
  520. KeyID: 0,
  521. }
  522. }
  523. return u, nil
  524. }
  525. // RemoveOrgRepo removes all team-repository relations of organization.
  526. func RemoveOrgRepo(ctx context.Context, orgID, repoID int64) error {
  527. teamRepos := make([]*TeamRepo, 0, 10)
  528. e := db.GetEngine(ctx)
  529. if err := e.Find(&teamRepos, &TeamRepo{OrgID: orgID, RepoID: repoID}); err != nil {
  530. return err
  531. }
  532. if len(teamRepos) == 0 {
  533. return nil
  534. }
  535. if _, err := e.Delete(&TeamRepo{
  536. OrgID: orgID,
  537. RepoID: repoID,
  538. }); err != nil {
  539. return err
  540. }
  541. teamIDs := make([]int64, len(teamRepos))
  542. for i, teamRepo := range teamRepos {
  543. teamIDs[i] = teamRepo.TeamID
  544. }
  545. _, err := e.Decr("num_repos").In("id", teamIDs).Update(new(Team))
  546. return err
  547. }
  548. func (org *Organization) getUserTeams(ctx context.Context, userID int64, cols ...string) ([]*Team, error) {
  549. teams := make([]*Team, 0, org.NumTeams)
  550. return teams, db.GetEngine(ctx).
  551. Where("`team_user`.org_id = ?", org.ID).
  552. Join("INNER", "team_user", "`team_user`.team_id = team.id").
  553. Join("INNER", "`user`", "`user`.id=team_user.uid").
  554. And("`team_user`.uid = ?", userID).
  555. Asc("`user`.name").
  556. Cols(cols...).
  557. Find(&teams)
  558. }
  559. func (org *Organization) getUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
  560. teamIDs := make([]int64, 0, org.NumTeams)
  561. return teamIDs, db.GetEngine(ctx).
  562. Table("team").
  563. Cols("team.id").
  564. Where("`team_user`.org_id = ?", org.ID).
  565. Join("INNER", "team_user", "`team_user`.team_id = team.id").
  566. And("`team_user`.uid = ?", userID).
  567. Find(&teamIDs)
  568. }
  569. // TeamsWithAccessToRepo returns all teams that have given access level to the repository.
  570. func (org *Organization) TeamsWithAccessToRepo(repoID int64, mode perm.AccessMode) ([]*Team, error) {
  571. return GetTeamsWithAccessToRepo(db.DefaultContext, org.ID, repoID, mode)
  572. }
  573. // GetUserTeamIDs returns of all team IDs of the organization that user is member of.
  574. func (org *Organization) GetUserTeamIDs(userID int64) ([]int64, error) {
  575. return org.getUserTeamIDs(db.DefaultContext, userID)
  576. }
  577. // GetUserTeams returns all teams that belong to user,
  578. // and that the user has joined.
  579. func (org *Organization) GetUserTeams(userID int64) ([]*Team, error) {
  580. return org.getUserTeams(db.DefaultContext, userID)
  581. }
  582. // AccessibleReposEnvironment operations involving the repositories that are
  583. // accessible to a particular user
  584. type AccessibleReposEnvironment interface {
  585. CountRepos() (int64, error)
  586. RepoIDs(page, pageSize int) ([]int64, error)
  587. Repos(page, pageSize int) (repo_model.RepositoryList, error)
  588. MirrorRepos() (repo_model.RepositoryList, error)
  589. AddKeyword(keyword string)
  590. SetSort(db.SearchOrderBy)
  591. }
  592. type accessibleReposEnv struct {
  593. org *Organization
  594. user *user_model.User
  595. team *Team
  596. teamIDs []int64
  597. ctx context.Context
  598. keyword string
  599. orderBy db.SearchOrderBy
  600. }
  601. // AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org`
  602. // that are accessible to the specified user.
  603. func AccessibleReposEnv(ctx context.Context, org *Organization, userID int64) (AccessibleReposEnvironment, error) {
  604. var user *user_model.User
  605. if userID > 0 {
  606. u, err := user_model.GetUserByID(ctx, userID)
  607. if err != nil {
  608. return nil, err
  609. }
  610. user = u
  611. }
  612. teamIDs, err := org.getUserTeamIDs(ctx, userID)
  613. if err != nil {
  614. return nil, err
  615. }
  616. return &accessibleReposEnv{
  617. org: org,
  618. user: user,
  619. teamIDs: teamIDs,
  620. ctx: ctx,
  621. orderBy: db.SearchOrderByRecentUpdated,
  622. }, nil
  623. }
  624. // AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
  625. // that are accessible to the specified team.
  626. func (org *Organization) AccessibleTeamReposEnv(team *Team) AccessibleReposEnvironment {
  627. return &accessibleReposEnv{
  628. org: org,
  629. team: team,
  630. ctx: db.DefaultContext,
  631. orderBy: db.SearchOrderByRecentUpdated,
  632. }
  633. }
  634. func (env *accessibleReposEnv) cond() builder.Cond {
  635. cond := builder.NewCond()
  636. if env.team != nil {
  637. cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
  638. } else {
  639. if env.user == nil || !env.user.IsRestricted {
  640. cond = cond.Or(builder.Eq{
  641. "`repository`.owner_id": env.org.ID,
  642. "`repository`.is_private": false,
  643. })
  644. }
  645. if len(env.teamIDs) > 0 {
  646. cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
  647. }
  648. }
  649. if env.keyword != "" {
  650. cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
  651. }
  652. return cond
  653. }
  654. func (env *accessibleReposEnv) CountRepos() (int64, error) {
  655. repoCount, err := db.GetEngine(env.ctx).
  656. Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
  657. Where(env.cond()).
  658. Distinct("`repository`.id").
  659. Count(&repo_model.Repository{})
  660. if err != nil {
  661. return 0, fmt.Errorf("count user repositories in organization: %w", err)
  662. }
  663. return repoCount, nil
  664. }
  665. func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) {
  666. if page <= 0 {
  667. page = 1
  668. }
  669. repoIDs := make([]int64, 0, pageSize)
  670. return repoIDs, db.GetEngine(env.ctx).
  671. Table("repository").
  672. Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
  673. Where(env.cond()).
  674. GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]).
  675. OrderBy(string(env.orderBy)).
  676. Limit(pageSize, (page-1)*pageSize).
  677. Cols("`repository`.id").
  678. Find(&repoIDs)
  679. }
  680. func (env *accessibleReposEnv) Repos(page, pageSize int) (repo_model.RepositoryList, error) {
  681. repoIDs, err := env.RepoIDs(page, pageSize)
  682. if err != nil {
  683. return nil, fmt.Errorf("GetUserRepositoryIDs: %w", err)
  684. }
  685. repos := make([]*repo_model.Repository, 0, len(repoIDs))
  686. if len(repoIDs) == 0 {
  687. return repos, nil
  688. }
  689. return repos, db.GetEngine(env.ctx).
  690. In("`repository`.id", repoIDs).
  691. OrderBy(string(env.orderBy)).
  692. Find(&repos)
  693. }
  694. func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
  695. repoIDs := make([]int64, 0, 10)
  696. return repoIDs, db.GetEngine(env.ctx).
  697. Table("repository").
  698. Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
  699. Where(env.cond()).
  700. GroupBy("`repository`.id, `repository`.updated_unix").
  701. OrderBy(string(env.orderBy)).
  702. Cols("`repository`.id").
  703. Find(&repoIDs)
  704. }
  705. func (env *accessibleReposEnv) MirrorRepos() (repo_model.RepositoryList, error) {
  706. repoIDs, err := env.MirrorRepoIDs()
  707. if err != nil {
  708. return nil, fmt.Errorf("MirrorRepoIDs: %w", err)
  709. }
  710. repos := make([]*repo_model.Repository, 0, len(repoIDs))
  711. if len(repoIDs) == 0 {
  712. return repos, nil
  713. }
  714. return repos, db.GetEngine(env.ctx).
  715. In("`repository`.id", repoIDs).
  716. Find(&repos)
  717. }
  718. func (env *accessibleReposEnv) AddKeyword(keyword string) {
  719. env.keyword = keyword
  720. }
  721. func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) {
  722. env.orderBy = orderBy
  723. }