Last active
February 28, 2023 12:38
-
-
Save vtbassmatt/d80065f63cedaec6039ced3dfaeb900f to your computer and use it in GitHub Desktop.
Azure Repos Git security token - Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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