Skip to content

Commit 467254c

Browse files
committed
Clearer demarcation between KeyIssuer and KeyJar.
Set of tests for KeyIssuer.
1 parent eb73ee8 commit 467254c

File tree

4 files changed

+890
-127
lines changed

4 files changed

+890
-127
lines changed

src/cryptojwt/key_issuer.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import logging
3+
import os
34

45
from requests import request
56

@@ -10,6 +11,9 @@
1011

1112
__author__ = 'Roland Hedberg'
1213

14+
from .key_bundle import key_diff
15+
from .key_bundle import update_key_bundle
16+
1317
from .utils import importer
1418

1519
from .utils import qualified_name
@@ -424,6 +428,23 @@ def __iter__(self):
424428
for bundle in self._bundles:
425429
yield bundle
426430

431+
def __eq__(self, other):
432+
if not isinstance(other, self.__class__):
433+
return False
434+
435+
if len(other.all_keys()) != len(self.all_keys()):
436+
return False
437+
438+
for k in self.all_keys():
439+
if k not in other:
440+
return False
441+
442+
for k in other.all_keys():
443+
if k not in self:
444+
return False
445+
446+
return True
447+
427448

428449
# =============================================================================
429450

@@ -479,3 +500,129 @@ def build_keyissuer(key_conf, kid_template="", key_issuer=None, issuer_id=''):
479500
key_issuer.add(bundle)
480501

481502
return key_issuer
503+
504+
505+
def rotate_keys(key_conf, issuer, kid_template=""):
506+
new_keys = build_keyissuer(key_conf, kid_template)
507+
issuer.mark_all_keys_as_inactive()
508+
for kb in new_keys:
509+
issuer.add_kb(kb)
510+
return issuer
511+
512+
513+
def init_key_issuer(public_path='', private_path='', key_defs='', read_only=True,
514+
storage_conf=None, abstract_storage_cls=None):
515+
"""
516+
A number of cases here:
517+
518+
1. A private path is given
519+
520+
a. The file exists and a JWKS is found there.
521+
From that JWKS a KeyJar instance is built.
522+
b.
523+
If the private path file doesn't exit the key definitions are
524+
used to build a KeyJar instance. A JWKS with the private keys are
525+
written to the file named in private_path.
526+
527+
If a public path is also provided a JWKS with public keys are written
528+
to that file.
529+
530+
2. A public path is given but no private path.
531+
532+
a. If the public path file exists then the JWKS in that file is used to
533+
construct a KeyJar.
534+
b. If no such file exists then a KeyJar will be built
535+
based on the key_defs specification and a JWKS with the public keys
536+
will be written to the public path file.
537+
538+
3. If neither a public path nor a private path is given then a KeyJar is
539+
built based on the key_defs specification and no JWKS will be written
540+
to file.
541+
542+
In all cases a KeyJar instance is returned
543+
544+
The keys stored in the KeyJar will be stored under the '' identifier.
545+
546+
:param public_path: A file path to a file that contains a JWKS with public
547+
keys
548+
:param private_path: A file path to a file that contains a JWKS with
549+
private keys.
550+
:param key_defs: A definition of what keys should be created if they are
551+
not already available
552+
:param read_only: This function should not attempt to write anything
553+
to a file system.
554+
:return: An instantiated :py:class;`oidcmsg.key_jar.KeyJar` instance
555+
"""
556+
557+
if private_path:
558+
if os.path.isfile(private_path):
559+
_jwks = open(private_path, 'r').read()
560+
_issuer = KeyIssuer()
561+
_issuer.import_jwks(json.loads(_jwks))
562+
if key_defs:
563+
_kb = _issuer[0]
564+
_diff = key_diff(_kb, key_defs)
565+
if _diff:
566+
update_key_bundle(_kb, _diff)
567+
if read_only:
568+
logger.error('Not allowed to write to disc!')
569+
else:
570+
_issuer.set([_kb])
571+
jwks = _issuer.export_jwks(private=True)
572+
fp = open(private_path, 'w')
573+
fp.write(json.dumps(jwks))
574+
fp.close()
575+
else:
576+
_issuer = build_keyissuer(key_defs)
577+
if not read_only:
578+
jwks = _issuer.export_jwks(private=True)
579+
head, tail = os.path.split(private_path)
580+
if head and not os.path.isdir(head):
581+
os.makedirs(head)
582+
fp = open(private_path, 'w')
583+
fp.write(json.dumps(jwks))
584+
fp.close()
585+
586+
if public_path and not read_only:
587+
jwks = _issuer.export_jwks() # public part
588+
head, tail = os.path.split(public_path)
589+
if head and not os.path.isdir(head):
590+
os.makedirs(head)
591+
fp = open(public_path, 'w')
592+
fp.write(json.dumps(jwks))
593+
fp.close()
594+
elif public_path:
595+
if os.path.isfile(public_path):
596+
_jwks = open(public_path, 'r').read()
597+
_issuer = KeyIssuer()
598+
_issuer.import_jwks(json.loads(_jwks))
599+
if key_defs:
600+
_kb = _issuer[0]
601+
_diff = key_diff(_kb, key_defs)
602+
if _diff:
603+
if read_only:
604+
logger.error('Not allowed to write to disc!')
605+
else:
606+
update_key_bundle(_kb, _diff)
607+
_issuer.set([_kb])
608+
jwks = _issuer.export_jwks()
609+
fp = open(public_path, 'w')
610+
fp.write(json.dumps(jwks))
611+
fp.close()
612+
else:
613+
_issuer = build_keyissuer(key_defs)
614+
if not read_only:
615+
_jwks = _issuer.export_jwks()
616+
head, tail = os.path.split(public_path)
617+
if head and not os.path.isdir(head):
618+
os.makedirs(head)
619+
fp = open(public_path, 'w')
620+
fp.write(json.dumps(_jwks))
621+
fp.close()
622+
else:
623+
_issuer = build_keyissuer(key_defs)
624+
625+
if _issuer is None:
626+
raise ValueError('Could not find any keys')
627+
628+
return _issuer

