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.

636 lines
15 KiB

  1. // Copyright 2016 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "errors"
  7. "fmt"
  8. "strings"
  9. "code.gitea.io/gitea/modules/log"
  10. )
  11. const ownerTeamName = "Owners"
  12. // Team represents a organization team.
  13. type Team struct {
  14. ID int64 `xorm:"pk autoincr"`
  15. OrgID int64 `xorm:"INDEX"`
  16. LowerName string
  17. Name string
  18. Description string
  19. Authorize AccessMode
  20. Repos []*Repository `xorm:"-"`
  21. Members []*User `xorm:"-"`
  22. NumRepos int
  23. NumMembers int
  24. UnitTypes []UnitType `xorm:"json"`
  25. }
  26. // GetUnitTypes returns unit types the team owned, empty means all the unit types
  27. func (t *Team) GetUnitTypes() []UnitType {
  28. if len(t.UnitTypes) == 0 {
  29. return allRepUnitTypes
  30. }
  31. return t.UnitTypes
  32. }
  33. // HasWriteAccess returns true if team has at least write level access mode.
  34. func (t *Team) HasWriteAccess() bool {
  35. return t.Authorize >= AccessModeWrite
  36. }
  37. // IsOwnerTeam returns true if team is owner team.
  38. func (t *Team) IsOwnerTeam() bool {
  39. return t.Name == ownerTeamName
  40. }
  41. // IsMember returns true if given user is a member of team.
  42. func (t *Team) IsMember(userID int64) bool {
  43. isMember, err := IsTeamMember(t.OrgID, t.ID, userID)
  44. if err != nil {
  45. log.Error(4, "IsMember: %v", err)
  46. return false
  47. }
  48. return isMember
  49. }
  50. func (t *Team) getRepositories(e Engine) error {
  51. return e.Join("INNER", "team_repo", "repository.id = team_repo.repo_id").
  52. Where("team_repo.team_id=?", t.ID).Find(&t.Repos)
  53. }
  54. // GetRepositories returns all repositories in team of organization.
  55. func (t *Team) GetRepositories() error {
  56. return t.getRepositories(x)
  57. }
  58. func (t *Team) getMembers(e Engine) (err error) {
  59. t.Members, err = getTeamMembers(e, t.ID)
  60. return err
  61. }
  62. // GetMembers returns all members in team of organization.
  63. func (t *Team) GetMembers() (err error) {
  64. return t.getMembers(x)
  65. }
  66. // AddMember adds new membership of the team to the organization,
  67. // the user will have membership to the organization automatically when needed.
  68. func (t *Team) AddMember(userID int64) error {
  69. return AddTeamMember(t, userID)
  70. }
  71. // RemoveMember removes member from team of organization.
  72. func (t *Team) RemoveMember(userID int64) error {
  73. return RemoveTeamMember(t, userID)
  74. }
  75. func (t *Team) hasRepository(e Engine, repoID int64) bool {
  76. return hasTeamRepo(e, t.OrgID, t.ID, repoID)
  77. }
  78. // HasRepository returns true if given repository belong to team.
  79. func (t *Team) HasRepository(repoID int64) bool {
  80. return t.hasRepository(x, repoID)
  81. }
  82. func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
  83. if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil {
  84. return err
  85. }
  86. if _, err = e.Incr("num_repos").ID(t.ID).Update(new(Team)); err != nil {
  87. return fmt.Errorf("update team: %v", err)
  88. }
  89. t.NumRepos++
  90. if err = repo.recalculateTeamAccesses(e, 0); err != nil {
  91. return fmt.Errorf("recalculateAccesses: %v", err)
  92. }
  93. if err = t.getMembers(e); err != nil {
  94. return fmt.Errorf("getMembers: %v", err)
  95. }
  96. for _, u := range t.Members {
  97. if err = watchRepo(e, u.ID, repo.ID, true); err != nil {
  98. return fmt.Errorf("watchRepo: %v", err)
  99. }
  100. }
  101. return nil
  102. }
  103. // AddRepository adds new repository to team of organization.
  104. func (t *Team) AddRepository(repo *Repository) (err error) {
  105. if repo.OwnerID != t.OrgID {
  106. return errors.New("Repository does not belong to organization")
  107. } else if t.HasRepository(repo.ID) {
  108. return nil
  109. }
  110. sess := x.NewSession()
  111. defer sess.Close()
  112. if err = sess.Begin(); err != nil {
  113. return err
  114. }
  115. if err = t.addRepository(sess, repo); err != nil {
  116. return err
  117. }
  118. return sess.Commit()
  119. }
  120. func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
  121. if err = removeTeamRepo(e, t.ID, repo.ID); err != nil {
  122. return err
  123. }
  124. t.NumRepos--
  125. if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
  126. return err
  127. }
  128. // Don't need to recalculate when delete a repository from organization.
  129. if recalculate {
  130. if err = repo.recalculateTeamAccesses(e, t.ID); err != nil {
  131. return err
  132. }
  133. }
  134. teamUsers, err := getTeamUsersByTeamID(e, t.ID)
  135. if err != nil {
  136. return fmt.Errorf("getTeamUsersByTeamID: %v", err)
  137. }
  138. for _, teamUser := range teamUsers {
  139. has, err := hasAccess(e, teamUser.UID, repo, AccessModeRead)
  140. if err != nil {
  141. return err
  142. } else if has {
  143. continue
  144. }
  145. if err = watchRepo(e, teamUser.UID, repo.ID, false); err != nil {
  146. return err
  147. }
  148. }
  149. return nil
  150. }
  151. // RemoveRepository removes repository from team of organization.
  152. func (t *Team) RemoveRepository(repoID int64) error {
  153. if !t.HasRepository(repoID) {
  154. return nil
  155. }
  156. repo, err := GetRepositoryByID(repoID)
  157. if err != nil {
  158. return err
  159. }
  160. sess := x.NewSession()
  161. defer sess.Close()
  162. if err = sess.Begin(); err != nil {
  163. return err
  164. }
  165. if err = t.removeRepository(sess, repo, true); err != nil {
  166. return err
  167. }
  168. return sess.Commit()
  169. }
  170. // UnitEnabled returns if the team has the given unit type enabled
  171. func (t *Team) UnitEnabled(tp UnitType) bool {
  172. if len(t.UnitTypes) == 0 {
  173. return true
  174. }
  175. for _, u := range t.UnitTypes {
  176. if u == tp {
  177. return true
  178. }
  179. }
  180. return false
  181. }
  182. // IsUsableTeamName tests if a name could be as team name
  183. func IsUsableTeamName(name string) error {
  184. switch name {
  185. case "new":
  186. return ErrNameReserved{name}
  187. default:
  188. return nil
  189. }
  190. }
  191. // NewTeam creates a record of new team.
  192. // It's caller's responsibility to assign organization ID.
  193. func NewTeam(t *Team) (err error) {
  194. if len(t.Name) == 0 {
  195. return errors.New("empty team name")
  196. }
  197. if err = IsUsableTeamName(t.Name); err != nil {
  198. return err
  199. }
  200. has, err := x.ID(t.OrgID).Get(new(User))
  201. if err != nil {
  202. return err
  203. } else if !has {
  204. return ErrOrgNotExist{t.OrgID, ""}
  205. }
  206. t.LowerName = strings.ToLower(t.Name)
  207. has, err = x.
  208. Where("org_id=?", t.OrgID).
  209. And("lower_name=?", t.LowerName).
  210. Get(new(Team))
  211. if err != nil {
  212. return err
  213. } else if has {
  214. return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
  215. }
  216. sess := x.NewSession()
  217. defer sess.Close()
  218. if err = sess.Begin(); err != nil {
  219. return err
  220. }
  221. if _, err = sess.Insert(t); err != nil {
  222. sess.Rollback()
  223. return err
  224. }
  225. // Update organization number of teams.
  226. if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
  227. sess.Rollback()
  228. return err
  229. }
  230. return sess.Commit()
  231. }
  232. func getTeam(e Engine, orgID int64, name string) (*Team, error) {
  233. t := &Team{
  234. OrgID: orgID,
  235. LowerName: strings.ToLower(name),
  236. }
  237. has, err := e.Get(t)
  238. if err != nil {
  239. return nil, err
  240. } else if !has {
  241. return nil, ErrTeamNotExist
  242. }
  243. return t, nil
  244. }
  245. // GetTeam returns team by given team name and organization.
  246. func GetTeam(orgID int64, name string) (*Team, error) {
  247. return getTeam(x, orgID, name)
  248. }
  249. func getTeamByID(e Engine, teamID int64) (*Team, error) {
  250. t := new(Team)
  251. has, err := e.ID(teamID).Get(t)
  252. if err != nil {
  253. return nil, err
  254. } else if !has {
  255. return nil, ErrTeamNotExist
  256. }
  257. return t, nil
  258. }
  259. // GetTeamByID returns team by given ID.
  260. func GetTeamByID(teamID int64) (*Team, error) {
  261. return getTeamByID(x, teamID)
  262. }
  263. // UpdateTeam updates information of team.
  264. func UpdateTeam(t *Team, authChanged bool) (err error) {
  265. if len(t.Name) == 0 {
  266. return errors.New("empty team name")
  267. }
  268. if len(t.Description) > 255 {
  269. t.Description = t.Description[:255]
  270. }
  271. sess := x.NewSession()
  272. defer sess.Close()
  273. if err = sess.Begin(); err != nil {
  274. return err
  275. }
  276. t.LowerName = strings.ToLower(t.Name)
  277. has, err := sess.
  278. Where("org_id=?", t.OrgID).
  279. And("lower_name=?", t.LowerName).
  280. And("id!=?", t.ID).
  281. Get(new(Team))
  282. if err != nil {
  283. return err
  284. } else if has {
  285. return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
  286. }
  287. if _, err = sess.ID(t.ID).AllCols().Update(t); err != nil {
  288. return fmt.Errorf("update: %v", err)
  289. }
  290. // Update access for team members if needed.
  291. if authChanged {
  292. if err = t.getRepositories(sess); err != nil {
  293. return fmt.Errorf("getRepositories: %v", err)
  294. }
  295. for _, repo := range t.Repos {
  296. if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
  297. return fmt.Errorf("recalculateTeamAccesses: %v", err)
  298. }
  299. }
  300. }
  301. return sess.Commit()
  302. }
  303. // DeleteTeam deletes given team.
  304. // It's caller's responsibility to assign organization ID.
  305. func DeleteTeam(t *Team) error {
  306. if err := t.GetRepositories(); err != nil {
  307. return err
  308. }
  309. sess := x.NewSession()
  310. defer sess.Close()
  311. if err := sess.Begin(); err != nil {
  312. return err
  313. }
  314. // Delete all accesses.
  315. for _, repo := range t.Repos {
  316. if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil {
  317. return err
  318. }
  319. }
  320. // Delete team-repo
  321. if _, err := sess.
  322. Where("team_id=?", t.ID).
  323. Delete(new(TeamRepo)); err != nil {
  324. return err
  325. }
  326. // Delete team-user.
  327. if _, err := sess.
  328. Where("org_id=?", t.OrgID).
  329. Where("team_id=?", t.ID).
  330. Delete(new(TeamUser)); err != nil {
  331. return err
  332. }
  333. // Delete team.
  334. if _, err := sess.ID(t.ID).Delete(new(Team)); err != nil {
  335. return err
  336. }
  337. // Update organization number of teams.
  338. if _, err := sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
  339. return err
  340. }
  341. return sess.Commit()
  342. }
  343. // ___________ ____ ___
  344. // \__ ___/___ _____ _____ | | \______ ___________
  345. // | |_/ __ \\__ \ / \| | / ___// __ \_ __ \
  346. // | |\ ___/ / __ \| Y Y \ | /\___ \\ ___/| | \/
  347. // |____| \___ >____ /__|_| /______//____ >\___ >__|
  348. // \/ \/ \/ \/ \/
  349. // TeamUser represents an team-user relation.
  350. type TeamUser struct {
  351. ID int64 `xorm:"pk autoincr"`
  352. OrgID int64 `xorm:"INDEX"`
  353. TeamID int64 `xorm:"UNIQUE(s)"`
  354. UID int64 `xorm:"UNIQUE(s)"`
  355. }
  356. func isTeamMember(e Engine, orgID, teamID, userID int64) (bool, error) {
  357. return e.
  358. Where("org_id=?", orgID).
  359. And("team_id=?", teamID).
  360. And("uid=?", userID).
  361. Table("team_user").
  362. Exist()
  363. }
  364. // IsTeamMember returns true if given user is a member of team.
  365. func IsTeamMember(orgID, teamID, userID int64) (bool, error) {
  366. return isTeamMember(x, orgID, teamID, userID)
  367. }
  368. func getTeamUsersByTeamID(e Engine, teamID int64) ([]*TeamUser, error) {
  369. teamUsers := make([]*TeamUser, 0, 10)
  370. return teamUsers, e.
  371. Where("team_id=?", teamID).
  372. Find(&teamUsers)
  373. }
  374. func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
  375. teamUsers, err := getTeamUsersByTeamID(e, teamID)
  376. if err != nil {
  377. return nil, fmt.Errorf("get team-users: %v", err)
  378. }
  379. members := make([]*User, len(teamUsers))
  380. for i, teamUser := range teamUsers {
  381. member, err := getUserByID(e, teamUser.UID)
  382. if err != nil {
  383. return nil, fmt.Errorf("get user '%d': %v", teamUser.UID, err)
  384. }
  385. members[i] = member
  386. }
  387. return members, nil
  388. }
  389. // GetTeamMembers returns all members in given team of organization.
  390. func GetTeamMembers(teamID int64) ([]*User, error) {
  391. return getTeamMembers(x, teamID)
  392. }
  393. func getUserTeams(e Engine, orgID, userID int64) (teams []*Team, err error) {
  394. return teams, e.
  395. Join("INNER", "team_user", "team_user.team_id = team.id").
  396. Where("team.org_id = ?", orgID).
  397. And("team_user.uid=?", userID).
  398. Find(&teams)
  399. }
  400. // GetUserTeams returns all teams that user belongs to in given organization.
  401. func GetUserTeams(orgID, userID int64) ([]*Team, error) {
  402. return getUserTeams(x, orgID, userID)
  403. }
  404. // AddTeamMember adds new membership of given team to given organization,
  405. // the user will have membership to given organization automatically when needed.
  406. func AddTeamMember(team *Team, userID int64) error {
  407. isAlreadyMember, err := IsTeamMember(team.OrgID, team.ID, userID)
  408. if err != nil || isAlreadyMember {
  409. return err
  410. }
  411. if err := AddOrgUser(team.OrgID, userID); err != nil {
  412. return err
  413. }
  414. // Get team and its repositories.
  415. if err := team.GetRepositories(); err != nil {
  416. return err
  417. }
  418. sess := x.NewSession()
  419. defer sess.Close()
  420. if err := sess.Begin(); err != nil {
  421. return err
  422. }
  423. if _, err := sess.Insert(&TeamUser{
  424. UID: userID,
  425. OrgID: team.OrgID,
  426. TeamID: team.ID,
  427. }); err != nil {
  428. return err
  429. } else if _, err := sess.Incr("num_members").ID(team.ID).Update(new(Team)); err != nil {
  430. return err
  431. }
  432. team.NumMembers++
  433. // Give access to team repositories.
  434. for _, repo := range team.Repos {
  435. if err := repo.recalculateTeamAccesses(sess, 0); err != nil {
  436. return err
  437. }
  438. }
  439. return sess.Commit()
  440. }
  441. func removeTeamMember(e Engine, team *Team, userID int64) error {
  442. isMember, err := isTeamMember(e, team.OrgID, team.ID, userID)
  443. if err != nil || !isMember {
  444. return err
  445. }
  446. // Check if the user to delete is the last member in owner team.
  447. if team.IsOwnerTeam() && team.NumMembers == 1 {
  448. return ErrLastOrgOwner{UID: userID}
  449. }
  450. team.NumMembers--
  451. if err := team.getRepositories(e); err != nil {
  452. return err
  453. }
  454. if _, err := e.Delete(&TeamUser{
  455. UID: userID,
  456. OrgID: team.OrgID,
  457. TeamID: team.ID,
  458. }); err != nil {
  459. return err
  460. } else if _, err = e.
  461. ID(team.ID).
  462. Cols("num_members").
  463. Update(team); err != nil {
  464. return err
  465. }
  466. // Delete access to team repositories.
  467. for _, repo := range team.Repos {
  468. if err := repo.recalculateTeamAccesses(e, 0); err != nil {
  469. return err
  470. }
  471. }
  472. return nil
  473. }
  474. // RemoveTeamMember removes member from given team of given organization.
  475. func RemoveTeamMember(team *Team, userID int64) error {
  476. sess := x.NewSession()
  477. defer sess.Close()
  478. if err := sess.Begin(); err != nil {
  479. return err
  480. }
  481. if err := removeTeamMember(sess, team, userID); err != nil {
  482. return err
  483. }
  484. return sess.Commit()
  485. }
  486. // IsUserInTeams returns if a user in some teams
  487. func IsUserInTeams(userID int64, teamIDs []int64) (bool, error) {
  488. return x.Where("uid=?", userID).In("team_id", teamIDs).Exist(new(TeamUser))
  489. }
  490. // ___________ __________
  491. // \__ ___/___ _____ _____\______ \ ____ ______ ____
  492. // | |_/ __ \\__ \ / \| _// __ \\____ \ / _ \
  493. // | |\ ___/ / __ \| Y Y \ | \ ___/| |_> > <_> )
  494. // |____| \___ >____ /__|_| /____|_ /\___ > __/ \____/
  495. // \/ \/ \/ \/ \/|__|
  496. // TeamRepo represents an team-repository relation.
  497. type TeamRepo struct {
  498. ID int64 `xorm:"pk autoincr"`
  499. OrgID int64 `xorm:"INDEX"`
  500. TeamID int64 `xorm:"UNIQUE(s)"`
  501. RepoID int64 `xorm:"UNIQUE(s)"`
  502. }
  503. func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
  504. has, _ := e.
  505. Where("org_id=?", orgID).
  506. And("team_id=?", teamID).
  507. And("repo_id=?", repoID).
  508. Get(new(TeamRepo))
  509. return has
  510. }
  511. // HasTeamRepo returns true if given repository belongs to team.
  512. func HasTeamRepo(orgID, teamID, repoID int64) bool {
  513. return hasTeamRepo(x, orgID, teamID, repoID)
  514. }
  515. func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
  516. _, err := e.InsertOne(&TeamRepo{
  517. OrgID: orgID,
  518. TeamID: teamID,
  519. RepoID: repoID,
  520. })
  521. return err
  522. }
  523. func removeTeamRepo(e Engine, teamID, repoID int64) error {
  524. _, err := e.Delete(&TeamRepo{
  525. TeamID: teamID,
  526. RepoID: repoID,
  527. })
  528. return err
  529. }
  530. // GetTeamsWithAccessToRepo returns all teams in an organization that have given access level to the repository.
  531. func GetTeamsWithAccessToRepo(orgID, repoID int64, mode AccessMode) ([]*Team, error) {
  532. teams := make([]*Team, 0, 5)
  533. return teams, x.Where("team.authorize >= ?", mode).
  534. Join("INNER", "team_repo", "team_repo.team_id = team.id").
  535. And("team_repo.org_id = ?", orgID).
  536. And("team_repo.repo_id = ?", repoID).
  537. Find(&teams)
  538. }