> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/go-gitea/gitea/llms.txt
> Use this file to discover all available pages before exploring further.

# Permissions

> Understanding Gitea's permission model and access levels

Gitea uses a comprehensive permission system to control access to organizations, teams, repositories, and repository units. Understanding this permission model is essential for managing access in multi-user environments.

## Permission Hierarchy

Permissions in Gitea are organized in a hierarchy:

```
Organization
  └── Teams
      └── Repositories
          └── Units (Code, Issues, PRs, Wiki, etc.)
```

Users inherit permissions based on their membership in teams and their roles within the organization.

## Access Modes

Gitea defines five core access modes, each granting progressively more permissions:

<CardGroup cols={2}>
  <Card title="None" icon="ban" color="#gray">
    **Level 0** - No access to the resource
  </Card>

  <Card title="Read" icon="book-open" color="#blue">
    **Level 1** - View and clone repositories, read issues and PRs
  </Card>

  <Card title="Write" icon="pen" color="#green">
    **Level 2** - Read access + push code, create issues, comment
  </Card>

  <Card title="Admin" icon="shield" color="#orange">
    **Level 3** - Write access + manage settings, webhooks, collaborators
  </Card>

  <Card title="Owner" icon="crown" color="#purple">
    **Level 4** - Full administrative control over the organization
  </Card>
</CardGroup>

### Access Mode Implementation

```go theme={null}
package perm

// AccessMode specifies the users access mode
type AccessMode int

const (
    AccessModeNone AccessMode = iota // 0: no access

    AccessModeRead  // 1: read access
    AccessModeWrite // 2: write access
    AccessModeAdmin // 3: admin access
    AccessModeOwner // 4: owner access
)

// ToString returns the string representation of the access mode
func (mode AccessMode) ToString() string {
    switch mode {
    case AccessModeRead:
        return "read"
    case AccessModeWrite:
        return "write"
    case AccessModeAdmin:
        return "admin"
    case AccessModeOwner:
        return "owner"
    default:
        return "none"
    }
}
```

Source: `models/perm/access_mode.go:14-39`

## Organization Permissions

### Organization Roles

Within an organization, users can have three roles:

<Tabs>
  <Tab title="Owner">
    **Full Control**

    Owners have complete control over the organization:

    * Create, edit, and delete teams
    * Add and remove members
    * Manage organization settings
    * Access all repositories
    * Delete the organization
    * Manage webhooks, labels, and packages

    ```go theme={null}
    // 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)
    }
    ```

    Source: `models/organization/org_user.go:61-72`
  </Tab>

  <Tab title="Admin">
    **Team Management**

    Admins can manage teams but have limited organization control:

    * Manage team membership
    * Access repositories based on team membership
    * Cannot delete the organization
    * Cannot modify organization settings

    ```go theme={null}
    // 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
    }
    ```

    Source: `models/organization/org_user.go:74-86`
  </Tab>

  <Tab title="Member">
    **Team-Based Access**

    Regular members:

    * Access only repositories their teams grant access to
    * Cannot manage teams or organization settings
    * Can be assigned to issues/PRs in accessible repositories
    * Visibility depends on membership publicity setting

    ```go theme={null}
    // 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()
    }
    ```

    Source: `models/organization/org_user.go:88-95`
  </Tab>
</Tabs>

## Team Permissions

Teams provide the primary mechanism for granting repository access within organizations.

### Base Team Permissions

Each team has a base access mode that applies to all repositories it has access to:

```go theme={null}
type Team struct {
    ID                      int64 `xorm:"pk autoincr"`
    OrgID                   int64 `xorm:"INDEX"`
    Name                    string
    Description             string
    AccessMode              perm.AccessMode    `xorm:"'authorize'"`
    NumRepos                int
    NumMembers              int
    IncludesAllRepositories bool        `xorm:"NOT NULL DEFAULT false"`
    CanCreateOrgRepo        bool        `xorm:"NOT NULL DEFAULT false"`
}
```

Source: `models/organization/team.go:74-86`

### Permission Inheritance

When a user belongs to multiple teams:

<Steps>
  <Step title="Collect all teams">
    Identify all teams the user belongs to within the organization
  </Step>

  <Step title="Gather repository access">
    For a specific repository, collect permissions from all teams that have access
  </Step>

  <Step title="Apply highest permission">
    The user receives the highest permission level granted by any team
  </Step>
</Steps>

Example:

* User is in **Team A** (read access to repo)
* User is in **Team B** (write access to repo)
* **Result**: User has write access

