Merge pull request 'feat(api): add branch prefixes to tokens' (#6) from feat/token-branch-prefixes into main
Reviewed-on: https://git.bnema.dev/bnema/ermite-api/pulls/6
2 files · +52 -10
M gen/ermite/token.pb.go +38 -9 generated file: collapsed
M proto/ermite/token.proto +14 -1
| @@ -58,13 +58,19 @@ message Token { | |||
| 58 | 58 | // LastUsedAt is when the token last authenticated a request. Unset if never | |
| 59 | 59 | // used. It may lag behind the real time by a short delay. | |
| 60 | 60 | google.protobuf.Timestamp last_used_at = 7; | |
| 61 | + | // BranchPrefixes limits the branches the token may write. Each prefix ends | |
| 62 | + | // with "/" (for example "agent/claude/"). Empty means every branch. A limited | |
| 63 | + | // token can push, force-push and delete only branches under its prefixes; it | |
| 64 | + | // cannot push tags or other refs, create repositories, or merge into or | |
| 65 | + | // delete a branch outside its prefixes. | |
| 66 | + | repeated string branch_prefixes = 8; | |
| 61 | 67 | } | |
| 62 | 68 | ||
| 63 | 69 | // TokenService manages stored tokens. Every RPC needs the token:admin scope. | |
| 64 | 70 | // | |
| 65 | 71 | // A token can only create tokens that are within its own limits: scopes that | |
| 66 | 72 | // it holds, repositories that it may access and an expiry no later than its | |
| 67 | - | // own. The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never | |
| 73 | + | // own (including its branch prefixes). The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never | |
| 68 | 74 | // listed. | |
| 69 | 75 | service TokenService { | |
| 70 | 76 | // CreateToken creates a token and returns its secret. The secret is shown | |
| @@ -92,6 +98,13 @@ message CreateTokenRequest { | |||
| 92 | 98 | // ExpiresAt is when the token stops working. Unset means no expiry. It must | |
| 93 | 99 | // be in the future. | |
| 94 | 100 | google.protobuf.Timestamp expires_at = 4; | |
| 101 | + | // BranchPrefixes limits the branches the token may write, for example | |
| 102 | + | // "agent/claude/". Each prefix must end with "/" and be a valid branch name | |
| 103 | + | // once the "/" is removed; at most 8 are allowed. Empty means every branch; | |
| 104 | + | // a limited token cannot push tags or create repositories. A token that has | |
| 105 | + | // prefixes can only create tokens whose prefixes each lie within its own. | |
| 106 | + | // Fails with invalid_argument when a prefix is malformed. | |
| 107 | + | repeated string branch_prefixes = 5; | |
| 95 | 108 | } | |
| 96 | 109 | ||
| 97 | 110 | // CreateTokenResponse holds the new token and its secret. | |
| @@ -58,13 +58,19 @@ message Token { | |||||
| 58 | // LastUsedAt is when the token last authenticated a request. Unset if never | 58 | // LastUsedAt is when the token last authenticated a request. Unset if never | ||
| 59 | // used. It may lag behind the real time by a short delay. | 59 | // used. It may lag behind the real time by a short delay. | ||
| 60 | google.protobuf.Timestamp last_used_at = 7; | 60 | google.protobuf.Timestamp last_used_at = 7; | ||
| 61 | + | // BranchPrefixes limits the branches the token may write. Each prefix ends | |||
| 62 | + | // with "/" (for example "agent/claude/"). Empty means every branch. A limited | |||
| 63 | + | // token can push, force-push and delete only branches under its prefixes; it | |||
| 64 | + | // cannot push tags or other refs, create repositories, or merge into or | |||
| 65 | + | // delete a branch outside its prefixes. | |||
| 66 | + | repeated string branch_prefixes = 8; | |||
| 61 | } | 67 | } | ||
| 62 | 68 | ||||
| 63 | // TokenService manages stored tokens. Every RPC needs the token:admin scope. | 69 | // TokenService manages stored tokens. Every RPC needs the token:admin scope. | ||
| 64 | // | 70 | // | ||
| 65 | // A token can only create tokens that are within its own limits: scopes that | 71 | // A token can only create tokens that are within its own limits: scopes that | ||
| 66 | // it holds, repositories that it may access and an expiry no later than its | 72 | // it holds, repositories that it may access and an expiry no later than its | ||
| 67 | - | // own. The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never | 73 | + | // own (including its branch prefixes). The root token (ERMITE_ADMIN_TOKEN) is not a stored token and is never |
| 68 | // listed. | 74 | // listed. | ||
| 69 | service TokenService { | 75 | service TokenService { | ||
| 70 | // CreateToken creates a token and returns its secret. The secret is shown | 76 | // CreateToken creates a token and returns its secret. The secret is shown | ||
| @@ -92,6 +98,13 @@ message CreateTokenRequest { | |||||
| 92 | // ExpiresAt is when the token stops working. Unset means no expiry. It must | 98 | // ExpiresAt is when the token stops working. Unset means no expiry. It must | ||
| 93 | // be in the future. | 99 | // be in the future. | ||
| 94 | google.protobuf.Timestamp expires_at = 4; | 100 | google.protobuf.Timestamp expires_at = 4; | ||
| 101 | + | // BranchPrefixes limits the branches the token may write, for example | |||
| 102 | + | // "agent/claude/". Each prefix must end with "/" and be a valid branch name | |||
| 103 | + | // once the "/" is removed; at most 8 are allowed. Empty means every branch; | |||
| 104 | + | // a limited token cannot push tags or create repositories. A token that has | |||
| 105 | + | // prefixes can only create tokens whose prefixes each lie within its own. | |||
| 106 | + | // Fails with invalid_argument when a prefix is malformed. | |||
| 107 | + | repeated string branch_prefixes = 5; | |||
| 95 | } | 108 | } | ||
| 96 | 109 | ||||
| 97 | // CreateTokenResponse holds the new token and its secret. | 110 | // CreateTokenResponse holds the new token and its secret. | ||