src/cryptojwt/key_jar.py

Lines changed: 55 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import json
22
import logging
3-
import os
43
from typing import List
54
from typing import Optional
65

@@ -9,16 +8,14 @@
98
from .jwe.jwe import alg2keytype as jwe_alg2keytype
109
from .jws.utils import alg2keytype as jws_alg2keytype
1110
from .key_bundle import KeyBundle
12-
from .key_bundle import key_diff
13-
from .key_bundle import update_key_bundle
14-
15-
__author__ = 'Roland Hedberg'
16-
1711
from .key_issuer import KeyIssuer
1812
from .key_issuer import build_keyissuer
13+
from .key_issuer import init_key_issuer
1914
from .utils import importer
2015
from .utils import qualified_name
2116

17+
__author__ = 'Roland Hedberg'
18+
2219
logger = logging.getLogger(__name__)
2320

2421

@@ -92,7 +89,7 @@ def _get_issuer(self, issuer_id: str) -> Optional[KeyIssuer]:
9289

9390
return self._issuers.get(issuer_id)
9491

95-
def _add_issuer(self, issuer_id):
92+
def _add_issuer(self, issuer_id) -> KeyIssuer:
9693
_iss = KeyIssuer(ca_certs=self.ca_certs, name=issuer_id,
9794
keybundle_cls=self.keybundle_cls,
9895
remove_after=self.remove_after,
@@ -197,51 +194,53 @@ def get(self, key_use, key_type="", issuer_id="", kid=None, **kwargs):
197194
if _issuer is None:
198195
return []
199196

200-
lst = []
201-
for bundle in _issuer:
202-
if key_type:
203-
if key_use in ['ver', 'dec']:
204-
_bkeys = bundle.get(key_type, only_active=False)
205-
else:
206-
_bkeys = bundle.get(key_type)
207-
else:
208-
_bkeys = bundle.keys()
209-
for key in _bkeys:
210-
if key.inactive_since and key_use != "sig":
211-
# Skip inactive keys unless for signature verification
212-
continue
213-
if not key.use or use == key.use:
214-
if kid:
215-
if key.kid == kid:
216-
lst.append(key)
217-
break
218-
else:
219-
continue
220-
else:
221-
lst.append(key)
222-
223-
# if elliptic curve, have to check if I have a key of the right curve
224-
if key_type == "EC" and "alg" in kwargs:
225-
name = "P-{}".format(kwargs["alg"][2:]) # the type
226-
_lst = []
227-
for key in lst:
228-
if name != key.crv:
229-
continue
230-
_lst.append(key)
231-
lst = _lst
232-
233-
if use == 'enc' and key_type == 'oct' and issuer_id != '':
234-
# Add my symmetric keys
235-
_issuer = self._get_issuer('')
236-
if _issuer:
237-
for kb in _issuer:
238-
for key in kb.get(key_type):
239-
if key.inactive_since:
240-
continue
241-
if not key.use or key.use == use:
242-
lst.append(key)
243-
244-
return lst
197+
return _issuer.get(key_use=key_use, key_type=key_type, kid=kid, **kwargs)
198+
199+
# lst = []
200+
# for bundle in _issuer:
201+
# if key_type:
202+
# if key_use in ['ver', 'dec']:
203+
# _bkeys = bundle.get(key_type, only_active=False)
204+
# else:
205+
# _bkeys = bundle.get(key_type)
206+
# else:
207+
# _bkeys = bundle.keys()
208+
# for key in _bkeys:
209+
# if key.inactive_since and key_use != "sig":
210+
# # Skip inactive keys unless for signature verification
211+
# continue
212+
# if not key.use or use == key.use:
213+
# if kid:
214+
# if key.kid == kid:
215+
# lst.append(key)
216+
# break
217+
# else:
218+
# continue
219+
# else:
220+
# lst.append(key)
221+
#
222+
# # if elliptic curve, have to check if I have a key of the right curve
223+
# if key_type == "EC" and "alg" in kwargs:
224+
# name = "P-{}".format(kwargs["alg"][2:]) # the type
225+
# _lst = []
226+
# for key in lst:
227+
# if name != key.crv:
228+
# continue
229+
# _lst.append(key)
230+
# lst = _lst
231+
#
232+
# if use == 'enc' and key_type == 'oct' and issuer_id != '':
233+
# # Add my symmetric keys
234+
# _issuer = self._get_issuer('')
235+
# if _issuer:
236+
# for kb in _issuer:
237+
# for key in kb.get(key_type):
238+
# if key.inactive_since:
239+
# continue
240+
# if not key.use or key.use == use:
241+
# lst.append(key)
242+
#
243+
# return lst
245244

246245
def get_signing_key(self, key_type="", issuer_id="", kid=None, **kwargs):
247246
"""
@@ -472,12 +471,7 @@ def __eq__(self, other):
472471

473472
# Keys per issuer must be the same
474473
for iss in self.owners():
475-
sk = self.get_issuer_keys(iss)
476-
ok = other.get_issuer_keys(iss)
477-
if len(sk) != len(ok):
478-
return False
479-
480-
if not any(k in ok for k in sk):
474+
if self[iss] != other[iss]:
481475
return False
482476

483477
return True
@@ -825,73 +819,9 @@ def init_key_jar(public_path='', private_path='', key_defs='', issuer_id='', rea
825819
:return: An instantiated :py:class;`oidcmsg.key_jar.KeyJar` instance
826820
"""
827821

828-
if private_path:
829-
if os.path.isfile(private_path):
830-
_jwks = open(private_path, 'r').read()
831-
_issuer = KeyIssuer(name=issuer_id)
832-
_issuer.import_jwks(json.loads(_jwks))
833-
if key_defs:
834-
_kb = _issuer[0]
835-
_diff = key_diff(_kb, key_defs)
836-
if _diff:
837-
update_key_bundle(_kb, _diff)
838-
if read_only:
839-
logger.error('Not allowed to write to disc!')
840-
else:
841-
_issuer.set([_kb])
842-
jwks = _issuer.export_jwks(private=True)
843-
fp = open(private_path, 'w')
844-
fp.write(json.dumps(jwks))
845-
fp.close()
846-
else:
847-
_issuer = build_keyissuer(key_defs, issuer_id=issuer_id)
848-
if not read_only:
849-
jwks = _issuer.export_jwks(private=True)
850-
head, tail = os.path.split(private_path)
851-
if head and not os.path.isdir(head):
852-
os.makedirs(head)
853-
fp = open(private_path, 'w')
854-
fp.write(json.dumps(jwks))
855-
fp.close()
856-
857-
if public_path and not read_only:
858-
jwks = _issuer.export_jwks() # public part
859-
head, tail = os.path.split(public_path)
860-
if head and not os.path.isdir(head):
861-
os.makedirs(head)
862-
fp = open(public_path, 'w')
863-
fp.write(json.dumps(jwks))
864-
fp.close()
865-
elif public_path:
866-
if os.path.isfile(public_path):
867-
_jwks = open(public_path, 'r').read()
868-
_issuer = KeyIssuer(name=issuer_id)
869-
_issuer.import_jwks(json.loads(_jwks))
870-
if key_defs:
871-
_kb = _issuer[0]
872-
_diff = key_diff(_kb, key_defs)
873-
if _diff:
874-
if read_only:
875-
logger.error('Not allowed to write to disc!')
876-
else:
877-
update_key_bundle(_kb, _diff)
878-
_issuer.set([_kb])
879-
jwks = _issuer.export_jwks()
880-
fp = open(public_path, 'w')
881-
fp.write(json.dumps(jwks))
882-
fp.close()
883-
else:
884-
_issuer = build_keyissuer(key_defs, issuer_id=issuer_id)
885-
if not read_only:
886-
_jwks = _issuer.export_jwks(issuer=issuer_id)
887-
head, tail = os.path.split(public_path)
888-
if head and not os.path.isdir(head):
889-
os.makedirs(head)
890-
fp = open(public_path, 'w')
891-
fp.write(json.dumps(_jwks))
892-
fp.close()
893-
else:
894-
_issuer = build_keyissuer(key_defs, issuer_id=issuer_id)
822+
_issuer = init_key_issuer(public_path=public_path, private_path=private_path,
823+
key_defs=key_defs, read_only=read_only,
824+
storage_conf=storage_conf, abstract_storage_cls=abstract_storage_cls)
895825

896826
if _issuer is None:
897827
raise ValueError('Could not find any keys')

0 commit comments

Comments
 (0)