Refactor Certificate Expiry Sensor (#32066)

* Cert Expiry refactor

* Unused parameter

* Reduce delay

* Deprecate 'name' config

* Use config entry unique_id

* Fix logic bugs found with tests

* Rewrite tests to use config flow core interfaces, validate created sensors

* Update strings

* Minor consistency fix

* Review fixes, complete test coverage

* Move error handling to helper

* Subclass exceptions

* Better tests

* Use first object reference

* Fix docstring
This commit is contained in:
jjlawren 2020-03-02 07:44:24 -06:00 committed by GitHub
parent eb90cefd84
commit df3f7687d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 652 additions and 236 deletions

View file

@ -1,12 +1,19 @@
"""Helper functions for the Cert Expiry platform."""
from datetime import datetime
import socket
import ssl
from .const import TIMEOUT
from .errors import (
ConnectionRefused,
ConnectionTimeout,
ResolveFailed,
ValidationFailure,
)
def get_cert(host, port):
"""Get the ssl certificate for the host and port combination."""
"""Get the certificate for the host and port combination."""
ctx = ssl.create_default_context()
address = (host, port)
with socket.create_connection(address, timeout=TIMEOUT) as sock:
@ -14,3 +21,24 @@ def get_cert(host, port):
# pylint disable: https://github.com/PyCQA/pylint/issues/3166
cert = ssock.getpeercert() # pylint: disable=no-member
return cert
async def get_cert_time_to_expiry(hass, hostname, port):
"""Return the certificate's time to expiry in days."""
try:
cert = await hass.async_add_executor_job(get_cert, hostname, port)
except socket.gaierror:
raise ResolveFailed(f"Cannot resolve hostname: {hostname}")
except socket.timeout:
raise ConnectionTimeout(f"Connection timeout with server: {hostname}:{port}")
except ConnectionRefusedError:
raise ConnectionRefused(f"Connection refused by server: {hostname}:{port}")
except ssl.CertificateError as err:
raise ValidationFailure(err.verify_message)
except ssl.SSLError as err:
raise ValidationFailure(err.args[0])
ts_seconds = ssl.cert_time_to_seconds(cert["notAfter"])
timestamp = datetime.fromtimestamp(ts_seconds)
expiry = timestamp - datetime.today()
return expiry.days