diff --git a/.flake8 b/.flake8 index 25b890a5..1dcda92d 100644 --- a/.flake8 +++ b/.flake8 @@ -4,6 +4,7 @@ ignore = E203,W503,ANN101,ANN102,S322,ANN206,S201,D105,D107,ANN401 per-file-ignores = src/replit/__init__.py:F401 src/replit/web/__init__.py:F401 + tests/test_identity.py:S105,S106 exclude = src/replit/goval/api diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 00000000..8e794ef3 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,39 @@ +# Upgrade protobufs + +In order to upgrade the protobuf definitions, the following script can be used. + +```shell +#!/bin/bash + +set -e + +GOVAL_DIR="${GOVAL_DIR:-${HOME}/goval}" +WORK_DIR="$(mktemp -d)" + +function cleanup { + rm -rf "${WORK_DIR}" +} +trap cleanup EXIT + +# Download the latest protobuf binaries. +curl -sSL https://github.com/protocolbuffers/protobuf/releases/download/v24.2/protoc-24.2-linux-x86_64.zip -o "${WORK_DIR}/protoc.zip" +(cd "${WORK_DIR}" && unzip protoc.zip) +mkdir -p "${WORK_DIR}/replit/goval" + +rsync \ + -Lazr \ + --exclude='api/npm' \ + --include='*/' \ + --include='*.proto' \ + --exclude='*' \ + "${GOVAL_DIR}/api" \ + "${WORK_DIR}/replit/goval" +find "${WORK_DIR}" -name '*.proto' | xargs sed -i 's@import "api/@import "replit/goval/api/@g' +"${WORK_DIR}/bin/protoc" \ + -I="${WORK_DIR}" \ + --python_out=src/ \ + "${WORK_DIR}/replit/goval/api/signing.proto" \ + "${WORK_DIR}/replit/goval/api/client.proto" \ + "${WORK_DIR}/replit/goval/api/repl/repl.proto" \ + "${WORK_DIR}/replit/goval/api/features/features.proto" +``` diff --git a/pyproject.toml b/pyproject.toml index 2e117976..a24d06df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,3 +51,6 @@ replit = "replit.__main__:cli" exclude = [ "_pb2.py$", # Generated code ] + +[tool.black] +exclude = "src/replit/goval/api" diff --git a/src/replit/goval/api/client_pb2.py b/src/replit/goval/api/client_pb2.py index b51b82fe..2fa2e766 100644 --- a/src/replit/goval/api/client_pb2.py +++ b/src/replit/goval/api/client_pb2.py @@ -2,60 +2,52 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: replit/goval/api/client.proto """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database - +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -from replit.goval.api.repl import ( - repl_pb2 as replit_dot_goval_dot_api_dot_repl_dot_repl__pb2, -) -from replit.goval.api.features import ( - features_pb2 as replit_dot_goval_dot_api_dot_features_dot_features__pb2, -) +from replit.goval.api.repl import repl_pb2 as replit_dot_goval_dot_api_dot_repl_dot_repl__pb2 +from replit.goval.api.features import features_pb2 as replit_dot_goval_dot_api_dot_features_dot_features__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x1dreplit/goval/api/client.proto\x12\x10replit.goval.api\x1a\x1fgoogle/protobuf/timestamp.proto\x1a replit/goval/api/repl/repl.proto\x1a(replit/goval/api/features/features.proto"\xd1\x06\n\tReplToken\x12\'\n\x03iat\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65xp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04salt\x18\x03 \x01(\t\x12\x0f\n\x07\x63luster\x18\x04 \x01(\t\x12\x37\n\x0bpersistence\x18\x06 \x01(\x0e\x32".replit.goval.api.repl.Persistence\x12+\n\x04repl\x18\x07 \x01(\x0b\x32\x1b.replit.goval.api.repl.ReplH\x00\x12\x30\n\x02id\x18\x08 \x01(\x0b\x32".replit.goval.api.ReplToken.ReplIDH\x00\x12\x46\n\tclassroom\x18\t \x01(\x0b\x32-.replit.goval.api.ReplToken.ClassroomMetadataB\x02\x18\x01H\x00\x12=\n\x0eresourceLimits\x18\n \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x36\n\x06\x66ormat\x18\x0c \x01(\x0e\x32&.replit.goval.api.ReplToken.WireFormat\x12\x38\n\tpresenced\x18\r \x01(\x0b\x32%.replit.goval.api.ReplToken.Presenced\x12\r\n\x05\x66lags\x18\x0e \x03(\t\x12\x37\n\x0bpermissions\x18\x0f \x01(\x0b\x32".replit.goval.api.repl.Permissions\x12\x34\n\x08\x66\x65\x61tures\x18\x10 \x03(\x0b\x32".replit.goval.api.features.Feature\x1a\x31\n\x11\x43lassroomMetadata\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08language\x18\x02 \x01(\t\x1a(\n\x06ReplID\x12\n\n\x02id\x18\x01 \x01(\t\x12\x12\n\nsourceRepl\x18\x02 \x01(\t\x1a\x31\n\tPresenced\x12\x10\n\x08\x62\x65\x61rerID\x18\x01 \x01(\r\x12\x12\n\nbearerName\x18\x02 \x01(\t"(\n\nWireFormat\x12\x0c\n\x08PROTOBUF\x10\x00\x12\x0c\n\x04JSON\x10\x01\x1a\x02\x08\x01\x42\n\n\x08metadata".\n\x0eTLSCertificate\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0c\n\x04\x63\x65rt\x18\x02 \x01(\x0c"\x8d\x02\n\x0cReplTransfer\x12)\n\x04repl\x18\x01 \x01(\x0b\x32\x1b.replit.goval.api.repl.Repl\x12\x39\n\nreplLimits\x18\x02 \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x39\n\nuserLimits\x18\x03 \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x15\n\rcustomDomains\x18\x04 \x03(\t\x12\x36\n\x0c\x63\x65rtificates\x18\x05 \x03(\x0b\x32 .replit.goval.api.TLSCertificate\x12\r\n\x05\x66lags\x18\x06 \x03(\t"H\n\x10\x41llowReplRequest\x12\x34\n\x0creplTransfer\x18\x01 \x01(\x0b\x32\x1e.replit.goval.api.ReplTransfer"Y\n\x0f\x43lusterMetadata\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tconmanURL\x18\x02 \x01(\t\x12\x0c\n\x04gurl\x18\x03 \x01(\t\x12\r\n\x05proxy\x18\x05 \x01(\tJ\x04\x08\x04\x10\x05J\x04\x08\x06\x10\x07"y\n\x10\x45victReplRequest\x12:\n\x0f\x63lusterMetadata\x18\x01 \x01(\x0b\x32!.replit.goval.api.ClusterMetadata\x12\r\n\x05token\x18\x02 \x01(\t\x12\x0c\n\x04user\x18\x03 \x01(\t\x12\x0c\n\x04slug\x18\x04 \x01(\t"I\n\x11\x45victReplResponse\x12\x34\n\x0creplTransfer\x18\x01 \x01(\x0b\x32\x1e.replit.goval.api.ReplTransferB\x1dZ\x1bgithub.com/replit/goval/apib\x06proto3' -) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1dreplit/goval/api/client.proto\x12\x10replit.goval.api\x1a\x1fgoogle/protobuf/timestamp.proto\x1a replit/goval/api/repl/repl.proto\x1a(replit/goval/api/features/features.proto\"\xdb\x07\n\tReplToken\x12\'\n\x03iat\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65xp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04salt\x18\x03 \x01(\t\x12\x0f\n\x07\x63luster\x18\x04 \x01(\t\x12\x37\n\x0bpersistence\x18\x06 \x01(\x0e\x32\".replit.goval.api.repl.Persistence\x12+\n\x04repl\x18\x07 \x01(\x0b\x32\x1b.replit.goval.api.repl.ReplH\x00\x12\x30\n\x02id\x18\x08 \x01(\x0b\x32\".replit.goval.api.ReplToken.ReplIDH\x00\x12\x46\n\tclassroom\x18\t \x01(\x0b\x32-.replit.goval.api.ReplToken.ClassroomMetadataB\x02\x18\x01H\x00\x12=\n\x0eresourceLimits\x18\n \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12H\n\x19interactiveResourceLimits\x18\x11 \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x36\n\x06\x66ormat\x18\x0c \x01(\x0e\x32&.replit.goval.api.ReplToken.WireFormat\x12\x38\n\tpresenced\x18\r \x01(\x0b\x32%.replit.goval.api.ReplToken.Presenced\x12\r\n\x05\x66lags\x18\x0e \x03(\t\x12\x37\n\x0bpermissions\x18\x0f \x01(\x0b\x32\".replit.goval.api.repl.Permissions\x12\x34\n\x08\x66\x65\x61tures\x18\x10 \x03(\x0b\x32\".replit.goval.api.features.Feature\x12\x34\n\nbuild_info\x18\x12 \x01(\x0b\x32 .replit.goval.api.repl.BuildInfo\x1a\x31\n\x11\x43lassroomMetadata\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08language\x18\x02 \x01(\t\x1a(\n\x06ReplID\x12\n\n\x02id\x18\x01 \x01(\t\x12\x12\n\nsourceRepl\x18\x02 \x01(\t\x1a\x31\n\tPresenced\x12\x10\n\x08\x62\x65\x61rerID\x18\x01 \x01(\r\x12\x12\n\nbearerName\x18\x02 \x01(\t\"2\n\nWireFormat\x12\x0c\n\x08PROTOBUF\x10\x00\x12\x0c\n\x04JSON\x10\x01\x1a\x02\x08\x01\x12\x08\n\x04PID2\x10\x02\x42\n\n\x08metadata\".\n\x0eTLSCertificate\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0c\n\x04\x63\x65rt\x18\x02 \x01(\x0c\"\xc0\x02\n\x0cReplTransfer\x12)\n\x04repl\x18\x01 \x01(\x0b\x32\x1b.replit.goval.api.repl.Repl\x12\x39\n\nreplLimits\x18\x02 \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x39\n\nuserLimits\x18\x03 \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x15\n\rcustomDomains\x18\x04 \x03(\t\x12\x36\n\x0c\x63\x65rtificates\x18\x05 \x03(\x0b\x32 .replit.goval.api.TLSCertificate\x12\r\n\x05\x66lags\x18\x06 \x03(\t\x12\x31\n\x08metadata\x18\x07 \x01(\x0b\x32\x1f.replit.goval.api.repl.Metadata\"H\n\x10\x41llowReplRequest\x12\x34\n\x0creplTransfer\x18\x01 \x01(\x0b\x32\x1e.replit.goval.api.ReplTransfer\"l\n\x0f\x43lusterMetadata\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tconmanURL\x18\x02 \x01(\t\x12\x0c\n\x04gurl\x18\x03 \x01(\t\x12\r\n\x05proxy\x18\x05 \x01(\t\x12\x11\n\tcontinent\x18\x07 \x01(\tJ\x04\x08\x04\x10\x05J\x04\x08\x06\x10\x07\"y\n\x10\x45victReplRequest\x12:\n\x0f\x63lusterMetadata\x18\x01 \x01(\x0b\x32!.replit.goval.api.ClusterMetadata\x12\r\n\x05token\x18\x02 \x01(\t\x12\x0c\n\x04user\x18\x03 \x01(\t\x12\x0c\n\x04slug\x18\x04 \x01(\t\"I\n\x11\x45victReplResponse\x12\x34\n\x0creplTransfer\x18\x01 \x01(\x0b\x32\x1e.replit.goval.api.ReplTransferB\x1dZ\x1bgithub.com/replit/goval/apib\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages( - DESCRIPTOR, "replit.goval.api.client_pb2", globals() -) +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'replit.goval.api.client_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b"Z\033github.com/replit/goval/api" - _REPLTOKEN_WIREFORMAT.values_by_name["JSON"]._options = None - _REPLTOKEN_WIREFORMAT.values_by_name["JSON"]._serialized_options = b"\010\001" - _REPLTOKEN.fields_by_name["classroom"]._options = None - _REPLTOKEN.fields_by_name["classroom"]._serialized_options = b"\030\001" - _REPLTOKEN._serialized_start = 161 - _REPLTOKEN._serialized_end = 1010 - _REPLTOKEN_CLASSROOMMETADATA._serialized_start = 814 - _REPLTOKEN_CLASSROOMMETADATA._serialized_end = 863 - _REPLTOKEN_REPLID._serialized_start = 865 - _REPLTOKEN_REPLID._serialized_end = 905 - _REPLTOKEN_PRESENCED._serialized_start = 907 - _REPLTOKEN_PRESENCED._serialized_end = 956 - _REPLTOKEN_WIREFORMAT._serialized_start = 958 - _REPLTOKEN_WIREFORMAT._serialized_end = 998 - _TLSCERTIFICATE._serialized_start = 1012 - _TLSCERTIFICATE._serialized_end = 1058 - _REPLTRANSFER._serialized_start = 1061 - _REPLTRANSFER._serialized_end = 1330 - _ALLOWREPLREQUEST._serialized_start = 1332 - _ALLOWREPLREQUEST._serialized_end = 1404 - _CLUSTERMETADATA._serialized_start = 1406 - _CLUSTERMETADATA._serialized_end = 1495 - _EVICTREPLREQUEST._serialized_start = 1497 - _EVICTREPLREQUEST._serialized_end = 1618 - _EVICTREPLRESPONSE._serialized_start = 1620 - _EVICTREPLRESPONSE._serialized_end = 1693 + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z\033github.com/replit/goval/api' + _REPLTOKEN_WIREFORMAT.values_by_name["JSON"]._options = None + _REPLTOKEN_WIREFORMAT.values_by_name["JSON"]._serialized_options = b'\010\001' + _REPLTOKEN.fields_by_name['classroom']._options = None + _REPLTOKEN.fields_by_name['classroom']._serialized_options = b'\030\001' + _globals['_REPLTOKEN']._serialized_start=161 + _globals['_REPLTOKEN']._serialized_end=1148 + _globals['_REPLTOKEN_CLASSROOMMETADATA']._serialized_start=942 + _globals['_REPLTOKEN_CLASSROOMMETADATA']._serialized_end=991 + _globals['_REPLTOKEN_REPLID']._serialized_start=993 + _globals['_REPLTOKEN_REPLID']._serialized_end=1033 + _globals['_REPLTOKEN_PRESENCED']._serialized_start=1035 + _globals['_REPLTOKEN_PRESENCED']._serialized_end=1084 + _globals['_REPLTOKEN_WIREFORMAT']._serialized_start=1086 + _globals['_REPLTOKEN_WIREFORMAT']._serialized_end=1136 + _globals['_TLSCERTIFICATE']._serialized_start=1150 + _globals['_TLSCERTIFICATE']._serialized_end=1196 + _globals['_REPLTRANSFER']._serialized_start=1199 + _globals['_REPLTRANSFER']._serialized_end=1519 + _globals['_ALLOWREPLREQUEST']._serialized_start=1521 + _globals['_ALLOWREPLREQUEST']._serialized_end=1593 + _globals['_CLUSTERMETADATA']._serialized_start=1595 + _globals['_CLUSTERMETADATA']._serialized_end=1703 + _globals['_EVICTREPLREQUEST']._serialized_start=1705 + _globals['_EVICTREPLREQUEST']._serialized_end=1826 + _globals['_EVICTREPLRESPONSE']._serialized_start=1828 + _globals['_EVICTREPLRESPONSE']._serialized_end=1901 # @@protoc_insertion_point(module_scope) diff --git a/src/replit/goval/api/features/features_pb2.py b/src/replit/goval/api/features/features_pb2.py index 45ab297f..e85edcf5 100644 --- a/src/replit/goval/api/features/features_pb2.py +++ b/src/replit/goval/api/features/features_pb2.py @@ -2,31 +2,29 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: replit/goval/api/features/features.proto """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database - +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n(replit/goval/api/features/features.proto\x12\x19replit.goval.api.features"\x05\n\x03Gpu"\t\n\x07\x42oosted"\x8c\x01\n\x07\x46\x65\x61ture\x12-\n\x03gpu\x18\x01 \x01(\x0b\x32\x1e.replit.goval.api.features.GpuH\x00\x12\x35\n\x07\x62oosted\x18\x02 \x01(\x0b\x32".replit.goval.api.features.BoostedH\x00\x12\x10\n\x08required\x18\x03 \x01(\x08\x42\t\n\x07\x66\x65\x61tureB&Z$github.com/replit/goval/api/featuresb\x06proto3' -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages( - DESCRIPTOR, "replit.goval.api.features.features_pb2", globals() -) + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(replit/goval/api/features/features.proto\x12\x19replit.goval.api.features\"\x05\n\x03Gpu\"\t\n\x07\x42oosted\"\x8c\x01\n\x07\x46\x65\x61ture\x12-\n\x03gpu\x18\x01 \x01(\x0b\x32\x1e.replit.goval.api.features.GpuH\x00\x12\x35\n\x07\x62oosted\x18\x02 \x01(\x0b\x32\".replit.goval.api.features.BoostedH\x00\x12\x10\n\x08required\x18\x03 \x01(\x08\x42\t\n\x07\x66\x65\x61tureB&Z$github.com/replit/goval/api/featuresb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'replit.goval.api.features.features_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b"Z$github.com/replit/goval/api/features" - _GPU._serialized_start = 71 - _GPU._serialized_end = 76 - _BOOSTED._serialized_start = 78 - _BOOSTED._serialized_end = 87 - _FEATURE._serialized_start = 90 - _FEATURE._serialized_end = 230 + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z$github.com/replit/goval/api/features' + _globals['_GPU']._serialized_start=71 + _globals['_GPU']._serialized_end=76 + _globals['_BOOSTED']._serialized_start=78 + _globals['_BOOSTED']._serialized_end=87 + _globals['_FEATURE']._serialized_start=90 + _globals['_FEATURE']._serialized_end=230 # @@protoc_insertion_point(module_scope) diff --git a/src/replit/goval/api/repl/repl_pb2.py b/src/replit/goval/api/repl/repl_pb2.py index 0ac6ae6a..605fce8f 100644 --- a/src/replit/goval/api/repl/repl_pb2.py +++ b/src/replit/goval/api/repl/repl_pb2.py @@ -2,42 +2,44 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: replit/goval/api/repl/repl.proto """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database - +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -from replit.goval.api.features import ( - features_pb2 as replit_dot_goval_dot_api_dot_features_dot_features__pb2, -) +from replit.goval.api.features import features_pb2 as replit_dot_goval_dot_api_dot_features_dot_features__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n replit/goval/api/repl/repl.proto\x12\x15replit.goval.api.repl\x1a(replit/goval/api/features/features.proto"v\n\x04Repl\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08language\x18\x02 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x03 \x01(\t\x12\x0c\n\x04slug\x18\x04 \x01(\t\x12\x0c\n\x04user\x18\x05 \x01(\t\x12\x12\n\nsourceRepl\x18\x06 \x01(\t\x12\x10\n\x08\x64\x61tabase\x18\x07 \x01(\t"\xfb\x01\n\x0eResourceLimits\x12\x0b\n\x03net\x18\x01 \x01(\x08\x12\x0e\n\x06memory\x18\x02 \x01(\x03\x12\x0f\n\x07threads\x18\x03 \x01(\x01\x12\x0e\n\x06shares\x18\x04 \x01(\x01\x12\x0c\n\x04\x64isk\x18\x05 \x01(\x03\x12@\n\x05\x63\x61\x63he\x18\x06 \x01(\x0e\x32\x31.replit.goval.api.repl.ResourceLimits.Cachability\x12\x17\n\x0frestrictNetwork\x18\x07 \x01(\x08\x12\x15\n\rpreventWakeup\x18\x08 \x01(\x08"+\n\x0b\x43\x61\x63hability\x12\x08\n\x04NONE\x10\x00\x12\x08\n\x04USER\x10\x01\x12\x08\n\x04REPL\x10\x02"%\n\x0bPermissions\x12\x16\n\x0etoggleAlwaysOn\x18\x01 \x01(\x08"\xab\x02\n\x08Metadata\x12)\n\x04repl\x18\x07 \x01(\x0b\x32\x1b.replit.goval.api.repl.Repl\x12=\n\x0eresourceLimits\x18\n \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x37\n\x0bpersistence\x18\x06 \x01(\x0e\x32".replit.goval.api.repl.Persistence\x12\r\n\x05\x66lags\x18\x0e \x03(\t\x12\x37\n\x0bpermissions\x18\x0f \x01(\x0b\x32".replit.goval.api.repl.Permissions\x12\x34\n\x08\x66\x65\x61tures\x18\x10 \x03(\x0b\x32".replit.goval.api.features.Feature*6\n\x0bPersistence\x12\x0e\n\nPERSISTENT\x10\x00\x12\r\n\tEPHEMERAL\x10\x01\x12\x08\n\x04NONE\x10\x02\x42"Z github.com/replit/goval/api/replb\x06proto3' -) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n replit/goval/api/repl/repl.proto\x12\x15replit.goval.api.repl\x1a(replit/goval/api/features/features.proto\"C\n\x07\x42uckets\x12\x11\n\tsnapshots\x18\x01 \x01(\t\x12\x10\n\x08metadata\x18\x02 \x01(\t\x12\x13\n\x0b\x64isk_blocks\x18\x03 \x01(\t\"\x8b\x02\n\x04Repl\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08language\x18\x02 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x03 \x01(\t\x12\x0c\n\x04slug\x18\x04 \x01(\t\x12\x0c\n\x04user\x18\x05 \x01(\t\x12\x12\n\nsourceRepl\x18\x06 \x01(\t\x12\x10\n\x08\x64\x61tabase\x18\x07 \x01(\t\x12/\n\x07\x62uckets\x18\x08 \x01(\x0b\x32\x1e.replit.goval.api.repl.Buckets\x12.\n\x07user_id\x18\t \x01(\x0b\x32\x1d.replit.goval.api.repl.UserId\x12\x0f\n\x07is_team\x18\n \x01(\x08\x12\r\n\x05roles\x18\x0b \x03(\t\x12\x12\n\nlog_fields\x18\r \x01(\t\"M\n\x06UserId\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x37\n\x0b\x65nvironment\x18\x02 \x01(\x0e\x32\".replit.goval.api.repl.Environment\"\xa7\x02\n\x0eResourceLimits\x12\x0b\n\x03net\x18\x01 \x01(\x08\x12\x0e\n\x06memory\x18\x02 \x01(\x03\x12\x0f\n\x07threads\x18\x03 \x01(\x01\x12\x0e\n\x06shares\x18\x04 \x01(\x01\x12\x0c\n\x04\x64isk\x18\x05 \x01(\x03\x12\x14\n\x0cminimum_disk\x18\n \x01(\x03\x12\x14\n\x0cscratch_disk\x18\t \x01(\x03\x12@\n\x05\x63\x61\x63he\x18\x06 \x01(\x0e\x32\x31.replit.goval.api.repl.ResourceLimits.Cachability\x12\x17\n\x0frestrictNetwork\x18\x07 \x01(\x08\x12\x15\n\rpreventWakeup\x18\x08 \x01(\x08\"+\n\x0b\x43\x61\x63hability\x12\x08\n\x04NONE\x10\x00\x12\x08\n\x04USER\x10\x01\x12\x08\n\x04REPL\x10\x02\"%\n\x0bPermissions\x12\x16\n\x0etoggleAlwaysOn\x18\x01 \x01(\x08\"\xab\x03\n\x08Metadata\x12)\n\x04repl\x18\x07 \x01(\x0b\x32\x1b.replit.goval.api.repl.Repl\x12=\n\x0eresourceLimits\x18\n \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12H\n\x19interactiveResourceLimits\x18\x11 \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x37\n\x0bpersistence\x18\x06 \x01(\x0e\x32\".replit.goval.api.repl.Persistence\x12\r\n\x05\x66lags\x18\x0e \x03(\t\x12\x37\n\x0bpermissions\x18\x0f \x01(\x0b\x32\".replit.goval.api.repl.Permissions\x12\x34\n\x08\x66\x65\x61tures\x18\x10 \x03(\x0b\x32\".replit.goval.api.features.Feature\x12\x34\n\nbuild_info\x18\x12 \x01(\x0b\x32 .replit.goval.api.repl.BuildInfo\"W\n\tBuildInfo\x12\x15\n\rdeployment_id\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x03 \x01(\t\x12\x14\n\x0cmachine_tier\x18\x04 \x01(\t*.\n\x0b\x45nvironment\x12\x0f\n\x0b\x44\x45VELOPMENT\x10\x00\x12\x0e\n\nPRODUCTION\x10\x01*E\n\x0bPersistence\x12\x0e\n\nPERSISTENT\x10\x00\x12\r\n\tEPHEMERAL\x10\x01\x12\x08\n\x04NONE\x10\x02\x12\r\n\tREAD_ONLY\x10\x03\x42\"Z github.com/replit/goval/api/replb\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages( - DESCRIPTOR, "replit.goval.api.repl.repl_pb2", globals() -) +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'replit.goval.api.repl.repl_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b"Z github.com/replit/goval/api/repl" - _PERSISTENCE._serialized_start = 816 - _PERSISTENCE._serialized_end = 870 - _REPL._serialized_start = 101 - _REPL._serialized_end = 219 - _RESOURCELIMITS._serialized_start = 222 - _RESOURCELIMITS._serialized_end = 473 - _RESOURCELIMITS_CACHABILITY._serialized_start = 430 - _RESOURCELIMITS_CACHABILITY._serialized_end = 473 - _PERMISSIONS._serialized_start = 475 - _PERMISSIONS._serialized_end = 512 - _METADATA._serialized_start = 515 - _METADATA._serialized_end = 814 + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z github.com/replit/goval/api/repl' + _globals['_ENVIRONMENT']._serialized_start=1375 + _globals['_ENVIRONMENT']._serialized_end=1421 + _globals['_PERSISTENCE']._serialized_start=1423 + _globals['_PERSISTENCE']._serialized_end=1492 + _globals['_BUCKETS']._serialized_start=101 + _globals['_BUCKETS']._serialized_end=168 + _globals['_REPL']._serialized_start=171 + _globals['_REPL']._serialized_end=438 + _globals['_USERID']._serialized_start=440 + _globals['_USERID']._serialized_end=517 + _globals['_RESOURCELIMITS']._serialized_start=520 + _globals['_RESOURCELIMITS']._serialized_end=815 + _globals['_RESOURCELIMITS_CACHABILITY']._serialized_start=772 + _globals['_RESOURCELIMITS_CACHABILITY']._serialized_end=815 + _globals['_PERMISSIONS']._serialized_start=817 + _globals['_PERMISSIONS']._serialized_end=854 + _globals['_METADATA']._serialized_start=857 + _globals['_METADATA']._serialized_end=1284 + _globals['_BUILDINFO']._serialized_start=1286 + _globals['_BUILDINFO']._serialized_end=1373 # @@protoc_insertion_point(module_scope) diff --git a/src/replit/goval/api/signing_pb2.py b/src/replit/goval/api/signing_pb2.py index 519bc072..bf624b4a 100644 --- a/src/replit/goval/api/signing_pb2.py +++ b/src/replit/goval/api/signing_pb2.py @@ -2,11 +2,10 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: replit/goval/api/signing.proto """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database - +from google.protobuf.internal import builder as _builder # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,31 +13,33 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 from replit.goval.api import client_pb2 as replit_dot_goval_dot_api_dot_client__pb2 +from replit.goval.api.repl import repl_pb2 as replit_dot_goval_dot_api_dot_repl_dot_repl__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x1ereplit/goval/api/signing.proto\x12\x10replit.goval.api\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1dreplit/goval/api/client.proto"\x89\x01\n\x15GovalSigningAuthority\x12\x10\n\x06key_id\x18\x01 \x01(\tH\x00\x12\x15\n\x0bsigned_cert\x18\x02 \x01(\tH\x00\x12/\n\x07version\x18\x03 \x01(\x0e\x32\x1e.replit.goval.api.TokenVersion\x12\x0e\n\x06issuer\x18\x04 \x01(\tB\x06\n\x04\x63\x65rt"}\n\x10\x43\x65rtificateClaim\x12\x10\n\x06replid\x18\x01 \x01(\tH\x00\x12\x0e\n\x04user\x18\x02 \x01(\tH\x00\x12\x11\n\x07\x63luster\x18\x04 \x01(\tH\x00\x12+\n\x04\x66lag\x18\x03 \x01(\x0e\x32\x1b.replit.goval.api.FlagClaimH\x00\x42\x07\n\x05\x63laim"\xa4\x01\n\tGovalCert\x12\'\n\x03iat\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65xp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x32\n\x06\x63laims\x18\x03 \x03(\x0b\x32".replit.goval.api.CertificateClaim\x12\x11\n\tpublicKey\x18\x04 \x01(\t"\xe8\x01\n\nGovalToken\x12\'\n\x03iat\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65xp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06replid\x18\x03 \x01(\t\x12\x31\n\nrepl_token\x18\x04 \x01(\x0b\x32\x1b.replit.goval.api.ReplTokenH\x00\x12<\n\rrepl_identity\x18\x05 \x01(\x0b\x32#.replit.goval.api.GovalReplIdentityH\x00\x42\x07\n\x05Token"u\n\x11GovalReplIdentity\x12\x0e\n\x06replid\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\x12\x0c\n\x04slug\x18\x03 \x01(\t\x12\x0b\n\x03\x61ud\x18\x04 \x01(\t\x12\x11\n\tephemeral\x18\x05 \x01(\x08\x12\x14\n\x0coriginReplid\x18\x06 \x01(\t*9\n\x0cTokenVersion\x12\x13\n\x0f\x42\x41RE_REPL_TOKEN\x10\x00\x12\x14\n\x10TYPE_AWARE_TOKEN\x10\x01*\x8b\x01\n\tFlagClaim\x12\x14\n\x10MINT_GOVAL_TOKEN\x10\x00\x12\x1a\n\x16SIGN_INTERMEDIATE_CERT\x10\x01\x12\x0c\n\x08IDENTITY\x10\x05\x12\x0f\n\x0bGHOSTWRITER\x10\x06\x12\x0e\n\nANY_REPLID\x10\x02\x12\x0c\n\x08\x41NY_USER\x10\x03\x12\x0f\n\x0b\x41NY_CLUSTER\x10\x04\x42\x1dZ\x1bgithub.com/replit/goval/apib\x06proto3' -) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ereplit/goval/api/signing.proto\x12\x10replit.goval.api\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1dreplit/goval/api/client.proto\x1a replit/goval/api/repl/repl.proto\"\x89\x01\n\x15GovalSigningAuthority\x12\x10\n\x06key_id\x18\x01 \x01(\tH\x00\x12\x15\n\x0bsigned_cert\x18\x02 \x01(\tH\x00\x12/\n\x07version\x18\x03 \x01(\x0e\x32\x1e.replit.goval.api.TokenVersion\x12\x0e\n\x06issuer\x18\x04 \x01(\tB\x06\n\x04\x63\x65rt\"\xbc\x01\n\x10\x43\x65rtificateClaim\x12\x10\n\x06replid\x18\x01 \x01(\tH\x00\x12\x0e\n\x04user\x18\x02 \x01(\tH\x00\x12\x11\n\x07user_id\x18\x07 \x01(\x03H\x00\x12\x11\n\x07\x63luster\x18\x04 \x01(\tH\x00\x12\x14\n\nsubcluster\x18\x05 \x01(\tH\x00\x12\x14\n\ndeployment\x18\x06 \x01(\x08H\x00\x12+\n\x04\x66lag\x18\x03 \x01(\x0e\x32\x1b.replit.goval.api.FlagClaimH\x00\x42\x07\n\x05\x63laim\"\xa4\x01\n\tGovalCert\x12\'\n\x03iat\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65xp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x32\n\x06\x63laims\x18\x03 \x03(\x0b\x32\".replit.goval.api.CertificateClaim\x12\x11\n\tpublicKey\x18\x04 \x01(\t\"\xe8\x01\n\nGovalToken\x12\'\n\x03iat\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65xp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06replid\x18\x03 \x01(\t\x12\x31\n\nrepl_token\x18\x04 \x01(\x0b\x32\x1b.replit.goval.api.ReplTokenH\x00\x12<\n\rrepl_identity\x18\x05 \x01(\x0b\x32#.replit.goval.api.GovalReplIdentityH\x00\x42\x07\n\x05Token\"\xe7\x02\n\x11GovalReplIdentity\x12\x0e\n\x06replid\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\x12\x0c\n\x04slug\x18\x03 \x01(\t\x12\x0b\n\x03\x61ud\x18\x04 \x01(\t\x12\x11\n\tephemeral\x18\x05 \x01(\x08\x12\x14\n\x0coriginReplid\x18\x06 \x01(\t\x12\x0f\n\x07user_id\x18\x07 \x01(\x03\x12\x34\n\nbuild_info\x18\x08 \x01(\x0b\x32 .replit.goval.api.repl.BuildInfo\x12\x0f\n\x07is_team\x18\t \x01(\x08\x12\r\n\x05roles\x18\n \x03(\t\x12?\n\x0binteractive\x18\x0b \x01(\x0b\x32(.replit.goval.api.ReplRuntimeInteractiveH\x00\x12=\n\ndeployment\x18\x0c \x01(\x0b\x32\'.replit.goval.api.ReplRuntimeDeploymentH\x00\x42\t\n\x07runtime\"=\n\x16ReplRuntimeInteractive\x12\x0f\n\x07\x63luster\x18\x01 \x01(\t\x12\x12\n\nsubcluster\x18\x02 \x01(\t\"\x17\n\x15ReplRuntimeDeployment*9\n\x0cTokenVersion\x12\x13\n\x0f\x42\x41RE_REPL_TOKEN\x10\x00\x12\x14\n\x10TYPE_AWARE_TOKEN\x10\x01*\xe3\x01\n\tFlagClaim\x12\x14\n\x10MINT_GOVAL_TOKEN\x10\x00\x12\x1a\n\x16SIGN_INTERMEDIATE_CERT\x10\x01\x12\x0c\n\x08IDENTITY\x10\x05\x12\x0f\n\x0bGHOSTWRITER\x10\x06\x12\x12\n\x0eRENEW_IDENTITY\x10\x07\x12\x0c\n\x08RENEW_KV\x10\x08\x12\x0f\n\x0b\x44\x45PLOYMENTS\x10\n\x12\x0e\n\nANY_REPLID\x10\x02\x12\x0c\n\x08\x41NY_USER\x10\x03\x12\x0f\n\x0b\x41NY_USER_ID\x10\x0b\x12\x0f\n\x0b\x41NY_CLUSTER\x10\x04\x12\x12\n\x0e\x41NY_SUBCLUSTER\x10\tB\x1dZ\x1bgithub.com/replit/goval/apib\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages( - DESCRIPTOR, "replit.goval.api.signing_pb2", globals() -) +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'replit.goval.api.signing_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b"Z\033github.com/replit/goval/api" - _TOKENVERSION._serialized_start = 904 - _TOKENVERSION._serialized_end = 961 - _FLAGCLAIM._serialized_start = 964 - _FLAGCLAIM._serialized_end = 1103 - _GOVALSIGNINGAUTHORITY._serialized_start = 117 - _GOVALSIGNINGAUTHORITY._serialized_end = 254 - _CERTIFICATECLAIM._serialized_start = 256 - _CERTIFICATECLAIM._serialized_end = 381 - _GOVALCERT._serialized_start = 384 - _GOVALCERT._serialized_end = 548 - _GOVALTOKEN._serialized_start = 551 - _GOVALTOKEN._serialized_end = 783 - _GOVALREPLIDENTITY._serialized_start = 785 - _GOVALREPLIDENTITY._serialized_end = 902 + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z\033github.com/replit/goval/api' + _globals['_TOKENVERSION']._serialized_start=1333 + _globals['_TOKENVERSION']._serialized_end=1390 + _globals['_FLAGCLAIM']._serialized_start=1393 + _globals['_FLAGCLAIM']._serialized_end=1620 + _globals['_GOVALSIGNINGAUTHORITY']._serialized_start=151 + _globals['_GOVALSIGNINGAUTHORITY']._serialized_end=288 + _globals['_CERTIFICATECLAIM']._serialized_start=291 + _globals['_CERTIFICATECLAIM']._serialized_end=479 + _globals['_GOVALCERT']._serialized_start=482 + _globals['_GOVALCERT']._serialized_end=646 + _globals['_GOVALTOKEN']._serialized_start=649 + _globals['_GOVALTOKEN']._serialized_end=881 + _globals['_GOVALREPLIDENTITY']._serialized_start=884 + _globals['_GOVALREPLIDENTITY']._serialized_end=1243 + _globals['_REPLRUNTIMEINTERACTIVE']._serialized_start=1245 + _globals['_REPLRUNTIMEINTERACTIVE']._serialized_end=1306 + _globals['_REPLRUNTIMEDEPLOYMENT']._serialized_start=1308 + _globals['_REPLRUNTIMEDEPLOYMENT']._serialized_end=1331 # @@protoc_insertion_point(module_scope) diff --git a/src/replit/identity/verify.py b/src/replit/identity/verify.py index 86462718..8635660e 100644 --- a/src/replit/identity/verify.py +++ b/src/replit/identity/verify.py @@ -21,7 +21,9 @@ class _MessageClaims: repls: Set[str] = dataclasses.field(default_factory=set) users: Set[str] = dataclasses.field(default_factory=set) + user_ids: Set[int] = dataclasses.field(default_factory=set) clusters: Set[str] = dataclasses.field(default_factory=set) + subclusters: Set[str] = dataclasses.field(default_factory=set) flags: Set[int] = dataclasses.field(default_factory=set) @@ -38,13 +40,17 @@ def _parse_claims(cert: signing_pb2.GovalCert) -> _MessageClaims: for claim in cert.claims: if claim.WhichOneof("claim") == "replid": - claims.repls.insert(claim.replid) + claims.repls.add(claim.replid) elif claim.WhichOneof("claim") == "user": - claims.users.insert(claim.user) + claims.users.add(claim.user) + elif claim.WhichOneof("claim") == "user_id": + claims.user_ids.add(claim.user_id) elif claim.WhichOneof("claim") == "cluster": - claims.clusters.insert(claim.cluster) + claims.clusters.add(claim.cluster) + elif claim.WhichOneof("claim") == "subcluster": + claims.subclusters.add(claim.subcluster) elif claim.WhichOneof("claim") == "flag": - claims.flags.insert(claim.flag) + claims.flags.add(claim.flag) return claims @@ -69,21 +75,47 @@ def _get_signing_authority(token: str) -> signing_pb2.GovalSigningAuthority: def _verify_raw_claims( replid: Optional[str] = None, user: Optional[str] = None, + user_id: Optional[int] = None, cluster: Optional[str] = None, + subcluster: Optional[str] = None, claims: Optional[_MessageClaims] = None, - any_replid: bool = False, - any_user: bool = False, - any_cluster: bool = False, + deployment: bool = False, ) -> None: if claims is None: return + any_replid = signing_pb2.FlagClaim.ANY_REPLID in claims.flags + any_user = signing_pb2.FlagClaim.ANY_USER in claims.flags + any_user_id = signing_pb2.FlagClaim.ANY_USER_ID in claims.flags + any_cluster = signing_pb2.FlagClaim.ANY_CLUSTER in claims.flags + any_subcluster = signing_pb2.FlagClaim.ANY_SUBCLUSTER in claims.flags + deployments = signing_pb2.FlagClaim.DEPLOYMENTS in claims.flags + if not any_replid and replid is not None and replid not in claims.repls: - raise VerifyError("not authorized (replid)") + raise VerifyError( + f"not authorized (replid), got {replid!r}, want {claims.repls!r}" + ) if not any_user and user is not None and user not in claims.users: - raise VerifyError("not authorized (user)") + raise VerifyError(f"not authorized (user), got {user!r}, want {claims.users!r}") + if not any_user_id and user_id is not None and user_id not in claims.user_ids: + raise VerifyError( + f"not authorized (user_id), got {user_id!r}, want {claims.user_ids!r}" + ) if not any_cluster and cluster is not None and cluster not in claims.clusters: - raise VerifyError("not authorized (cluster)") + raise VerifyError( + f"not authorized (cluster), got {cluster!r}, want {claims.clusters!r}" + ) + if ( + not any_subcluster + and subcluster is not None + and subcluster not in claims.subclusters + ): + raise VerifyError( + f"not authorized (subcluster), " + f"got {subcluster!r}, want {claims.subclusters!r}" + ) + if not deployments and deployment: + raise VerifyError("not authorized (deployment)") def _verify_claims( @@ -91,7 +123,10 @@ def _verify_claims( exp: datetime.datetime, replid: Optional[str] = None, user: Optional[str] = None, + user_id: Optional[int] = None, cluster: Optional[str] = None, + subcluster: Optional[str] = None, + deployment: bool = False, claims: Optional[_MessageClaims] = None, ) -> None: now = datetime.datetime.utcnow() @@ -100,7 +135,14 @@ def _verify_claims( if exp < now: raise VerifyError(f"expired {now - exp} ago") - _verify_raw_claims(replid=replid, user=user, cluster=cluster, claims=claims) + _verify_raw_claims( + replid=replid, + user=user, + user_id=user_id, + cluster=cluster, + subcluster=subcluster, + claims=claims, + ) class Verifier: @@ -168,7 +210,11 @@ def verify_cert( cert = signing_pb2.GovalCert.FromString(encoded_cert) # Verify that the cert is valid. - _verify_claims(iat=cert.iat.ToDatetime(), exp=cert.exp.ToDatetime()) + _verify_claims( + iat=cert.iat.ToDatetime(), + exp=cert.exp.ToDatetime(), + claims=_parse_claims(cert), + ) # If the parent is the root cert, there's nothing else to verify. if signing_cert: @@ -182,24 +228,34 @@ def verify_cert( authorized_claims: Set[str] = set() any_replid = False any_user = False + any_user_id = False any_cluster = False + any_subcluster = False for claim in signing_cert.claims: - authorized_claims.insert(str(claim)) + authorized_claims.add(str(claim)) if claim.WhichOneof("claim") == "flag": if claim.flag == signing_pb2.FlagClaim.ANY_REPLID: any_replid = True elif claim.flag == signing_pb2.FlagClaim.ANY_USER: any_user = True + elif claim.flag == signing_pb2.FlagClaim.ANY_USER_ID: + any_user_id = True elif claim.flag == signing_pb2.FlagClaim.ANY_CLUSTER: any_cluster = True + elif claim.flag == signing_pb2.FlagClaim.ANY_SUBCLUSTER: + any_subcluster = True for claim in signing_cert.claims: if claim.WhichOneof("claim") == "replid" and any_replid: continue if claim.WhichOneof("claim") == "user" and any_user: continue + if claim.WhichOneof("claim") == "user_id" and any_user_id: + continue if claim.WhichOneof("claim") == "cluster" and any_cluster: continue + if claim.WhichOneof("claim") == "subcluster" and any_subcluster: + continue if str(claim) not in authorized_claims: raise VerifyError(f"signing cert does not authorize claim {claim}") @@ -230,6 +286,52 @@ def read_public_key_from_env(keyid: str, issuer: str) -> pyseto.KeyInterface: return pyseto.Key.from_asymmetric_key_params(version=2, x=key) +def verify_identity_token( + identity_token: str, + audience: str, + pubkey_source: PubKeySource = read_public_key_from_env, +) -> signing_pb2.GovalReplIdentity: + """Verifies a Repl Identity token. + + Args: + identity_token: The Identity token. + audience: The audience that the token was signed for. + pubkey_source: The PubKeySource to get the public key. + + Returns: + The parsed and verified signing_pb2.GovalReplIdentity. + + Raises: + VerifyError: If there's any problem verifying the token. + """ + v = Verifier() + raw_goval_token, goval_cert = v.verify_chain(identity_token, pubkey_source) + repl_identity = signing_pb2.GovalReplIdentity.FromString(raw_goval_token) + + # Verify that the cert is valid. + if repl_identity.aud != audience: + raise VerifyError( + f"not authorized (audience), got {repl_identity.aud!r}, want {audience!r}" + ) + deployment: bool = False + cluster: Optional[str] = None + subcluster: Optional[str] = None + if repl_identity.WhichOneof("runtime") == "deployment": + deployment = True + elif repl_identity.WhichOneof("runtime") == "interactive": + cluster = repl_identity.interactive.cluster + subcluster = repl_identity.interactive.subcluster + _verify_claims( + iat=goval_cert.iat.ToDatetime(), + exp=goval_cert.exp.ToDatetime(), + cluster=cluster, + subcluster=subcluster, + deployment=deployment, + claims=_parse_claims(goval_cert) if goval_cert else None, + ) + return repl_identity + + def verify_ghostwriter( ghostwriter_token: str, pubkey_source: PubKeySource = read_public_key_from_env, @@ -257,6 +359,7 @@ def verify_ghostwriter( iat=goval_cert.iat.ToDatetime(), exp=goval_cert.exp.ToDatetime(), replid=goval_token.replid or None, + claims=_parse_claims(goval_cert), ) # Ensure that the claims include ghostwriter. has_ghostwriter_claim = False diff --git a/tests/test_identity.py b/tests/test_identity.py index 929a1ce1..a37cef37 100644 --- a/tests/test_identity.py +++ b/tests/test_identity.py @@ -23,10 +23,31 @@ def test_read_public_key_from_env(self) -> None: pubkey = replit.identity.read_public_key_from_env("dev:1", "goval") self.assertIsInstance(pubkey, pyseto.versions.v2.V2Public) + def test_verify_identity_token(self) -> None: + """Test verify_identity_token.""" + # This token should be valid for 100y. + # Generated with: + # ``` + # go run ./cmd/goval_keypairgen/ -eternal -sample-token \ + # -identity -gen-prefix dev -gen-id identity -issuer conman \ + # -replid=test -shortlived=false + # ``` + # in goval. + replit.identity.verify_identity_token( + identity_token="v2.public.Q2dSMFpYTjBJZ1IwWlhOMFxmipFrFWTrOkBoOzmSd8l3hYl88GIxsQTq4oueW4d8Lq7mhxYhl3RrZ6Tty24kpkOuIf0b5h582qp98L9iJwI.R0FFaUJtTnZibTFoYmhLbUFuWXlMbkIxWW14cFl5NVJNbVI2VTFkNGJXVnRWbmRrTVd4U1QxaFJNMkp0U2tOVFZYaEVVekZOTUdScVVtcFZNRlpPWVcwd01VMXVaR2hSVjJodVVtdGtibGRWZEVOVFJrcHpXWHBPVW1GVk5WaGpNMnhOWW10SmVGZFhNVFJqUm13MVRsWk9hMDFyV1hoVk1qRnpZVlp3U0dJemFHaGhlbFY1VjFaa2IxVnNaRmxpTTJSaFpWUkZlVlJYY0d0WGJFcDBWMjVLYUZaclZYcGFWbVJyWlVkU1JtUkVXbXRTYkZwV1ZGUktWazVLZVdKaGJsWmlOeTFRTUZsRWJIRnlibkZCV1RaSGMwRTFZbU5rUWtaUmVIRkJNMnRSWkd0NFozSXdYeTFrUVZSUGFtRk9PRGhFUkZVMldVZFJlazlwTkY5WVoyaGlTbWM1WVRodE1GcFlNRGhwTm14QldTNVNNRVpHWVZWS2RGUnVXbWxpVkVadldXMWtkbEpzY0VoV2FrcFFZV3RWT1E9PQ", # noqa: E501,B950,S106 # line too long + audience="test", + ) + def test_verify_ghostwriter(self) -> None: """Test verify_ghostwriter.""" # This token should be valid for 100y. - # Generated with `go run ./cmd/goval_keypairgen/ -eternal -sample-token -ghostwriter -gen-prefix dev -gen-id ghostwriter -issuer ghostwriter` in goval. + # Generated with: + # ``` + # go run ./cmd/goval_keypairgen/ -eternal -sample-token \ + # -ghostwriter -gen-prefix dev -gen-id ghostwriter \ + # -issuer ghostwriter -replid=test -shortlived=false + # ``` + # in goval. replit.identity.verify_ghostwriter( - "v2.public.Q2d3SXpwSEZwZ1lRdS9uS2hnSVNEQWlPeE1tbUJoQ1QrOHFHQWhvRWRHVnpkQT09Cupv8UwpdFsrAj8U_lAZXxYaBL3jL-tMHkhpveBEHqNpTGehlN-oWlEYcvyUwQq9JKxvNSblReAdElDxGXrkBQ.R0FFaUMyZG9iM04wZDNKcGRHVnlFcVlDZGpJdWNIVmliR2xqTGxFeVpETlRXRmt4VTBWYWQxb3hiRkprVkdoUVRqSm9ibE5XVGtWUlYyeFFaVVV4ZEdKVlNtOVNSM0ExV1c1V1NGRlhhSFpSTUdSQ1YxZHNUMVl6VGpWVVJ6VkRUVlpzZEdWSVFscGxWRlp6VmtaYWIxbFhWbGhXYlhSVlZteEtUMVJWVFhkTmJVWkhZMGhXYW1KVWJIWmFWVlY0VkVkR1JWVnVWbFppYlhONFdURm9iMVpzYjNsT1dFSmhZa1ZhYlZkSWNGTlViRloxVjIwMWNWZFFRVVUwZDNNdFltaGFaMGxPV2xGRlMwRjFOa3B0V1dVeVRYcHZabXBEYkZwaWJVWjRVbXBzZUhnMGVXMWlkazloTjJOM1ZuZzJZemR0YUhsaFpVaGtlamwyT0c5Rk1XRmxhekZsYlZVNU9XTlJUaTVTTUVaR1lWVk5lVnBIT1dsTk1EUjNXa1JPUzJOSFVraFdibXhFV2pGYWNsZHNhRnBPYXpGU1VGUXc" # noqa: E501,B950 # line too long + "v2.public.Q2d3SW12M2Vwd1lRNU9YUCtBRVNEQWlhdWIrSEVoQ042Yy80QVJvRWRHVnpkQT09DdURfbtuhiOQ-2cB_j1YE2pDKOnggxcFUEQsl9601dSTmqApx-6PuJiqGAHVEaMlDJa09zz04gBiYTqH75_SDg.R0FFaUMyZG9iM04wZDNKcGRHVnlFcllDZGpJdWNIVmliR2xqTGxFeVpETlRWMnQyVFRKV2QyUXhiRkpqUkZKWVMzbDBRbEpXVGtWUlYyeG9aRmRKY2xORlZtOVJNVkp4WTJwak1GRldTblpSTUdSQ1YxZEdRMW95T1VaYVJXUlhaVzFTUkZOVVJtaGxhMnd4V1RCb1YyRlhTa2hpUjNCTlltMVNXbFJyVWxkVFIxWkdWbTF3YUZZeWFGRlhiWE14V1ZaS1dGSnJUbWxTYkZWNFYyeGFkMVJXY0hOUldHUnJZWHByTVZkdGNGZFRNVzk0VVcxc1VGSnJXbFJVTUdoVFkwWnZkMUpVTVZoMFF6TktORTVIYW5NMlRubHlMVmhHVFd4blVHb3hUVEpsUzNKWE0zQnZjMmRTTlRGRVRXTk9keTFaVlZGdWRqbFRWbTFoYTNsMU9WbDZlRkEyU25SNk5GQndWMHBwUlMxRFdFZHpSMnN6WjNSSVNVa3VVakJHUm1GVlRYbGFSemxwVFRBMGQxcEVUa3RqUjFKSVZtNXNSRm94V25KWGJHaGFUbXN4VWxCVU1BPT0" # noqa: E501,B950,S106 # line too long )