turbo_broccoli.custom.secret
Serialize secrets
1"""Serialize secrets""" 2 3import json 4from typing import Any, NoReturn 5 6from nacl.secret import SecretBox 7 8from ..context import Context 9from ..exceptions import TypeNotSupported 10 11 12class Secret: 13 """ 14 A wrapper for a basic Python variable whose value is considered to be 15 secret. Similar API as [`pydantic`'s secret 16 types](https://pydantic-docs.helpmanual.io/usage/types/#secret-types) 17 """ 18 19 _value: Any 20 21 def __eq__(self, __o: object) -> bool: 22 return False 23 24 def __init__(self, value: Any) -> None: 25 self._value = value 26 27 def __ne__(self, __o: object) -> bool: 28 return False 29 30 def __repr__(self) -> str: 31 return "--REDACTED--" 32 33 def __str__(self) -> str: 34 return "--REDACTED--" 35 36 def get_secret_value(self) -> Any: 37 """Self-explanatory""" 38 return self._value 39 40 41class LockedSecret(Secret): 42 """ 43 Represented a secret that could not be decrypted because the shared key was 44 not provided. The `get_secret_value` method always raises a `RuntimeError`. 45 """ 46 47 def __init__(self) -> None: 48 super().__init__(None) 49 50 def get_secret_value(self) -> NoReturn: 51 raise RuntimeError("Cannot get the secret value of a locked secret") 52 53 54class SecretDict(Secret): 55 def __init__(self, value: dict) -> None: 56 super().__init__(value) 57 58 59class SecretFloat(Secret): 60 def __init__(self, value: float) -> None: 61 super().__init__(value) 62 63 64class SecretInt(Secret): 65 def __init__(self, value: int) -> None: 66 super().__init__(value) 67 68 69class SecretList(Secret): 70 def __init__(self, value: list) -> None: 71 super().__init__(value) 72 73 74class SecretStr(Secret): 75 def __init__(self, value: str) -> None: 76 super().__init__(value) 77 78 79def _from_json_v2(dct: dict, ctx: Context) -> Any: 80 if ctx.nacl_shared_key is None: 81 return LockedSecret() 82 box = SecretBox(ctx.nacl_shared_key) 83 return json.loads(box.decrypt(dct["data"]).decode("utf-8")) 84 85 86def from_json(dct: dict, ctx: Context) -> Any: 87 ctx.raise_if_nodecode("bytes") 88 decoders = { 89 # 1: _from_json_v1, # Use turbo_broccoli v3 90 2: _from_json_v2, 91 } 92 obj = decoders[dct["__version__"]](dct, ctx) 93 if isinstance(obj, LockedSecret): 94 return obj 95 types = { 96 dict: SecretDict, 97 float: SecretFloat, 98 int: SecretInt, 99 list: SecretList, 100 str: SecretStr, 101 } 102 return types[type(obj)](obj) 103 104 105def to_json(obj: Secret, ctx: Context) -> dict: 106 """ 107 Encrypts a JSON **string representation** of a secret document into a 108 new JSON document with the following structure: 109 110 ```py 111 { 112 "__type__": "secret", 113 "__version__": 2, 114 "data": <encrypted bytes>, 115 } 116 ``` 117 """ 118 if not isinstance(obj, Secret): 119 raise TypeNotSupported() 120 if ctx.nacl_shared_key is None: 121 raise RuntimeError( 122 "Attempting to serialize a secret type but no shared key is set. " 123 "Either set `nacl_shared_key` when constructing the encoding " 124 "torbo_broccoli.context.Context, or set the TB_SHARED_KEY " 125 "environment variable." 126 ) 127 box = SecretBox(ctx.nacl_shared_key) 128 return { 129 "__type__": "secret", 130 "__version__": 2, 131 "data": box.encrypt( 132 json.dumps(obj.get_secret_value()).encode("utf-8") 133 ), 134 }
13class Secret: 14 """ 15 A wrapper for a basic Python variable whose value is considered to be 16 secret. Similar API as [`pydantic`'s secret 17 types](https://pydantic-docs.helpmanual.io/usage/types/#secret-types) 18 """ 19 20 _value: Any 21 22 def __eq__(self, __o: object) -> bool: 23 return False 24 25 def __init__(self, value: Any) -> None: 26 self._value = value 27 28 def __ne__(self, __o: object) -> bool: 29 return False 30 31 def __repr__(self) -> str: 32 return "--REDACTED--" 33 34 def __str__(self) -> str: 35 return "--REDACTED--" 36 37 def get_secret_value(self) -> Any: 38 """Self-explanatory""" 39 return self._value
A wrapper for a basic Python variable whose value is considered to be
secret. Similar API as pydantic
's secret
types
42class LockedSecret(Secret): 43 """ 44 Represented a secret that could not be decrypted because the shared key was 45 not provided. The `get_secret_value` method always raises a `RuntimeError`. 46 """ 47 48 def __init__(self) -> None: 49 super().__init__(None) 50 51 def get_secret_value(self) -> NoReturn: 52 raise RuntimeError("Cannot get the secret value of a locked secret")
Represented a secret that could not be decrypted because the shared key was
not provided. The get_secret_value
method always raises a RuntimeError
.
A wrapper for a basic Python variable whose value is considered to be
secret. Similar API as pydantic
's secret
types
Inherited Members
60class SecretFloat(Secret): 61 def __init__(self, value: float) -> None: 62 super().__init__(value)
A wrapper for a basic Python variable whose value is considered to be
secret. Similar API as pydantic
's secret
types
Inherited Members
A wrapper for a basic Python variable whose value is considered to be
secret. Similar API as pydantic
's secret
types
Inherited Members
A wrapper for a basic Python variable whose value is considered to be
secret. Similar API as pydantic
's secret
types
Inherited Members
A wrapper for a basic Python variable whose value is considered to be
secret. Similar API as pydantic
's secret
types
Inherited Members
87def from_json(dct: dict, ctx: Context) -> Any: 88 ctx.raise_if_nodecode("bytes") 89 decoders = { 90 # 1: _from_json_v1, # Use turbo_broccoli v3 91 2: _from_json_v2, 92 } 93 obj = decoders[dct["__version__"]](dct, ctx) 94 if isinstance(obj, LockedSecret): 95 return obj 96 types = { 97 dict: SecretDict, 98 float: SecretFloat, 99 int: SecretInt, 100 list: SecretList, 101 str: SecretStr, 102 } 103 return types[type(obj)](obj)
106def to_json(obj: Secret, ctx: Context) -> dict: 107 """ 108 Encrypts a JSON **string representation** of a secret document into a 109 new JSON document with the following structure: 110 111 ```py 112 { 113 "__type__": "secret", 114 "__version__": 2, 115 "data": <encrypted bytes>, 116 } 117 ``` 118 """ 119 if not isinstance(obj, Secret): 120 raise TypeNotSupported() 121 if ctx.nacl_shared_key is None: 122 raise RuntimeError( 123 "Attempting to serialize a secret type but no shared key is set. " 124 "Either set `nacl_shared_key` when constructing the encoding " 125 "torbo_broccoli.context.Context, or set the TB_SHARED_KEY " 126 "environment variable." 127 ) 128 box = SecretBox(ctx.nacl_shared_key) 129 return { 130 "__type__": "secret", 131 "__version__": 2, 132 "data": box.encrypt( 133 json.dumps(obj.get_secret_value()).encode("utf-8") 134 ), 135 }
Encrypts a JSON string representation of a secret document into a new JSON document with the following structure:
{
"__type__": "secret",
"__version__": 2,
"data": <encrypted bytes>,
}