|
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