Files
zeekayandHanzo Dev aa87442fdd Record upstream authorship centrally in NOTICE instead of in every file header
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the
Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in
NOTICE, alongside LICENSE. The header lint accepts any copyright line above the
SPDX line, so it no longer demands an upstream one.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-10 17:50:35 -07:00

154 lines
4.5 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package organization
import (
"context"
"fmt"
"github.com/hanzoai/git/models/db"
"github.com/hanzoai/git/models/perm"
user_model "github.com/hanzoai/git/models/user"
"github.com/hanzoai/git/modules/log"
"github.com/hanzoai/builder"
)
// ________ ____ ___
// \_____ \_______ ____ | | \______ ___________
// / | \_ __ \/ ___\| | / ___// __ \_ __ \
// / | \ | \/ /_/ > | /\___ \\ ___/| | \/
// \_______ /__| \___ /|______//____ >\___ >__|
// \/ /_____/ \/ \/
// OrgUser represents an organization-user relation.
type OrgUser struct {
ID int64 `xorm:"pk autoincr"`
UID int64 `xorm:"INDEX UNIQUE(s)"`
OrgID int64 `xorm:"INDEX UNIQUE(s)"`
IsPublic bool `xorm:"INDEX"`
}
func init() {
db.RegisterModel(new(OrgUser))
}
// ErrUserHasOrgs represents a "UserHasOrgs" kind of error.
type ErrUserHasOrgs struct {
UID int64
}
// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs.
func IsErrUserHasOrgs(err error) bool {
_, ok := err.(ErrUserHasOrgs)
return ok
}
func (err ErrUserHasOrgs) Error() string {
return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
}
// GetOrganizationCount returns count of membership of organization of the user.
func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error) {
return db.GetEngine(ctx).
Where("uid=?", u.ID).
Count(new(OrgUser))
}
// IsOrganizationOwner returns true if given user is in the owner team.
func IsOrganizationOwner(ctx context.Context, orgID, uid int64) (bool, error) {
ownerTeam, err := GetOwnerTeam(ctx, orgID)
if err != nil {
if IsErrTeamNotExist(err) {
log.Error("Organization does not have owner team: %d", orgID)
return false, nil
}
return false, err
}
return IsTeamMember(ctx, orgID, ownerTeam.ID, uid)
}
// IsOrganizationAdmin returns true if given user is in the owner team or an admin team.
func IsOrganizationAdmin(ctx context.Context, orgID, uid int64) (bool, error) {
teams, err := GetUserOrgTeams(ctx, orgID, uid)
if err != nil {
return false, err
}
for _, t := range teams {
if t.HasAdminAccess() {
return true, nil
}
}
return false, nil
}
// IsOrganizationMember returns true if given user is member of organization.
func IsOrganizationMember(ctx context.Context, orgID, uid int64) (bool, error) {
return db.GetEngine(ctx).
Where("uid=?", uid).
And("org_id=?", orgID).
Table("org_user").
Exist()
}
// IsPublicMembership returns true if the given user's membership of given org is public.
func IsPublicMembership(ctx context.Context, orgID, uid int64) (bool, error) {
return db.GetEngine(ctx).
Where("uid=?", uid).
And("org_id=?", orgID).
And("is_public=?", true).
Table("org_user").
Exist()
}
// CanCreateOrgRepo returns true if user can create repo in organization
func CanCreateOrgRepo(ctx context.Context, orgID, uid int64) (bool, error) {
return db.GetEngine(ctx).
Where(builder.Eq{"team.can_create_org_repo": true}.Or(builder.Eq{"team.authorize": perm.AccessModeOwner})).
Join("INNER", "team_user", "team_user.team_id = team.id").
And("team_user.uid = ?", uid).
And("team_user.org_id = ?", orgID).
Exist(new(Team))
}
// IsUserOrgOwner returns true if user is in the owner team of given organization.
func IsUserOrgOwner(ctx context.Context, users user_model.UserList, orgID int64) map[int64]bool {
results := make(map[int64]bool, len(users))
for _, user := range users {
results[user.ID] = false // Set default to false
}
ownerMaps, err := loadOrganizationOwners(ctx, users, orgID)
if err == nil {
for _, owner := range ownerMaps {
results[owner.UID] = true
}
}
return results
}
func loadOrganizationOwners(ctx context.Context, users user_model.UserList, orgID int64) (map[int64]*TeamUser, error) {
if len(users) == 0 {
return nil, nil //nolint:nilnil // return nil when there are no users
}
ownerTeam, err := GetOwnerTeam(ctx, orgID)
if err != nil {
if IsErrTeamNotExist(err) {
log.Error("Organization does not have owner team: %d", orgID)
return nil, nil //nolint:nilnil // return nil when owner team does not exist
}
return nil, err
}
userIDs := users.GetUserIDs()
ownerMaps := make(map[int64]*TeamUser)
err = db.GetEngine(ctx).In("uid", userIDs).
And("org_id=?", orgID).
And("team_id=?", ownerTeam.ID).
Find(&ownerMaps)
if err != nil {
return nil, fmt.Errorf("find team users: %w", err)
}
return ownerMaps, nil
}