### Maximum Team Authorization

The system calculates the maximum authorization level across teams:

```go theme={null}
// GetOrgUserMaxAuthorizeLevel returns highest authorize level of user in an organization
func (org *Organization) GetOrgUserMaxAuthorizeLevel(ctx context.Context, uid int64) (perm.AccessMode, error) {
    var authorize perm.AccessMode
    _, err := db.GetEngine(ctx).
        Select("max(team.authorize)").
        Table("team").
        Join("INNER", "team_user", "team_user.team_id = team.id").
        Where("team_user.uid = ?", uid).
        And("team_user.org_id = ?", org.ID).
        Get(&authorize)
    return authorize, err
}
```

Source: `models/organization/org.go:396-407`

## Repository Unit Permissions

Repositories are divided into units, each with independent permission controls:

### Available Units

| Unit                 | Description                 | Access Levels      |
| -------------------- | --------------------------- | ------------------ |
| **Code**             | Git repository access       | Read, Write, Admin |
| **Issues**           | Issue tracking              | Read, Write        |
| **Pull Requests**    | Code review and merging     | Read, Write        |
| **Releases**         | Release management          | Read, Write        |
| **Wiki**             | Repository wiki             | Read, Write        |
| **External Wiki**    | External wiki link          | Read only          |
| **External Tracker** | External issue tracker link | Read only          |
| **Projects**         | Project boards              | Read, Write        |
| **Packages**         | Package registry            | Read, Write        |
| **Actions**          | CI/CD workflows             | Read, Write        |

### Unit-Level Access Control

```go theme={null}
// TeamUnit describes all units of a repository
type TeamUnit struct {
    ID         int64     `xorm:"pk autoincr"`
    OrgID      int64     `xorm:"INDEX"`
    TeamID     int64     `xorm:"UNIQUE(s)"`
    Type       unit.Type `xorm:"UNIQUE(s)"`
    AccessMode perm.AccessMode
}

// UnitAccessMode returns the access mode for the given unit type
func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode {
    accessMode, _ := t.UnitAccessModeEx(ctx, tp)
    return accessMode
}

func (t *Team) UnitAccessModeEx(ctx context.Context, tp unit.Type) (accessMode perm.AccessMode, exist bool) {
    if err := t.LoadUnits(ctx); err != nil {
        log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
    }
    for _, u := range t.Units {
        if u.Type == tp {
            return u.AccessMode, true
        }
    }
    return perm.AccessModeNone, false
}
```

Source: `models/organization/team.go:174-189` and `models/organization/team_unit.go:14-21`

<Note>
  Unit-level permissions enable scenarios like:

  * Developers with write access to code but read-only access to issues
  * Support team with write access to issues but read-only access to code
  * Documentation team with write access to wiki only
</Note>

## Organization Visibility and Access

Organization visibility affects who can see the organization and its repositories:

```go theme={null}
// HasOrgOrUserVisible tells if the given user can see the given org or user
func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
    // If user is nil, it's an anonymous user/request
    if user == nil || user.IsGhost() {
        return orgOrUser.Visibility == structs.VisibleTypePublic
    }

    if user.IsAdmin || orgOrUser.ID == user.ID {
        return true
    }

    if !setting.Service.RequireSignInViewStrict && orgOrUser.Visibility == structs.VisibleTypePublic {
        return true
    }

    if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
        return false
    }
    return true
}
```

Source: `models/organization/org.go:421-440`

### Visibility Rules

<CardGroup cols={3}>
  <Card title="Public Organization" icon="globe">
    * Visible to everyone
    * Repositories follow their own visibility
    * Anonymous users can view public org page
  </Card>

  <Card title="Limited Organization" icon="users">
    * Visible to authenticated users
    * Hidden from anonymous visitors
    * Repositories accessible to signed-in users
  </Card>

  <Card title="Private Organization" icon="lock">
    * Visible only to members
    * All repositories effectively private
    * Only members can view org page
  </Card>
</CardGroup>

## Repository Creation Permissions

Teams can be granted permission to create repositories:

```go theme={null}
// 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}).
        Join("INNER", "team_user", "team_user.team_id = team.id").
        And("team_user.uid = ?", uid).
        And("team_user.org_id = ?", orgID).
        Exist(new(Team))
}
```

Source: `models/organization/org_user.go:107-115`

<Tip>
  The `CanCreateOrgRepo` flag allows delegating repository creation to trusted teams without granting full owner permissions.
