Skip to content

Instantly share code, notes, and snippets.

@vtbassmatt
Last active February 28, 2023 12:38
Show Gist options
  • Save vtbassmatt/d80065f63cedaec6039ced3dfaeb900f to your computer and use it in GitHub Desktop.
Save vtbassmatt/d80065f63cedaec6039ced3dfaeb900f to your computer and use it in GitHub Desktop.
Azure Repos Git security token - Python
"""
This implements security namespace tokens for Azure Repos (Git).
There is one top-level function, calculate_securable_from_branch().
>>> calculate_securable_from_branch('212d1460-2143-4296-9771-c54336dbf3d3', '393d8e86-ed2b-473f-8480-0cf728c1f866', 'master')
'repoV2/212d1460-2143-4296-9771-c54336dbf3d3/393d8e86-ed2b-473f-8480-0cf728c1f866/refs/heads/6d0061007300740065007200/'
"""
import sys
import uuid
if sys.version_info[0] < 3:
raise "Requires Python 3"
UUID_EMPTY = str(uuid.UUID("00000000-0000-0000-0000-000000000000"))
SECURABLE_ROOT = "repoV2/"
MAX_GIT_REF_NAME_LENGTH = 400
def _namespace_encode(name_part):
name_bytes = bytes(name_part, "utf_16_le")
encoded_part = []
for i in range(0, len(name_bytes)):
b = name_bytes[i]
first = ((b >> 4) & 0x0f) + 0x30
second = (b & 0x0f) + 0x30
for sub_part in [first, second]:
if sub_part >= 0x3a:
encoded_part.append(sub_part + 0x27)
else:
encoded_part.append(sub_part)
return bytes(encoded_part).decode("utf_8")
def _validate_securable_inputs(project_id, repo_id, ref_name, ref_prefix):
project_id = str(uuid.UUID(project_id))
repo_id = str(uuid.UUID(repo_id))
ref_name = str(ref_name)
ref_prefix = str(ref_prefix)
# if you pass in a repo_id, you must pass in a team project
assert project_id != UUID_EMPTY or repo_id == UUID_EMPTY
# if you pass in a ref name, you must pass a repo uuid
assert ref_name == "" or repo_id != UUID_EMPTY
# ref_prefix must be lower case, contain 2 slashes, and end with a slash
assert ref_prefix == ref_prefix.lower()
assert len(ref_prefix.split("/")) == 3
assert ref_prefix.endswith("/")
# total ref name length must be under a certain size
assert len(ref_prefix + ref_name) <= MAX_GIT_REF_NAME_LENGTH
return project_id, repo_id, ref_name, ref_prefix
def _calculate_securable(project_id, repo_id, ref_name, ref_prefix):
project_id, repo_id, ref_name, ref_prefix = _validate_securable_inputs(project_id, repo_id, ref_name, ref_prefix)
securable = [SECURABLE_ROOT,]
if project_id != UUID_EMPTY:
securable.append(project_id)
securable.append("/")
if repo_id != UUID_EMPTY:
securable.append(repo_id)
securable.append("/")
if ref_name != "":
ref_name = ref_name.rstrip("/")
securable.append(str(ref_prefix))
name_parts = ref_name.split("/")
for part in name_parts:
securable.append(_namespace_encode(part))
securable.append("/")
return "".join(securable)
def calculate_securable_from_branch(project_id, repo_id, branch_name):
return _calculate_securable(project_id, repo_id, branch_name, "refs/heads/")
if __name__ == "__main__":
calculate_securable_from_branch('212d1460-2143-4296-9771-c54336dbf3d3', '393d8e86-ed2b-473f-8480-0cf728c1f866', 'master')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment