@@ -305,6 +305,7 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven
305
305
ModelError ,
306
306
Relation ,
307
307
RelationDataContent ,
308
+ Secret ,
308
309
SecretNotFoundError ,
309
310
Unit ,
310
311
)
@@ -317,7 +318,7 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven
317
318
318
319
# Increment this PATCH version before using `charmcraft publish-lib` or reset
319
320
# to 0 if you are raising the major API version
320
- LIBPATCH = 17
321
+ LIBPATCH = 20
321
322
322
323
PYDEPS = ["cryptography" , "jsonschema" ]
323
324
@@ -735,16 +736,16 @@ def calculate_expiry_notification_time(
735
736
"""
736
737
if provider_recommended_notification_time is not None :
737
738
provider_recommended_notification_time = abs (provider_recommended_notification_time )
738
- provider_recommendation_time_delta = (
739
- expiry_time - timedelta ( hours = provider_recommended_notification_time )
739
+ provider_recommendation_time_delta = expiry_time - timedelta (
740
+ hours = provider_recommended_notification_time
740
741
)
741
742
if validity_start_time < provider_recommendation_time_delta :
742
743
return provider_recommendation_time_delta
743
744
744
745
if requirer_recommended_notification_time is not None :
745
746
requirer_recommended_notification_time = abs (requirer_recommended_notification_time )
746
- requirer_recommendation_time_delta = (
747
- expiry_time - timedelta ( hours = requirer_recommended_notification_time )
747
+ requirer_recommendation_time_delta = expiry_time - timedelta (
748
+ hours = requirer_recommended_notification_time
748
749
)
749
750
if validity_start_time < requirer_recommendation_time_delta :
750
751
return requirer_recommendation_time_delta
@@ -1448,18 +1449,31 @@ def _revoke_certificates_for_which_no_csr_exists(self, relation_id: int) -> None
1448
1449
Returns:
1449
1450
None
1450
1451
"""
1451
- provider_certificates = self .get_provider_certificates (relation_id )
1452
- requirer_csrs = self .get_requirer_csrs (relation_id )
1452
+ provider_certificates = self .get_unsolicited_certificates (relation_id = relation_id )
1453
+ for provider_certificate in provider_certificates :
1454
+ self .on .certificate_revocation_request .emit (
1455
+ certificate = provider_certificate .certificate ,
1456
+ certificate_signing_request = provider_certificate .csr ,
1457
+ ca = provider_certificate .ca ,
1458
+ chain = provider_certificate .chain ,
1459
+ )
1460
+ self .remove_certificate (certificate = provider_certificate .certificate )
1461
+
1462
+ def get_unsolicited_certificates (
1463
+ self , relation_id : Optional [int ] = None
1464
+ ) -> List [ProviderCertificate ]:
1465
+ """Return provider certificates for which no certificate requests exists.
1466
+
1467
+ Those certificates should be revoked.
1468
+ """
1469
+ unsolicited_certificates : List [ProviderCertificate ] = []
1470
+ provider_certificates = self .get_provider_certificates (relation_id = relation_id )
1471
+ requirer_csrs = self .get_requirer_csrs (relation_id = relation_id )
1453
1472
list_of_csrs = [csr .csr for csr in requirer_csrs ]
1454
1473
for certificate in provider_certificates :
1455
1474
if certificate .csr not in list_of_csrs :
1456
- self .on .certificate_revocation_request .emit (
1457
- certificate = certificate .certificate ,
1458
- certificate_signing_request = certificate .csr ,
1459
- ca = certificate .ca ,
1460
- chain = certificate .chain ,
1461
- )
1462
- self .remove_certificate (certificate = certificate .certificate )
1475
+ unsolicited_certificates .append (certificate )
1476
+ return unsolicited_certificates
1463
1477
1464
1478
def get_outstanding_certificate_requests (
1465
1479
self , relation_id : Optional [int ] = None
@@ -1877,8 +1891,7 @@ def _on_relation_changed(self, event: RelationChangedEvent) -> None:
1877
1891
"Removing secret with label %s" ,
1878
1892
f"{ LIBID } -{ csr_in_sha256_hex } " ,
1879
1893
)
1880
- secret = self .model .get_secret (
1881
- label = f"{ LIBID } -{ csr_in_sha256_hex } " )
1894
+ secret = self .model .get_secret (label = f"{ LIBID } -{ csr_in_sha256_hex } " )
1882
1895
secret .remove_all_revisions ()
1883
1896
self .on .certificate_invalidated .emit (
1884
1897
reason = "revoked" ,
@@ -1966,9 +1979,10 @@ def _on_secret_expired(self, event: SecretExpiredEvent) -> None:
1966
1979
Args:
1967
1980
event (SecretExpiredEvent): Juju event
1968
1981
"""
1969
- if not event .secret .label or not event .secret .label .startswith (f"{ LIBID } -" ):
1982
+ csr = self ._get_csr_from_secret (event .secret )
1983
+ if not csr :
1984
+ logger .error ("Failed to get CSR from secret %s" , event .secret .label )
1970
1985
return
1971
- csr = event .secret .get_content ()["csr" ]
1972
1986
provider_certificate = self ._find_certificate_in_relation_data (csr )
1973
1987
if not provider_certificate :
1974
1988
# A secret expired but we did not find matching certificate. Cleaning up
@@ -2008,3 +2022,18 @@ def _find_certificate_in_relation_data(self, csr: str) -> Optional[ProviderCerti
2008
2022
continue
2009
2023
return provider_certificate
2010
2024
return None
2025
+
2026
+ def _get_csr_from_secret (self , secret : Secret ) -> str :
2027
+ """Extract the CSR from the secret label or content.
2028
+
2029
+ This function is a workaround to maintain backwards compatibility
2030
+ and fix the issue reported in
2031
+ https://github.com/canonical/tls-certificates-interface/issues/228
2032
+ """
2033
+ if not (csr := secret .get_content ().get ("csr" , "" )):
2034
+ # In versions <14 of the Lib we were storing the CSR in the label of the secret
2035
+ # The CSR now is stored int the content of the secret, which was a breaking change
2036
+ # Here we get the CSR if the secret was created by an app using libpatch 14 or lower
2037
+ if secret .label and secret .label .startswith (f"{ LIBID } -" ):
2038
+ csr = secret .label [len (f"{ LIBID } -" ) :]
2039
+ return csr
0 commit comments