@@ -305,6 +305,7 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven
305305 ModelError ,
306306 Relation ,
307307 RelationDataContent ,
308+ Secret ,
308309 SecretNotFoundError ,
309310 Unit ,
310311)
@@ -317,7 +318,7 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven
317318
318319# Increment this PATCH version before using `charmcraft publish-lib` or reset
319320# to 0 if you are raising the major API version
320- LIBPATCH = 17
321+ LIBPATCH = 19
321322
322323PYDEPS = ["cryptography" , "jsonschema" ]
323324
@@ -735,16 +736,16 @@ def calculate_expiry_notification_time(
735736 """
736737 if provider_recommended_notification_time is not None :
737738 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
740741 )
741742 if validity_start_time < provider_recommendation_time_delta :
742743 return provider_recommendation_time_delta
743744
744745 if requirer_recommended_notification_time is not None :
745746 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
748749 )
749750 if validity_start_time < requirer_recommendation_time_delta :
750751 return requirer_recommendation_time_delta
@@ -1448,18 +1449,33 @@ def _revoke_certificates_for_which_no_csr_exists(self, relation_id: int) -> None
14481449 Returns:
14491450 None
14501451 """
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 (
1453+ relation_id = relation_id
1454+ )
1455+ for provider_certificate in provider_certificates :
1456+ self .on .certificate_revocation_request .emit (
1457+ certificate = provider_certificate .certificate ,
1458+ certificate_signing_request = provider_certificate .csr ,
1459+ ca = provider_certificate .ca ,
1460+ chain = provider_certificate .chain ,
1461+ )
1462+ self .remove_certificate (certificate = provider_certificate .certificate )
1463+
1464+ def get_unsolicited_certificates (
1465+ self , relation_id : Optional [int ] = None
1466+ ) -> List [ProviderCertificate ]:
1467+ """Return provider certificates for which no certificate requests exists.
1468+
1469+ Those certificates should be revoked.
1470+ """
1471+ unsolicited_certificates : List [ProviderCertificate ] = []
1472+ provider_certificates = self .get_provider_certificates (relation_id = relation_id )
1473+ requirer_csrs = self .get_requirer_csrs (relation_id = relation_id )
14531474 list_of_csrs = [csr .csr for csr in requirer_csrs ]
14541475 for certificate in provider_certificates :
14551476 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 )
1477+ unsolicited_certificates .append (certificate )
1478+ return unsolicited_certificates
14631479
14641480 def get_outstanding_certificate_requests (
14651481 self , relation_id : Optional [int ] = None
@@ -1877,8 +1893,7 @@ def _on_relation_changed(self, event: RelationChangedEvent) -> None:
18771893 "Removing secret with label %s" ,
18781894 f"{ LIBID } -{ csr_in_sha256_hex } " ,
18791895 )
1880- secret = self .model .get_secret (
1881- label = f"{ LIBID } -{ csr_in_sha256_hex } " )
1896+ secret = self .model .get_secret (label = f"{ LIBID } -{ csr_in_sha256_hex } " )
18821897 secret .remove_all_revisions ()
18831898 self .on .certificate_invalidated .emit (
18841899 reason = "revoked" ,
@@ -1966,9 +1981,10 @@ def _on_secret_expired(self, event: SecretExpiredEvent) -> None:
19661981 Args:
19671982 event (SecretExpiredEvent): Juju event
19681983 """
1969- if not event .secret .label or not event .secret .label .startswith (f"{ LIBID } -" ):
1984+ csr = self ._get_csr_from_secret (event .secret )
1985+ if not csr :
1986+ logger .error ("Failed to get CSR from secret %s" , event .secret .label )
19701987 return
1971- csr = event .secret .get_content ()["csr" ]
19721988 provider_certificate = self ._find_certificate_in_relation_data (csr )
19731989 if not provider_certificate :
19741990 # A secret expired but we did not find matching certificate. Cleaning up
@@ -2008,3 +2024,18 @@ def _find_certificate_in_relation_data(self, csr: str) -> Optional[ProviderCerti
20082024 continue
20092025 return provider_certificate
20102026 return None
2027+
2028+ def _get_csr_from_secret (self , secret : Secret ) -> str :
2029+ """Extract the CSR from the secret label or content.
2030+
2031+ This function is a workaround to maintain backwards compatiblity
2032+ and fix the issue reported in
2033+ https://github.com/canonical/tls-certificates-interface/issues/228
2034+ """
2035+ if not (csr := secret .get_content ().get ("csr" , "" )):
2036+ # In versions <14 of the Lib we were storing the CSR in the label of the secret
2037+ # The CSR now is stored int the content of the secret, which was a breaking change
2038+ # Here we get the CSR if the secret was created by an app using libpatch 14 or lower
2039+ if secret .label and secret .label .startswith (f"{ LIBID } -" ):
2040+ csr = secret .label [len (f"{ LIBID } -" ) :]
2041+ return csr
0 commit comments