|
6 | 6 | import time |
7 | 7 | from datetime import datetime |
8 | 8 | from functools import cmp_to_key |
| 9 | +from typing import List |
| 10 | +from typing import Optional |
9 | 11 |
|
10 | 12 | import requests |
11 | 13 |
|
|
24 | 26 | from .jwk.jwk import dump_jwk |
25 | 27 | from .jwk.jwk import import_jwk |
26 | 28 | from .jwk.rsa import RSAKey |
27 | | -from .jwk.rsa import import_private_rsa_key_from_file |
28 | 29 | from .jwk.rsa import new_rsa_key |
29 | 30 | from .utils import as_unicode |
30 | 31 |
|
@@ -152,6 +153,26 @@ def ec_init(spec): |
152 | 153 | class KeyBundle: |
153 | 154 | """The Key Bundle""" |
154 | 155 |
|
| 156 | + params = { |
| 157 | + "cache_time": 0, |
| 158 | + "etag": "", |
| 159 | + "fileformat": "jwks", |
| 160 | + "httpc_params": {}, |
| 161 | + "ignore_errors_period": 0, |
| 162 | + "ignore_errors_until": None, |
| 163 | + "ignore_invalid_keys": True, |
| 164 | + "imp_jwks": None, |
| 165 | + "keytype": "RSA", |
| 166 | + "keyusage": None, |
| 167 | + "last_local": None, |
| 168 | + "last_remote": None, |
| 169 | + "last_updated": 0, |
| 170 | + "local": False, |
| 171 | + "remote": False, |
| 172 | + "source": None, |
| 173 | + "time_out": 0, |
| 174 | + } |
| 175 | + |
155 | 176 | def __init__( |
156 | 177 | self, |
157 | 178 | keys=None, |
@@ -189,22 +210,22 @@ def __init__( |
189 | 210 | """ |
190 | 211 |
|
191 | 212 | self._keys = [] |
192 | | - self.remote = False |
193 | | - self.local = False |
194 | 213 | self.cache_time = cache_time |
195 | | - self.ignore_errors_period = ignore_errors_period |
196 | | - self.ignore_errors_until = None # UNIX timestamp of last error |
197 | | - self.time_out = 0 |
198 | 214 | self.etag = "" |
199 | | - self.source = None |
200 | 215 | self.fileformat = fileformat.lower() |
| 216 | + self.ignore_errors_period = ignore_errors_period |
| 217 | + self.ignore_errors_until = None # UNIX timestamp of last error |
| 218 | + self.ignore_invalid_keys = ignore_invalid_keys |
| 219 | + self.imp_jwks = None |
201 | 220 | self.keytype = keytype |
202 | 221 | self.keyusage = keyusage |
203 | | - self.imp_jwks = None |
204 | | - self.last_updated = 0 |
205 | | - self.last_remote = None # HTTP Date of last remote update |
206 | 222 | self.last_local = None # UNIX timestamp of last local update |
207 | | - self.ignore_invalid_keys = ignore_invalid_keys |
| 223 | + self.last_remote = None # HTTP Date of last remote update |
| 224 | + self.last_updated = 0 |
| 225 | + self.local = False |
| 226 | + self.remote = False |
| 227 | + self.source = None |
| 228 | + self.time_out = 0 |
208 | 229 |
|
209 | 230 | if httpc: |
210 | 231 | self.httpc = httpc |
@@ -490,6 +511,7 @@ def update(self): |
490 | 511 |
|
491 | 512 | # reread everything |
492 | 513 | self._keys = [] |
| 514 | + updated = None |
493 | 515 |
|
494 | 516 | try: |
495 | 517 | if self.local: |
@@ -751,48 +773,68 @@ def difference(self, bundle): |
751 | 773 |
|
752 | 774 | return [k for k in self._keys if k not in bundle] |
753 | 775 |
|
754 | | - def dump(self): |
755 | | - _keys = [] |
756 | | - for _k in self._keys: |
757 | | - _ser = _k.to_dict() |
758 | | - if _k.inactive_since: |
759 | | - _ser["inactive_since"] = _k.inactive_since |
760 | | - _keys.append(_ser) |
761 | | - |
762 | | - res = { |
763 | | - "keys": _keys, |
764 | | - "fileformat": self.fileformat, |
765 | | - "last_updated": self.last_updated, |
766 | | - "last_remote": self.last_remote, |
767 | | - "last_local": self.last_local, |
768 | | - "httpc_params": self.httpc_params, |
769 | | - "remote": self.remote, |
770 | | - "local": self.local, |
771 | | - "imp_jwks": self.imp_jwks, |
772 | | - "time_out": self.time_out, |
773 | | - "cache_time": self.cache_time, |
774 | | - } |
| 776 | + def dump(self, exclude_attributes: Optional[List[str]] = None): |
| 777 | + if exclude_attributes is None: |
| 778 | + exclude_attributes = [] |
775 | 779 |
|
776 | | - if self.source: |
777 | | - res["source"] = self.source |
| 780 | + res = {} |
| 781 | + |
| 782 | + if "keys" not in exclude_attributes: |
| 783 | + _keys = [] |
| 784 | + for _k in self._keys: |
| 785 | + _ser = _k.to_dict() |
| 786 | + if _k.inactive_since: |
| 787 | + _ser["inactive_since"] = _k.inactive_since |
| 788 | + _keys.append(_ser) |
| 789 | + res["keys"] = _keys |
| 790 | + |
| 791 | + for attr, default in self.params.items(): |
| 792 | + if attr in exclude_attributes: |
| 793 | + continue |
| 794 | + val = getattr(self, attr) |
| 795 | + res[attr] = val |
778 | 796 |
|
779 | 797 | return res |
780 | 798 |
|
781 | 799 | def load(self, spec): |
| 800 | + """ |
| 801 | + Sets attributes according to a specification. |
| 802 | + Does not overwrite an existing attributes value with a default value. |
| 803 | +
|
| 804 | + :param spec: Dictionary with attributes and value to populate the instance with |
| 805 | + :return: The instance itself |
| 806 | + """ |
782 | 807 | _keys = spec.get("keys", []) |
783 | 808 | if _keys: |
784 | 809 | self.do_keys(_keys) |
785 | | - self.source = spec.get("source", None) |
786 | | - self.fileformat = spec.get("fileformat", "jwks") |
787 | | - self.last_updated = spec.get("last_updated", 0) |
788 | | - self.last_remote = spec.get("last_remote", None) |
789 | | - self.last_local = spec.get("last_local", None) |
790 | | - self.remote = spec.get("remote", False) |
791 | | - self.local = spec.get("local", False) |
792 | | - self.imp_jwks = spec.get("imp_jwks", None) |
793 | | - self.time_out = spec.get("time_out", 0) |
794 | | - self.cache_time = spec.get("cache_time", 0) |
795 | | - self.httpc_params = spec.get("httpc_params", {}) |
| 810 | + |
| 811 | + for attr, default in self.params.items(): |
| 812 | + val = spec.get(attr) |
| 813 | + if val: |
| 814 | + setattr(self, attr, val) |
| 815 | + |
| 816 | + return self |
| 817 | + |
| 818 | + def flush(self): |
| 819 | + self._keys = [] |
| 820 | + self.cache_time = (300,) |
| 821 | + self.etag = "" |
| 822 | + self.fileformat = "jwks" |
| 823 | + # self.httpc=None, |
| 824 | + self.httpc_params = (None,) |
| 825 | + self.ignore_errors_period = 0 |
| 826 | + self.ignore_errors_until = None |
| 827 | + self.ignore_invalid_keys = True |
| 828 | + self.imp_jwks = None |
| 829 | + self.keytype = ("RSA",) |
| 830 | + self.keyusage = (None,) |
| 831 | + self.last_local = None # UNIX timestamp of last local update |
| 832 | + self.last_remote = None # HTTP Date of last remote update |
| 833 | + self.last_updated = 0 |
| 834 | + self.local = False |
| 835 | + self.remote = False |
| 836 | + self.source = None |
| 837 | + self.time_out = 0 |
796 | 838 | return self |
797 | 839 |
|
798 | 840 |
|
@@ -1246,3 +1288,19 @@ def init_key(filename, type, kid="", **kwargs): |
1246 | 1288 | _new_key = key_gen(type, kid=kid, **kwargs) |
1247 | 1289 | dump_jwk(filename, _new_key) |
1248 | 1290 | return _new_key |
| 1291 | + |
| 1292 | + |
| 1293 | +def key_by_alg(alg: str): |
| 1294 | + if alg.startswith("RS"): |
| 1295 | + return key_gen("RSA", alg="RS256") |
| 1296 | + elif alg.startswith("ES"): |
| 1297 | + if alg == "ES256": |
| 1298 | + return key_gen("EC", crv="P-256") |
| 1299 | + elif alg == "ES384": |
| 1300 | + return key_gen("EC", crv="P-384") |
| 1301 | + elif alg == "ES512": |
| 1302 | + return key_gen("EC", crv="P-521") |
| 1303 | + elif alg.startswith("HS"): |
| 1304 | + return key_gen("sym") |
| 1305 | + |
| 1306 | + raise ValueError("Don't know who to create a key to use with '{}'".format(alg)) |
0 commit comments