</Tip>

## Permission Checking Best Practices

### 1. Always Check at the Lowest Level

Check permissions for the specific action being performed:

```go theme={null}
// Check unit-specific permission
if !team.UnitEnabled(ctx, unit.TypeIssues) {
    return ErrNoAccess
}
```

### 2. Use Highest Permission When Multiple Teams

When a user belongs to multiple teams, always use the maximum permission:

```go theme={null}
// UnitMaxAccess returns the max access mode for a unit across multiple teams
func (teams TeamList) UnitMaxAccess(tp unit.Type) perm.AccessMode {
    maxAccess := perm.AccessModeNone
    for _, t := range teams {
        if accessMode := t.UnitAccessMode(ctx, tp); accessMode > maxAccess {
            maxAccess = accessMode
        }
    }
    return maxAccess
}
```

### 3. Consider Organization Visibility

Always check if a user can see the organization before checking repository permissions.

### 4. Handle Special Cases

* Site administrators always have full access
* Blocked users have no access
* Restricted users have limited visibility

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="What happens when a user is in multiple teams with different permissions?">
    The user receives the highest permission level granted by any of their teams. For example, if Team A grants read access and Team B grants write access, the user will have write access.
  </Accordion>

  <Accordion title="Can a team have different permissions for different repositories?">
    No, a team's permission settings apply to all repositories it has access to. To achieve different permissions for different repositories, create separate teams.
  </Accordion>

  <Accordion title="What's the difference between Admin and Owner access?">
    **Owner** (level 4) is the highest level and is reserved for organization ownership. Owners can manage all aspects of the organization including deletion. **Admin** (level 3) provides administrative access to repositories but not to organization-level settings.
  </Accordion>

  <Accordion title="Can team permissions override owner permissions?">
    No, organization owners always have full access to all repositories and settings, regardless of team membership.
  </Accordion>

  <Accordion title="How do external wiki and tracker permissions work?">
    External wiki and external tracker units can only have read permissions since they link to external services. Even if a team has admin access, these units remain read-only.
  </Accordion>

  <Accordion title="What permissions does a restricted user have?">
    Restricted users can only see organizations they are explicitly members of and can only interact with repositories they have been explicitly granted access to through team membership. They cannot see public organizations or repositories unless they are members.
  </Accordion>

  <Accordion title="How are permissions recalculated when team settings change?">
    When team permissions change, Gitea automatically recalculates access for all team members across all team repositories. This ensures permissions are immediately updated.

    ```go theme={null}
    // Update access for team members if needed
    if authChanged {
        repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
            TeamID: t.ID,
        })
        if err != nil {
            return fmt.Errorf("GetTeamRepositories: %w", err)
        }

        for _, repo := range repos {
            if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
                return fmt.Errorf("recalculateTeamAccesses: %w", err)
            }
        }
    }
    ```

    Source: `services/org/team.go:133-146`
  </Accordion>
</AccordionGroup>

## Permission Reference Table

### Organization Level

| Action                | Owner | Admin | Member |
| --------------------- | ----- | ----- | ------ |
| View organization     | ✓     | ✓     | ✓      |
| Create teams          | ✓     | ✗     | ✗      |
| Delete teams          | ✓     | ✗     | ✗      |
| Manage members        | ✓     | ✗     | ✗      |
| Organization settings | ✓     | ✗     | ✗      |
| Delete organization   | ✓     | ✗     | ✗      |

### Repository Level (per team)

| Action              | Owner | Admin | Write | Read       |
| ------------------- | ----- | ----- | ----- | ---------- |
| View code           | ✓     | ✓     | ✓     | ✓          |
| Clone repository    | ✓     | ✓     | ✓     | ✓          |
| Push code           | ✓     | ✓     | ✓     | ✗          |
| Create issues       | ✓     | ✓     | ✓     | Unit-based |
| Close issues        | ✓     | ✓     | ✓     | ✗          |
| Merge PRs           | ✓     | ✓     | ✓     | ✗          |
| Repository settings | ✓     | ✓     | ✗     | ✗          |
| Manage webhooks     | ✓     | ✓     | ✗     | ✗          |
| Delete repository   | ✓     | ✓     | ✗     | ✗          |

## Related Resources

* [Organizations](/users/organizations) - Creating and managing organizations
* [Teams](/users/teams) - Team management within organizations
* [Repository Collaborators](/repositories/collaborators) - Direct repository access
* [Branch Protection](/repositories/branch-protection) - Protecting branches with rules
