Skip to content

Latest commit

 

History

History
221 lines (180 loc) · 13.1 KB

bitbucket.md

File metadata and controls

221 lines (180 loc) · 13.1 KB

bitbucket

One connection is one Bitbucket Cloud workspace (edition: cloud, the default) or one Bitbucket Data Center instance (edition: datacenter). Both answer the same actions.

Cloud: toolpass finds the user among the workspace's members by email, reads the user's effective repository permission (the highest of direct, group and project grants, as Bitbucket computes it), the user's explicit project permission, whether the user is a workspace owner, and for @branch questions the repository's branch restrictions. Data Center: toolpass finds the user by email, lists the groups the user belongs to, and combines the direct, group, project, project-default, public and global grants into the effective level itself, then the ref restrictions for @branch questions. Every call is a read with toolpass's own token; nothing is written.

Credential

Cloud

A workspace access token (Premium) with the read repository, project and account (workspace) scopes, sent as a bearer. Reading repository permissions and branch restrictions needs admin on the repository (repository:admin), and reading explicit project permissions needs project:admin; a workspace access token holds those for the whole workspace. Alternatively an Atlassian API token of a workspace administrator with auth_mode: basic and username set to the account's email. App passwords no longer work.

Filtering members by email, which is how the user is found, is allowed only for a workspace administrator, an integration or a workspace access token.

Data Center

An HTTP access token of a user with the global ADMIN permission, sent as a bearer. Listing repository and project permissions needs REPO_ADMIN or PROJECT_ADMIN on each object, listing global permissions needs ADMIN, and listing a user's groups needs LICENSED_USER; a global administrator has them all. With a lesser token, objects and grants toolpass cannot read answer unknown, never deny.

toolpass probe checks the token: on Cloud that it can look members up by email, on Data Center that it authenticates and whether it can read global permissions.

Connection

  - id: bitbucket-acme
    integration: bitbucket
    workspace: acme                          # Cloud: every resource is in this workspace
    credential: env:BITBUCKET_TOKEN          # workspace access token
    # auth_mode: basic                       # Atlassian API token instead: username + credential
    # username: bot@acme.com

  - id: bitbucket-dc
    integration: bitbucket
    edition: datacenter
    url: https://bitbucket.acme.internal
    credential: env:BITBUCKET_DC_TOKEN       # HTTP access token of an administrator
Key Meaning
edition cloud (default) or datacenter
url Data Center base URL, required there; Cloud default https://api.bitbucket.org
workspace Cloud only, required: the workspace slug
auth_mode bearer (default): Authorization: Bearer; basic: HTTP Basic with username and the token
username auth_mode: basic: the Atlassian account email the API token belongs to
credential the token, env: or file:

Identity

Cloud: GET /2.0/workspaces/{workspace}/members?q=user.email IN ("<email>") with fields=values.user.email,values.user.account_id,.... The address is lowercased and must be a plain email (no quotes, so the filter cannot be broken out of); the member whose email equals it is the user and the Atlassian account_id is the identity. No member is user_not_found, two are user_ambiguous. A person with a Bitbucket account who is not in the workspace is user_not_found.

Data Center: GET /rest/api/latest/users?filter=<email>, every page; the filter is a substring match on name and email, so only the record whose emailAddress equals the address counts. The user name is the identity; active: false is denied every action. The user's groups come from GET /rest/api/latest/admin/users/more-members?context=<name>; when that answers 403 the identity still resolves, and any group grant that would change the answer makes it unknown.

Groups sent by the caller are ignored.

Resources

Resource Cloud Data Center
repo:<slug>[@branch] a repository of the workspace not accepted
repo:<PROJECT>/<slug>[@branch] not accepted a repository; ~user keys are personal projects
project:<key> a project of the workspace a project
workspace the workspace the instance

Slugs and keys are [A-Za-z0-9][A-Za-z0-9._-]* (or a Cloud {uuid}); a branch name follows git's rules (no .., no leading -, no ?*[\^~: or spaces). Everything is path-escaped before it reaches a URL.

Actions

Action Needs Resource
repo.read read repo
repo.push write; on @branch, no push restriction (Cloud) or read-only / pull-request-only restriction (Data Center) that stops the user repo
pr.merge write; on @branch, no restrict_merges restriction (Cloud) or read-only restriction (Data Center) that stops the user repo
repo.admin admin repo
project.read / project.write / project.admin read / write / admin on the project project
repo.create Cloud: create-repo (or admin); Data Center: project admin, see Unverified project
workspace.member Cloud: member of the workspace; Data Center: an active user workspace
workspace.admin Cloud: workspace owner; Data Center: global ADMIN or SYS_ADMIN workspace

Cloud evaluation

  • Repository: GET /2.0/repositories/{ws}/{slug} (404 is resource_not_visible), then GET /2.0/workspaces/{ws}/permissions/repositories/{slug}?q=user.account_id="<id>", whose entries are effective permissions. A Bitbucket that rejects the filter (400) is read whole. No entry: a workspace owner has admin, a public repository grants read, otherwise none.
  • Branch: GET /2.0/repositories/{ws}/{slug}/branch-restrictions?kind=push|restrict_merges. A restriction whose glob pattern matches the branch stops the user unless the user is in its users; a matching restriction that exempts groups is unknown, since Cloud exposes no group membership. branching_model restrictions and patterns with []{} are unknown.
  • Project: GET .../projects/{key}/permissions-config/users/{account_id} is the explicit permission. When it does not suffice, a public project grants read and a workspace owner is allowed; otherwise the project's group permissions are read and a group that would suffice makes the answer unknown.
  • Workspace: GET .../members/{account_id} (404 is not a member); owners from GET .../permissions?q=permission="owner".

