#1692 add admin APIs to add/remove a user from teams
This commit is contained in:
		
							parent
							
								
									9dda9ef07c
								
							
						
					
					
						commit
						b1d41cfa60
					
				
					 11 changed files with 712 additions and 639 deletions
				
			
		|  | @ -3,7 +3,7 @@ Gogs - Go Git Service [ | ||||
| 
 | ||||
| ##### Current version: 0.9.15 | ||||
| ##### Current version: 0.9.16 | ||||
| 
 | ||||
| | Web | UI  | Preview  | | ||||
| |:-------------:|:-------:|:-------:| | ||||
|  |  | |||
							
								
								
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							|  | @ -17,7 +17,7 @@ import ( | |||
| 	"github.com/gogits/gogs/modules/setting" | ||||
| ) | ||||
| 
 | ||||
| const APP_VER = "0.9.15.0323" | ||||
| const APP_VER = "0.9.16.0325" | ||||
| 
 | ||||
| func init() { | ||||
| 	runtime.GOMAXPROCS(runtime.NumCPU()) | ||||
|  |  | |||
							
								
								
									
										612
									
								
								models/org.go
									
									
									
									
									
								
							
							
						
						
									
										612
									
								
								models/org.go
									
									
									
									
									
								
							|  | @ -426,618 +426,6 @@ func RemoveOrgUser(orgId, uid int64) error { | |||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // ___________
 | ||||
| // \__    ___/___ _____    _____
 | ||||
| //   |    |_/ __ \\__  \  /     \
 | ||||
| //   |    |\  ___/ / __ \|  Y Y  \
 | ||||
| //   |____| \___  >____  /__|_|  /
 | ||||
| //              \/     \/      \/
 | ||||
| 
 | ||||
| const OWNER_TEAM = "Owners" | ||||
| 
 | ||||
| // Team represents a organization team.
 | ||||
| type Team struct { | ||||
| 	ID          int64 `xorm:"pk autoincr"` | ||||
| 	OrgID       int64 `xorm:"INDEX"` | ||||
| 	LowerName   string | ||||
| 	Name        string | ||||
| 	Description string | ||||
| 	Authorize   AccessMode | ||||
| 	Repos       []*Repository `xorm:"-"` | ||||
| 	Members     []*User       `xorm:"-"` | ||||
| 	NumRepos    int | ||||
| 	NumMembers  int | ||||
| } | ||||
| 
 | ||||
| // IsOwnerTeam returns true if team is owner team.
 | ||||
| func (t *Team) IsOwnerTeam() bool { | ||||
| 	return t.Name == OWNER_TEAM | ||||
| } | ||||
| 
 | ||||
| // IsTeamMember returns true if given user is a member of team.
 | ||||
| func (t *Team) IsMember(uid int64) bool { | ||||
| 	return IsTeamMember(t.OrgID, t.ID, uid) | ||||
| } | ||||
| 
 | ||||
| func (t *Team) getRepositories(e Engine) (err error) { | ||||
| 	teamRepos := make([]*TeamRepo, 0, t.NumRepos) | ||||
| 	if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil { | ||||
| 		return fmt.Errorf("get team-repos: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	t.Repos = make([]*Repository, 0, len(teamRepos)) | ||||
| 	for i := range teamRepos { | ||||
| 		repo, err := getRepositoryByID(e, teamRepos[i].RepoID) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err) | ||||
| 		} | ||||
| 		t.Repos = append(t.Repos, repo) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // GetRepositories returns all repositories in team of organization.
 | ||||
| func (t *Team) GetRepositories() error { | ||||
| 	return t.getRepositories(x) | ||||
| } | ||||
| 
 | ||||
| func (t *Team) getMembers(e Engine) (err error) { | ||||
| 	t.Members, err = getTeamMembers(e, t.ID) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // GetMembers returns all members in team of organization.
 | ||||
| func (t *Team) GetMembers() (err error) { | ||||
| 	return t.getMembers(x) | ||||
| } | ||||
| 
 | ||||
| // AddMember adds new member to team of organization.
 | ||||
| func (t *Team) AddMember(uid int64) error { | ||||
| 	return AddTeamMember(t.OrgID, t.ID, uid) | ||||
| } | ||||
| 
 | ||||
| // RemoveMember removes member from team of organization.
 | ||||
| func (t *Team) RemoveMember(uid int64) error { | ||||
| 	return RemoveTeamMember(t.OrgID, t.ID, uid) | ||||
| } | ||||
| 
 | ||||
| func (t *Team) hasRepository(e Engine, repoID int64) bool { | ||||
| 	return hasTeamRepo(e, t.OrgID, t.ID, repoID) | ||||
| } | ||||
| 
 | ||||
| // HasRepository returns true if given repository belong to team.
 | ||||
| func (t *Team) HasRepository(repoID int64) bool { | ||||
| 	return t.hasRepository(x, repoID) | ||||
| } | ||||
| 
 | ||||
| func (t *Team) addRepository(e Engine, repo *Repository) (err error) { | ||||
| 	if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	t.NumRepos++ | ||||
| 	if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { | ||||
| 		return fmt.Errorf("update team: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err = repo.recalculateTeamAccesses(e, 0); err != nil { | ||||
| 		return fmt.Errorf("recalculateAccesses: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err = t.getMembers(e); err != nil { | ||||
| 		return fmt.Errorf("getMembers: %v", err) | ||||
| 	} | ||||
| 	for _, u := range t.Members { | ||||
| 		if err = watchRepo(e, u.Id, repo.ID, true); err != nil { | ||||
| 			return fmt.Errorf("watchRepo: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // AddRepository adds new repository to team of organization.
 | ||||
| func (t *Team) AddRepository(repo *Repository) (err error) { | ||||
| 	if repo.OwnerID != t.OrgID { | ||||
| 		return errors.New("Repository does not belong to organization") | ||||
| 	} else if t.HasRepository(repo.ID) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if err = t.addRepository(sess, repo); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) { | ||||
| 	if err = removeTeamRepo(e, t.ID, repo.ID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	t.NumRepos-- | ||||
| 	if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Don't need to recalculate when delete a repository from organization.
 | ||||
| 	if recalculate { | ||||
| 		if err = repo.recalculateTeamAccesses(e, t.ID); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err = t.getMembers(e); err != nil { | ||||
| 		return fmt.Errorf("get team members: %v", err) | ||||
| 	} | ||||
| 	for _, u := range t.Members { | ||||
| 		has, err := hasAccess(e, u, repo, ACCESS_MODE_READ) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} else if has { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		if err = watchRepo(e, u.Id, repo.ID, false); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RemoveRepository removes repository from team of organization.
 | ||||
| func (t *Team) RemoveRepository(repoID int64) error { | ||||
| 	if !t.HasRepository(repoID) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	repo, err := GetRepositoryByID(repoID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if err = t.removeRepository(sess, repo, true); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // NewTeam creates a record of new team.
 | ||||
| // It's caller's responsibility to assign organization ID.
 | ||||
| func NewTeam(t *Team) error { | ||||
| 	if len(t.Name) == 0 { | ||||
| 		return errors.New("empty team name") | ||||
| 	} | ||||
| 
 | ||||
| 	has, err := x.Id(t.OrgID).Get(new(User)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if !has { | ||||
| 		return ErrOrgNotExist | ||||
| 	} | ||||
| 
 | ||||
| 	t.LowerName = strings.ToLower(t.Name) | ||||
| 	has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if has { | ||||
| 		return ErrTeamAlreadyExist{t.OrgID, t.LowerName} | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err = sess.Insert(t); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Update organization number of teams.
 | ||||
| 	if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| func getTeam(e Engine, orgId int64, name string) (*Team, error) { | ||||
| 	t := &Team{ | ||||
| 		OrgID:     orgId, | ||||
| 		LowerName: strings.ToLower(name), | ||||
| 	} | ||||
| 	has, err := e.Get(t) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrTeamNotExist | ||||
| 	} | ||||
| 	return t, nil | ||||
| } | ||||
| 
 | ||||
| // GetTeam returns team by given team name and organization.
 | ||||
| func GetTeam(orgId int64, name string) (*Team, error) { | ||||
| 	return getTeam(x, orgId, name) | ||||
| } | ||||
| 
 | ||||
| func getTeamById(e Engine, teamId int64) (*Team, error) { | ||||
| 	t := new(Team) | ||||
| 	has, err := e.Id(teamId).Get(t) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrTeamNotExist | ||||
| 	} | ||||
| 	return t, nil | ||||
| } | ||||
| 
 | ||||
| // GetTeamById returns team by given ID.
 | ||||
| func GetTeamById(teamId int64) (*Team, error) { | ||||
| 	return getTeamById(x, teamId) | ||||
| } | ||||
| 
 | ||||
| // UpdateTeam updates information of team.
 | ||||
| func UpdateTeam(t *Team, authChanged bool) (err error) { | ||||
| 	if len(t.Name) == 0 { | ||||
| 		return errors.New("empty team name") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(t.Description) > 255 { | ||||
| 		t.Description = t.Description[:255] | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	t.LowerName = strings.ToLower(t.Name) | ||||
| 	has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(new(Team)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if has { | ||||
| 		return ErrTeamAlreadyExist{t.OrgID, t.LowerName} | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil { | ||||
| 		return fmt.Errorf("update: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Update access for team members if needed.
 | ||||
| 	if authChanged { | ||||
| 		if err = t.getRepositories(sess); err != nil { | ||||
| 			return fmt.Errorf("getRepositories:%v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		for _, repo := range t.Repos { | ||||
| 			if err = repo.recalculateTeamAccesses(sess, 0); err != nil { | ||||
| 				return fmt.Errorf("recalculateTeamAccesses: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // DeleteTeam deletes given team.
 | ||||
| // It's caller's responsibility to assign organization ID.
 | ||||
| func DeleteTeam(t *Team) error { | ||||
| 	if err := t.GetRepositories(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Get organization.
 | ||||
| 	org, err := GetUserByID(t.OrgID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete all accesses.
 | ||||
| 	for _, repo := range t.Repos { | ||||
| 		if err = repo.recalculateTeamAccesses(sess, t.ID); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete team-user.
 | ||||
| 	if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete team.
 | ||||
| 	if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// Update organization number of teams.
 | ||||
| 	if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // ___________                    ____ ___
 | ||||
| // \__    ___/___ _____    _____ |    |   \______ ___________
 | ||||
| //   |    |_/ __ \\__  \  /     \|    |   /  ___// __ \_  __ \
 | ||||
| //   |    |\  ___/ / __ \|  Y Y  \    |  /\___ \\  ___/|  | \/
 | ||||
| //   |____| \___  >____  /__|_|  /______//____  >\___  >__|
 | ||||
| //              \/     \/      \/             \/     \/
 | ||||
| 
 | ||||
| // TeamUser represents an team-user relation.
 | ||||
| type TeamUser struct { | ||||
| 	ID     int64 `xorm:"pk autoincr"` | ||||
| 	OrgID  int64 `xorm:"INDEX"` | ||||
| 	TeamID int64 `xorm:"UNIQUE(s)"` | ||||
| 	Uid    int64 `xorm:"UNIQUE(s)"` | ||||
| } | ||||
| 
 | ||||
| func isTeamMember(e Engine, orgID, teamID, uid int64) bool { | ||||
| 	has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser)) | ||||
| 	return has | ||||
| } | ||||
| 
 | ||||
| // IsTeamMember returns true if given user is a member of team.
 | ||||
| func IsTeamMember(orgID, teamID, uid int64) bool { | ||||
| 	return isTeamMember(x, orgID, teamID, uid) | ||||
| } | ||||
| 
 | ||||
| func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) { | ||||
| 	teamUsers := make([]*TeamUser, 0, 10) | ||||
| 	if err = e.Where("team_id=?", teamID).Find(&teamUsers); err != nil { | ||||
| 		return nil, fmt.Errorf("get team-users: %v", err) | ||||
| 	} | ||||
| 	members := make([]*User, 0, len(teamUsers)) | ||||
| 	for i := range teamUsers { | ||||
| 		member := new(User) | ||||
| 		if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil { | ||||
| 			return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err) | ||||
| 		} | ||||
| 		members = append(members, member) | ||||
| 	} | ||||
| 	return members, nil | ||||
| } | ||||
| 
 | ||||
| // GetTeamMembers returns all members in given team of organization.
 | ||||
| func GetTeamMembers(teamID int64) ([]*User, error) { | ||||
| 	return getTeamMembers(x, teamID) | ||||
| } | ||||
| 
 | ||||
| func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) { | ||||
| 	tus := make([]*TeamUser, 0, 5) | ||||
| 	if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	ts := make([]*Team, len(tus)) | ||||
| 	for i, tu := range tus { | ||||
| 		t := new(Team) | ||||
| 		has, err := e.Id(tu.TeamID).Get(t) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} else if !has { | ||||
| 			return nil, ErrTeamNotExist | ||||
| 		} | ||||
| 		ts[i] = t | ||||
| 	} | ||||
| 	return ts, nil | ||||
| } | ||||
| 
 | ||||
| // GetUserTeams returns all teams that user belongs to in given organization.
 | ||||
| func GetUserTeams(orgId, uid int64) ([]*Team, error) { | ||||
| 	return getUserTeams(x, orgId, uid) | ||||
| } | ||||
| 
 | ||||
| // AddTeamMember adds new member to given team of given organization.
 | ||||
| func AddTeamMember(orgId, teamId, uid int64) error { | ||||
| 	if IsTeamMember(orgId, teamId, uid) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	if err := AddOrgUser(orgId, uid); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Get team and its repositories.
 | ||||
| 	t, err := GetTeamById(teamId) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	t.NumMembers++ | ||||
| 
 | ||||
| 	if err = t.GetRepositories(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	tu := &TeamUser{ | ||||
| 		Uid:    uid, | ||||
| 		OrgID:  orgId, | ||||
| 		TeamID: teamId, | ||||
| 	} | ||||
| 	if _, err = sess.Insert(tu); err != nil { | ||||
| 		return err | ||||
| 	} else if _, err = sess.Id(t.ID).Update(t); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Give access to team repositories.
 | ||||
| 	for _, repo := range t.Repos { | ||||
| 		if err = repo.recalculateTeamAccesses(sess, 0); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// We make sure it exists before.
 | ||||
| 	ou := new(OrgUser) | ||||
| 	if _, err = sess.Where("uid=?", uid).And("org_id=?", orgId).Get(ou); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	ou.NumTeams++ | ||||
| 	if t.IsOwnerTeam() { | ||||
| 		ou.IsOwner = true | ||||
| 	} | ||||
| 	if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| func removeTeamMember(e Engine, orgId, teamId, uid int64) error { | ||||
| 	if !isTeamMember(e, orgId, teamId, uid) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Get team and its repositories.
 | ||||
| 	t, err := getTeamById(e, teamId) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if the user to delete is the last member in owner team.
 | ||||
| 	if t.IsOwnerTeam() && t.NumMembers == 1 { | ||||
| 		return ErrLastOrgOwner{UID: uid} | ||||
| 	} | ||||
| 
 | ||||
| 	t.NumMembers-- | ||||
| 
 | ||||
| 	if err = t.getRepositories(e); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Get organization.
 | ||||
| 	org, err := getUserByID(e, orgId) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	tu := &TeamUser{ | ||||
| 		Uid:    uid, | ||||
| 		OrgID:  orgId, | ||||
| 		TeamID: teamId, | ||||
| 	} | ||||
| 	if _, err := e.Delete(tu); err != nil { | ||||
| 		return err | ||||
| 	} else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete access to team repositories.
 | ||||
| 	for _, repo := range t.Repos { | ||||
| 		if err = repo.recalculateTeamAccesses(e, 0); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// This must exist.
 | ||||
| 	ou := new(OrgUser) | ||||
| 	_, err = e.Where("uid=?", uid).And("org_id=?", org.Id).Get(ou) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	ou.NumTeams-- | ||||
| 	if t.IsOwnerTeam() { | ||||
| 		ou.IsOwner = false | ||||
| 	} | ||||
| 	if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RemoveTeamMember removes member from given team of given organization.
 | ||||
| func RemoveTeamMember(orgId, teamId, uid int64) error { | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err := sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := removeTeamMember(sess, orgId, teamId, uid); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // ___________                  __________
 | ||||
| // \__    ___/___ _____    _____\______   \ ____ ______   ____
 | ||||
| //   |    |_/ __ \\__  \  /     \|       _// __ \\____ \ /  _ \
 | ||||
| //   |    |\  ___/ / __ \|  Y Y  \    |   \  ___/|  |_> >  <_> )
 | ||||
| //   |____| \___  >____  /__|_|  /____|_  /\___  >   __/ \____/
 | ||||
| //              \/     \/      \/       \/     \/|__|
 | ||||
| 
 | ||||
| // TeamRepo represents an team-repository relation.
 | ||||
| type TeamRepo struct { | ||||
| 	ID     int64 `xorm:"pk autoincr"` | ||||
| 	OrgID  int64 `xorm:"INDEX"` | ||||
| 	TeamID int64 `xorm:"UNIQUE(s)"` | ||||
| 	RepoID int64 `xorm:"UNIQUE(s)"` | ||||
| } | ||||
| 
 | ||||
| func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool { | ||||
| 	has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo)) | ||||
| 	return has | ||||
| } | ||||
| 
 | ||||
| // HasTeamRepo returns true if given repository belongs to team.
 | ||||
| func HasTeamRepo(orgID, teamID, repoID int64) bool { | ||||
| 	return hasTeamRepo(x, orgID, teamID, repoID) | ||||
| } | ||||
| 
 | ||||
| func addTeamRepo(e Engine, orgID, teamID, repoID int64) error { | ||||
| 	_, err := e.InsertOne(&TeamRepo{ | ||||
| 		OrgID:  orgID, | ||||
| 		TeamID: teamID, | ||||
| 		RepoID: repoID, | ||||
| 	}) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // AddTeamRepo adds new repository relation to team.
 | ||||
| func AddTeamRepo(orgID, teamID, repoID int64) error { | ||||
| 	return addTeamRepo(x, orgID, teamID, repoID) | ||||
| } | ||||
| 
 | ||||
| func removeTeamRepo(e Engine, teamID, repoID int64) error { | ||||
| 	_, err := e.Delete(&TeamRepo{ | ||||
| 		TeamID: teamID, | ||||
| 		RepoID: repoID, | ||||
| 	}) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // RemoveTeamRepo deletes repository relation to team.
 | ||||
| func RemoveTeamRepo(teamID, repoID int64) error { | ||||
| 	return removeTeamRepo(x, teamID, repoID) | ||||
| } | ||||
| 
 | ||||
| func removeOrgRepo(e Engine, orgID, repoID int64) error { | ||||
| 	_, err := e.Delete(&TeamRepo{ | ||||
| 		OrgID:  orgID, | ||||
|  |  | |||
							
								
								
									
										618
									
								
								models/org_team.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										618
									
								
								models/org_team.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,618 @@ | |||
| // Copyright 2016 The Gogs Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a MIT-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package models | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| const OWNER_TEAM = "Owners" | ||||
| 
 | ||||
| // Team represents a organization team.
 | ||||
| type Team struct { | ||||
| 	ID          int64 `xorm:"pk autoincr"` | ||||
| 	OrgID       int64 `xorm:"INDEX"` | ||||
| 	LowerName   string | ||||
| 	Name        string | ||||
| 	Description string | ||||
| 	Authorize   AccessMode | ||||
| 	Repos       []*Repository `xorm:"-"` | ||||
| 	Members     []*User       `xorm:"-"` | ||||
| 	NumRepos    int | ||||
| 	NumMembers  int | ||||
| } | ||||
| 
 | ||||
| // IsOwnerTeam returns true if team is owner team.
 | ||||
| func (t *Team) IsOwnerTeam() bool { | ||||
| 	return t.Name == OWNER_TEAM | ||||
| } | ||||
| 
 | ||||
| // IsTeamMember returns true if given user is a member of team.
 | ||||
| func (t *Team) IsMember(uid int64) bool { | ||||
| 	return IsTeamMember(t.OrgID, t.ID, uid) | ||||
| } | ||||
| 
 | ||||
| func (t *Team) getRepositories(e Engine) (err error) { | ||||
| 	teamRepos := make([]*TeamRepo, 0, t.NumRepos) | ||||
| 	if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil { | ||||
| 		return fmt.Errorf("get team-repos: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	t.Repos = make([]*Repository, 0, len(teamRepos)) | ||||
| 	for i := range teamRepos { | ||||
| 		repo, err := getRepositoryByID(e, teamRepos[i].RepoID) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err) | ||||
| 		} | ||||
| 		t.Repos = append(t.Repos, repo) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // GetRepositories returns all repositories in team of organization.
 | ||||
| func (t *Team) GetRepositories() error { | ||||
| 	return t.getRepositories(x) | ||||
| } | ||||
| 
 | ||||
| func (t *Team) getMembers(e Engine) (err error) { | ||||
| 	t.Members, err = getTeamMembers(e, t.ID) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // GetMembers returns all members in team of organization.
 | ||||
| func (t *Team) GetMembers() (err error) { | ||||
| 	return t.getMembers(x) | ||||
| } | ||||
| 
 | ||||
| // AddMember adds new membership of the team to the organization,
 | ||||
| // the user will have membership to the organization automatically when needed.
 | ||||
| func (t *Team) AddMember(uid int64) error { | ||||
| 	return AddTeamMember(t.OrgID, t.ID, uid) | ||||
| } | ||||
| 
 | ||||
| // RemoveMember removes member from team of organization.
 | ||||
| func (t *Team) RemoveMember(uid int64) error { | ||||
| 	return RemoveTeamMember(t.OrgID, t.ID, uid) | ||||
| } | ||||
| 
 | ||||
| func (t *Team) hasRepository(e Engine, repoID int64) bool { | ||||
| 	return hasTeamRepo(e, t.OrgID, t.ID, repoID) | ||||
| } | ||||
| 
 | ||||
| // HasRepository returns true if given repository belong to team.
 | ||||
| func (t *Team) HasRepository(repoID int64) bool { | ||||
| 	return t.hasRepository(x, repoID) | ||||
| } | ||||
| 
 | ||||
| func (t *Team) addRepository(e Engine, repo *Repository) (err error) { | ||||
| 	if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	t.NumRepos++ | ||||
| 	if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { | ||||
| 		return fmt.Errorf("update team: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err = repo.recalculateTeamAccesses(e, 0); err != nil { | ||||
| 		return fmt.Errorf("recalculateAccesses: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err = t.getMembers(e); err != nil { | ||||
| 		return fmt.Errorf("getMembers: %v", err) | ||||
| 	} | ||||
| 	for _, u := range t.Members { | ||||
| 		if err = watchRepo(e, u.Id, repo.ID, true); err != nil { | ||||
| 			return fmt.Errorf("watchRepo: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // AddRepository adds new repository to team of organization.
 | ||||
| func (t *Team) AddRepository(repo *Repository) (err error) { | ||||
| 	if repo.OwnerID != t.OrgID { | ||||
| 		return errors.New("Repository does not belong to organization") | ||||
| 	} else if t.HasRepository(repo.ID) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if err = t.addRepository(sess, repo); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) { | ||||
| 	if err = removeTeamRepo(e, t.ID, repo.ID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	t.NumRepos-- | ||||
| 	if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Don't need to recalculate when delete a repository from organization.
 | ||||
| 	if recalculate { | ||||
| 		if err = repo.recalculateTeamAccesses(e, t.ID); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err = t.getMembers(e); err != nil { | ||||
| 		return fmt.Errorf("get team members: %v", err) | ||||
| 	} | ||||
| 	for _, u := range t.Members { | ||||
| 		has, err := hasAccess(e, u, repo, ACCESS_MODE_READ) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} else if has { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		if err = watchRepo(e, u.Id, repo.ID, false); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RemoveRepository removes repository from team of organization.
 | ||||
| func (t *Team) RemoveRepository(repoID int64) error { | ||||
| 	if !t.HasRepository(repoID) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	repo, err := GetRepositoryByID(repoID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if err = t.removeRepository(sess, repo, true); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // NewTeam creates a record of new team.
 | ||||
| // It's caller's responsibility to assign organization ID.
 | ||||
| func NewTeam(t *Team) error { | ||||
| 	if len(t.Name) == 0 { | ||||
| 		return errors.New("empty team name") | ||||
| 	} | ||||
| 
 | ||||
| 	has, err := x.Id(t.OrgID).Get(new(User)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if !has { | ||||
| 		return ErrOrgNotExist | ||||
| 	} | ||||
| 
 | ||||
| 	t.LowerName = strings.ToLower(t.Name) | ||||
| 	has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if has { | ||||
| 		return ErrTeamAlreadyExist{t.OrgID, t.LowerName} | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sess.Close() | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err = sess.Insert(t); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Update organization number of teams.
 | ||||
| 	if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { | ||||
| 		sess.Rollback() | ||||
| 		return err | ||||
| 	} | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| func getTeam(e Engine, orgId int64, name string) (*Team, error) { | ||||
| 	t := &Team{ | ||||
| 		OrgID:     orgId, | ||||
| 		LowerName: strings.ToLower(name), | ||||
| 	} | ||||
| 	has, err := e.Get(t) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrTeamNotExist | ||||
| 	} | ||||
| 	return t, nil | ||||
| } | ||||
| 
 | ||||
| // GetTeam returns team by given team name and organization.
 | ||||
| func GetTeam(orgId int64, name string) (*Team, error) { | ||||
| 	return getTeam(x, orgId, name) | ||||
| } | ||||
| 
 | ||||
| func getTeamByID(e Engine, teamId int64) (*Team, error) { | ||||
| 	t := new(Team) | ||||
| 	has, err := e.Id(teamId).Get(t) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !has { | ||||
| 		return nil, ErrTeamNotExist | ||||
| 	} | ||||
| 	return t, nil | ||||
| } | ||||
| 
 | ||||
| // GetTeamByID returns team by given ID.
 | ||||
| func GetTeamByID(teamId int64) (*Team, error) { | ||||
| 	return getTeamByID(x, teamId) | ||||
| } | ||||
| 
 | ||||
| // UpdateTeam updates information of team.
 | ||||
| func UpdateTeam(t *Team, authChanged bool) (err error) { | ||||
| 	if len(t.Name) == 0 { | ||||
| 		return errors.New("empty team name") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(t.Description) > 255 { | ||||
| 		t.Description = t.Description[:255] | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	t.LowerName = strings.ToLower(t.Name) | ||||
| 	has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(new(Team)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} else if has { | ||||
| 		return ErrTeamAlreadyExist{t.OrgID, t.LowerName} | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil { | ||||
| 		return fmt.Errorf("update: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Update access for team members if needed.
 | ||||
| 	if authChanged { | ||||
| 		if err = t.getRepositories(sess); err != nil { | ||||
| 			return fmt.Errorf("getRepositories:%v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		for _, repo := range t.Repos { | ||||
| 			if err = repo.recalculateTeamAccesses(sess, 0); err != nil { | ||||
| 				return fmt.Errorf("recalculateTeamAccesses: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // DeleteTeam deletes given team.
 | ||||
| // It's caller's responsibility to assign organization ID.
 | ||||
| func DeleteTeam(t *Team) error { | ||||
| 	if err := t.GetRepositories(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Get organization.
 | ||||
| 	org, err := GetUserByID(t.OrgID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete all accesses.
 | ||||
| 	for _, repo := range t.Repos { | ||||
| 		if err = repo.recalculateTeamAccesses(sess, t.ID); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete team-user.
 | ||||
| 	if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete team.
 | ||||
| 	if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	// Update organization number of teams.
 | ||||
| 	if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // ___________                    ____ ___
 | ||||
| // \__    ___/___ _____    _____ |    |   \______ ___________
 | ||||
| //   |    |_/ __ \\__  \  /     \|    |   /  ___// __ \_  __ \
 | ||||
| //   |    |\  ___/ / __ \|  Y Y  \    |  /\___ \\  ___/|  | \/
 | ||||
| //   |____| \___  >____  /__|_|  /______//____  >\___  >__|
 | ||||
| //              \/     \/      \/             \/     \/
 | ||||
| 
 | ||||
| // TeamUser represents an team-user relation.
 | ||||
| type TeamUser struct { | ||||
| 	ID     int64 `xorm:"pk autoincr"` | ||||
| 	OrgID  int64 `xorm:"INDEX"` | ||||
| 	TeamID int64 `xorm:"UNIQUE(s)"` | ||||
| 	Uid    int64 `xorm:"UNIQUE(s)"` | ||||
| } | ||||
| 
 | ||||
| func isTeamMember(e Engine, orgID, teamID, uid int64) bool { | ||||
| 	has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser)) | ||||
| 	return has | ||||
| } | ||||
| 
 | ||||
| // IsTeamMember returns true if given user is a member of team.
 | ||||
| func IsTeamMember(orgID, teamID, uid int64) bool { | ||||
| 	return isTeamMember(x, orgID, teamID, uid) | ||||
| } | ||||
| 
 | ||||
| func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) { | ||||
| 	teamUsers := make([]*TeamUser, 0, 10) | ||||
| 	if err = e.Where("team_id=?", teamID).Find(&teamUsers); err != nil { | ||||
| 		return nil, fmt.Errorf("get team-users: %v", err) | ||||
| 	} | ||||
| 	members := make([]*User, 0, len(teamUsers)) | ||||
| 	for i := range teamUsers { | ||||
| 		member := new(User) | ||||
| 		if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil { | ||||
| 			return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err) | ||||
| 		} | ||||
| 		members = append(members, member) | ||||
| 	} | ||||
| 	return members, nil | ||||
| } | ||||
| 
 | ||||
| // GetTeamMembers returns all members in given team of organization.
 | ||||
| func GetTeamMembers(teamID int64) ([]*User, error) { | ||||
| 	return getTeamMembers(x, teamID) | ||||
| } | ||||
| 
 | ||||
| func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) { | ||||
| 	tus := make([]*TeamUser, 0, 5) | ||||
| 	if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	ts := make([]*Team, len(tus)) | ||||
| 	for i, tu := range tus { | ||||
| 		t := new(Team) | ||||
| 		has, err := e.Id(tu.TeamID).Get(t) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} else if !has { | ||||
| 			return nil, ErrTeamNotExist | ||||
| 		} | ||||
| 		ts[i] = t | ||||
| 	} | ||||
| 	return ts, nil | ||||
| } | ||||
| 
 | ||||
| // GetUserTeams returns all teams that user belongs to in given organization.
 | ||||
| func GetUserTeams(orgId, uid int64) ([]*Team, error) { | ||||
| 	return getUserTeams(x, orgId, uid) | ||||
| } | ||||
| 
 | ||||
| // AddTeamMember adds new membership of given team to given organization,
 | ||||
| // the user will have membership to given organization automatically when needed.
 | ||||
| func AddTeamMember(orgID, teamID, uid int64) error { | ||||
| 	if IsTeamMember(orgID, teamID, uid) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	if err := AddOrgUser(orgID, uid); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Get team and its repositories.
 | ||||
| 	t, err := GetTeamByID(teamID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	t.NumMembers++ | ||||
| 
 | ||||
| 	if err = t.GetRepositories(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err = sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	tu := &TeamUser{ | ||||
| 		Uid:    uid, | ||||
| 		OrgID:  orgID, | ||||
| 		TeamID: teamID, | ||||
| 	} | ||||
| 	if _, err = sess.Insert(tu); err != nil { | ||||
| 		return err | ||||
| 	} else if _, err = sess.Id(t.ID).Update(t); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Give access to team repositories.
 | ||||
| 	for _, repo := range t.Repos { | ||||
| 		if err = repo.recalculateTeamAccesses(sess, 0); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// We make sure it exists before.
 | ||||
| 	ou := new(OrgUser) | ||||
| 	if _, err = sess.Where("uid = ?", uid).And("org_id = ?", orgID).Get(ou); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	ou.NumTeams++ | ||||
| 	if t.IsOwnerTeam() { | ||||
| 		ou.IsOwner = true | ||||
| 	} | ||||
| 	if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| func removeTeamMember(e Engine, orgID, teamID, uid int64) error { | ||||
| 	if !isTeamMember(e, orgID, teamID, uid) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Get team and its repositories.
 | ||||
| 	t, err := getTeamByID(e, teamID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if the user to delete is the last member in owner team.
 | ||||
| 	if t.IsOwnerTeam() && t.NumMembers == 1 { | ||||
| 		return ErrLastOrgOwner{UID: uid} | ||||
| 	} | ||||
| 
 | ||||
| 	t.NumMembers-- | ||||
| 
 | ||||
| 	if err = t.getRepositories(e); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Get organization.
 | ||||
| 	org, err := getUserByID(e, orgID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	tu := &TeamUser{ | ||||
| 		Uid:    uid, | ||||
| 		OrgID:  orgID, | ||||
| 		TeamID: teamID, | ||||
| 	} | ||||
| 	if _, err := e.Delete(tu); err != nil { | ||||
| 		return err | ||||
| 	} else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete access to team repositories.
 | ||||
| 	for _, repo := range t.Repos { | ||||
| 		if err = repo.recalculateTeamAccesses(e, 0); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// This must exist.
 | ||||
| 	ou := new(OrgUser) | ||||
| 	_, err = e.Where("uid = ?", uid).And("org_id = ?", org.Id).Get(ou) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	ou.NumTeams-- | ||||
| 	if t.IsOwnerTeam() { | ||||
| 		ou.IsOwner = false | ||||
| 	} | ||||
| 	if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RemoveTeamMember removes member from given team of given organization.
 | ||||
| func RemoveTeamMember(orgID, teamID, uid int64) error { | ||||
| 	sess := x.NewSession() | ||||
| 	defer sessionRelease(sess) | ||||
| 	if err := sess.Begin(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := removeTeamMember(sess, orgID, teamID, uid); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return sess.Commit() | ||||
| } | ||||
| 
 | ||||
| // ___________                  __________
 | ||||
| // \__    ___/___ _____    _____\______   \ ____ ______   ____
 | ||||
| //   |    |_/ __ \\__  \  /     \|       _// __ \\____ \ /  _ \
 | ||||
| //   |    |\  ___/ / __ \|  Y Y  \    |   \  ___/|  |_> >  <_> )
 | ||||
| //   |____| \___  >____  /__|_|  /____|_  /\___  >   __/ \____/
 | ||||
| //              \/     \/      \/       \/     \/|__|
 | ||||
| 
 | ||||
| // TeamRepo represents an team-repository relation.
 | ||||
| type TeamRepo struct { | ||||
| 	ID     int64 `xorm:"pk autoincr"` | ||||
| 	OrgID  int64 `xorm:"INDEX"` | ||||
| 	TeamID int64 `xorm:"UNIQUE(s)"` | ||||
| 	RepoID int64 `xorm:"UNIQUE(s)"` | ||||
| } | ||||
| 
 | ||||
| func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool { | ||||
| 	has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo)) | ||||
| 	return has | ||||
| } | ||||
| 
 | ||||
| // HasTeamRepo returns true if given repository belongs to team.
 | ||||
| func HasTeamRepo(orgID, teamID, repoID int64) bool { | ||||
| 	return hasTeamRepo(x, orgID, teamID, repoID) | ||||
| } | ||||
| 
 | ||||
| func addTeamRepo(e Engine, orgID, teamID, repoID int64) error { | ||||
| 	_, err := e.InsertOne(&TeamRepo{ | ||||
| 		OrgID:  orgID, | ||||
| 		TeamID: teamID, | ||||
| 		RepoID: repoID, | ||||
| 	}) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // AddTeamRepo adds new repository relation to team.
 | ||||
| func AddTeamRepo(orgID, teamID, repoID int64) error { | ||||
| 	return addTeamRepo(x, orgID, teamID, repoID) | ||||
| } | ||||
| 
 | ||||
| func removeTeamRepo(e Engine, teamID, repoID int64) error { | ||||
| 	_, err := e.Delete(&TeamRepo{ | ||||
| 		TeamID: teamID, | ||||
| 		RepoID: repoID, | ||||
| 	}) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // RemoveTeamRepo deletes repository relation to team.
 | ||||
| func RemoveTeamRepo(teamID, repoID int64) error { | ||||
| 	return removeTeamRepo(x, teamID, repoID) | ||||
| } | ||||
|  | @ -18,6 +18,7 @@ import ( | |||
| 
 | ||||
| type APIContext struct { | ||||
| 	*Context | ||||
| 	Org *APIOrganization | ||||
| } | ||||
| 
 | ||||
| // Error responses error message to client with given message.
 | ||||
|  | @ -40,6 +41,7 @@ func (ctx *APIContext) Error(status int, title string, obj interface{}) { | |||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // SetLinkHeader sets pagination link header by given totol number and page size.
 | ||||
| func (ctx *APIContext) SetLinkHeader(total, pageSize int) { | ||||
| 	page := paginater.New(total, pageSize, ctx.QueryInt("page"), 0) | ||||
| 	links := make([]string, 0, 4) | ||||
|  |  | |||
							
								
								
									
										14
									
								
								modules/context/api_org.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								modules/context/api_org.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| // Copyright 2016 The Gogs Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a MIT-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package context | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gogits/gogs/models" | ||||
| ) | ||||
| 
 | ||||
| type APIOrganization struct { | ||||
| 	Organization *models.User | ||||
| 	Team         *models.Team | ||||
| } | ||||
|  | @ -14,13 +14,8 @@ import ( | |||
| ) | ||||
| 
 | ||||
| func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { | ||||
| 	org := user.GetUserByParamsName(ctx, ":orgname") | ||||
| 	if ctx.Written() { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	team := &models.Team{ | ||||
| 		OrgID:       org.Id, | ||||
| 		OrgID:       ctx.Org.Organization.Id, | ||||
| 		Name:        form.Name, | ||||
| 		Description: form.Description, | ||||
| 		Authorize:   models.ParseAccessMode(form.Permission), | ||||
|  | @ -36,3 +31,30 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { | |||
| 
 | ||||
| 	ctx.JSON(201, convert.ToTeam(team)) | ||||
| } | ||||
| 
 | ||||
| func AddTeamMember(ctx *context.APIContext) { | ||||
| 	u := user.GetUserByParams(ctx) | ||||
| 	if ctx.Written() { | ||||
| 		return | ||||
| 	} | ||||
| 	if err := ctx.Org.Team.AddMember(u.Id); err != nil { | ||||
| 		ctx.Error(500, "AddMember", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Status(204) | ||||
| } | ||||
| 
 | ||||
| func RemoveTeamMember(ctx *context.APIContext) { | ||||
| 	u := user.GetUserByParams(ctx) | ||||
| 	if ctx.Written() { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err := ctx.Org.Team.RemoveMember(u.Id); err != nil { | ||||
| 		ctx.Error(500, "RemoveMember", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Status(204) | ||||
| } | ||||
|  |  | |||
|  | @ -110,6 +110,42 @@ func ReqAdmin() macaron.Handler { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func OrgAssignment(args ...bool) macaron.Handler { | ||||
| 	var ( | ||||
| 		assignTeam bool | ||||
| 	) | ||||
| 
 | ||||
| 	if len(args) > 0 { | ||||
| 		assignTeam = args[0] | ||||
| 	} | ||||
| 	return func(ctx *context.APIContext) { | ||||
| 		org, err := models.GetUserByName(ctx.Params(":orgname")) | ||||
| 		if err != nil { | ||||
| 			if models.IsErrUserNotExist(err) { | ||||
| 				ctx.Status(404) | ||||
| 			} else { | ||||
| 				ctx.Error(500, "GetUserByName", err) | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 		ctx.Org = &context.APIOrganization{ | ||||
| 			Organization: org, | ||||
| 		} | ||||
| 
 | ||||
| 		if assignTeam { | ||||
| 			ctx.Org.Team, err = models.GetTeamByID(ctx.ParamsInt64(":teamid")) | ||||
| 			if err != nil { | ||||
| 				if models.IsErrUserNotExist(err) { | ||||
| 					ctx.Status(404) | ||||
| 				} else { | ||||
| 					ctx.Error(500, "GetTeamById", err) | ||||
| 				} | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RegisterRoutes registers all v1 APIs routes to web application.
 | ||||
| // FIXME: custom form error response
 | ||||
| func RegisterRoutes(m *macaron.Macaron) { | ||||
|  | @ -208,7 +244,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| 		m.Group("/orgs/:orgname", func() { | ||||
| 			m.Combo("").Get(org.Get).Patch(bind(api.EditOrgOption{}), org.Edit) | ||||
| 			m.Combo("/teams").Get(org.ListTeams) | ||||
| 		}) | ||||
| 		}, OrgAssignment()) | ||||
| 
 | ||||
| 		m.Any("/*", func(ctx *context.Context) { | ||||
| 			ctx.Error(404) | ||||
|  | @ -228,7 +264,13 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| 			}) | ||||
| 
 | ||||
| 			m.Group("/orgs/:orgname", func() { | ||||
| 				m.Combo("/teams").Post(bind(api.CreateTeamOption{}), admin.CreateTeam) | ||||
| 				m.Group("/teams", func() { | ||||
| 					m.Post("", OrgAssignment(), bind(api.CreateTeamOption{}), admin.CreateTeam) | ||||
| 
 | ||||
| 					m.Group("/:teamid", func() { | ||||
| 						m.Combo("/memberships/:username").Put(admin.AddTeamMember).Delete(admin.RemoveTeamMember) | ||||
| 					}, OrgAssignment(true)) | ||||
| 				}) | ||||
| 			}) | ||||
| 		}, ReqAdmin()) | ||||
| 	}, context.APIContexter()) | ||||
|  |  | |||
|  | @ -42,20 +42,12 @@ func ListUserOrgs(ctx *context.APIContext) { | |||
| 
 | ||||
| // https://github.com/gogits/go-gogs-client/wiki/Organizations#get-an-organization
 | ||||
| func Get(ctx *context.APIContext) { | ||||
| 	org := user.GetUserByParamsName(ctx, ":orgname") | ||||
| 	if ctx.Written() { | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.JSON(200, convert.ToOrganization(org)) | ||||
| 	ctx.JSON(200, convert.ToOrganization(ctx.Org.Organization)) | ||||
| } | ||||
| 
 | ||||
| // https://github.com/gogits/go-gogs-client/wiki/Organizations#edit-an-organization
 | ||||
| func Edit(ctx *context.APIContext, form api.EditOrgOption) { | ||||
| 	org := user.GetUserByParamsName(ctx, ":orgname") | ||||
| 	if ctx.Written() { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	org := ctx.Org.Organization | ||||
| 	if !org.IsOwnedBy(ctx.User.Id) { | ||||
| 		ctx.Status(403) | ||||
| 		return | ||||
|  |  | |||
|  | @ -9,15 +9,10 @@ import ( | |||
| 
 | ||||
| 	"github.com/gogits/gogs/modules/context" | ||||
| 	"github.com/gogits/gogs/routers/api/v1/convert" | ||||
| 	"github.com/gogits/gogs/routers/api/v1/user" | ||||
| ) | ||||
| 
 | ||||
| func ListTeams(ctx *context.APIContext) { | ||||
| 	org := user.GetUserByParamsName(ctx, ":orgname") | ||||
| 	if ctx.Written() { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	org := ctx.Org.Organization | ||||
| 	if err := org.GetTeams(); err != nil { | ||||
| 		ctx.Error(500, "GetTeams", err) | ||||
| 		return | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| 0.9.15.0323 | ||||
| 0.9.16.0325 | ||||
		Loading…
	
		Reference in a new issue