Data Center evaluation

  • The level is the highest of: the user's direct grant and the grants of the user's groups on the repository (.../repos/{slug}/permissions/users?filter=<name> exact-matched, and /permissions/groups), the same on the project, the project's default permission (.../permissions/PROJECT_READ|WRITE|ADMIN/all), public on the repository or project (read), and the global permissions (/rest/api/latest/admin/permissions/users|groups, where ADMIN and SYS_ADMIN carry admin everywhere). Project levels carry to repositories.
  • Branch: GET /rest/branch-permissions/2.0/projects/{key}/repos/{slug}/restrictions and the project's own .../projects/{key}/restrictions, which every repository inherits. read-only stops pushes and merges, pull-request-only stops direct pushes; ANY_REF matchers match every branch, BRANCH matchers compare the name, PATTERN matchers are globs; the listed users and groups are exempt. Branching-model matchers are unknown.

Globs are Ant-style: * and ? stay within one path segment, ** crosses segments, and refs/heads/ is stripped from a pattern. Bitbucket Cloud does not document whether its * crosses /, and neither edition documents whether a pattern without / matches a branch of that name inside a folder (main against release/main), so a pattern whose readings disagree for the branch asked about answers unknown rather than guessing. Character classes and alternations are unknown on both editions.

Decisions

Situation toolpass answers
level held, and for @branch no restriction stops the user allow, naming the source (direct, group, project, default, public, owner, global)
level not held deny ("has read, needs write")
a restriction matches and the user is not exempt deny, naming the restriction
a matching restriction exempts a group toolpass cannot resolve (Cloud always; Data Center without LICENSED_USER) unknown (unsupported)
a branching_model restriction, a pattern with character classes, or on Cloud a pattern whose glob semantics are ambiguous for the branch, could apply unknown (unsupported)
Cloud project: explicit permission insufficient, not an owner, a group grant would suffice unknown (unsupported)
Data Center: level insufficient and a group or global grant toolpass could not read would matter unknown (unsupported)
deactivated user (Data Center) deny
no member / user with the email deny (user_not_found)
repository or project answers 404 unknown (resource_not_visible)
401, 403 (the token lacks the permission the read needs; Data Center workspace.admin without ADMIN) unknown (credential_rejected)
a next page outside the API base unknown (upstream_error)
429, 5xx, timeout unknown (upstream_rate_limited / upstream_error / upstream_timeout)

Error bodies are never copied into a decision text.

What it cannot see

  • Cloud group membership: the 2.0 API exposes none, so grants that come only through a group answer unknown (repository permissions are the exception: Bitbucket folds groups into the effective permission itself).
  • Cloud project-level grants folded into a repository answer are trusted as Bitbucket reports them; toolpass does not recompute them.
  • Branching-model branch types, merge checks other than who may merge (approvals, builds, tasks), and Data Center access keys as exemptions.
  • Data Center: the permission filters of /rest/api/latest/users, personal repository visibility rules beyond public, and repository-level default permissions.

Unverified

Marked # UNVERIFIED: in the code:

  • Cloud: the filter grammar q=user.account_id="..." on the repository permissions list; the spec says the list "may be filtered by user" and documents only permission>"read". A 400 falls back to reading the list whole, so a wrong grammar costs calls, not correctness.
  • Cloud: whether * in a branch restriction pattern matches across /; both editions: whether a pattern without / matches inside folders. toolpass answers unknown whenever the readings differ for the branch asked about.
  • Data Center: repository creation in a project is taken to need PROJECT_ADMIN. If Bitbucket lets PROJECT_WRITE create repositories, users with write are denied repo.create although they could.

Observed requests

For toolpass tools, the route table (toolpass routes bitbucket) maps requests to api.bitbucket.org/2.0 (Cloud) or to the instance's /rest/api/1.0, /rest/api/latest and /rest/branch-utils (Data Center) to this integration's actions:

  • Cloud resources belong to the connection's workspace, so a request must name that workspace in its path (/2.0/repositories/<workspace>/..., /2.0/workspaces/<workspace>/...); one naming another workspace, or the same one by UUID or in other letter case, is refused.
  • Reads check repo.read, project.read or workspace.member; hooks, deploy keys, permissions and pipeline variables check repo.admin, project.admin or workspace.admin.
  • repo.push and pr.merge evaluate branch restrictions only when the resource names the branch, so a write that lands on a branch must name it: creating or deleting a branch, committing files (POST /src with one branch field; Data Center PUT /browse/...). Merging a pull request does not name its destination branch, so it is refused rather than checked on write access alone.
  • Workspace reads, code search included (GET /search/code and POST /codesearch), check workspace.member.
  • Opening a pull request, commenting and approving check repo.read; a pull request from another repository (a fork) is refused. Editing or deleting a comment checks repo.admin.

Forks (they land in a project named in the body), moving a repository to another project, Data Center tags (ref restrictions can cover them), Connect app properties and git over HTTPS are not routed.

Test

Unit tests run against fakes of both editions: the Cloud fake validates every request against the official description (bitbucket-cloud in test/specs/fetch.sh, with the undeclared q, fields and paging parameters allowed) and serves members, permissions, branch restrictions and projects; the Data Center fake serves users, groups, grant listings with substring filters, defaults, public flags, restrictions and start/limit paging. There is no live test; after configuring, run toolpass probe and one check for a user you know is allowed.