From 5e6d2562f9edc977447d4af4ced7d939dfd0f7bb Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Sat, 29 Apr 2023 16:34:23 -0700 Subject: [PATCH] add certbot command parser --- CHANGELOG | 3 +- README.md | 1 + completions/jc_bash_completion.sh | 4 +- completions/jc_zsh_completion.sh | 6 +- jc/lib.py | 1 + jc/parsers/certbot.py | 255 ++++++++++++++++++++ jc/utils.py | 1 + man/jc.1 | 7 +- tests/fixtures/generic/certbot-account.json | 1 + tests/fixtures/generic/certbot-account.out | 5 + tests/fixtures/generic/certbot-certs.json | 1 + tests/fixtures/generic/certbot-certs.out | 20 ++ tests/test_certbot.py | 58 +++++ tests/test_jc_utils.py | 2 + 14 files changed, 359 insertions(+), 6 deletions(-) create mode 100644 jc/parsers/certbot.py create mode 100644 tests/fixtures/generic/certbot-account.json create mode 100644 tests/fixtures/generic/certbot-account.out create mode 100644 tests/fixtures/generic/certbot-certs.json create mode 100644 tests/fixtures/generic/certbot-certs.out create mode 100644 tests/test_certbot.py diff --git a/CHANGELOG b/CHANGELOG index a434d33c..1091af80 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,8 @@ jc changelog -20230421 v1.23.2 +20230429 v1.23.2 - Add `bluetoothctl` command parser +- Add `certbot` command parser for `certificates` and `show_account` options - Fix `acpi` command parser for "Not charging" battery status lines - Fix `iwconfig` command parser for SSIDs with dashes in the name - Fix `crontab` command parsers for incorrect variable parsing in some cases diff --git a/README.md b/README.md index 2fcb26d7..6e946df6 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,7 @@ option. | `--cbt` | `cbt` (Google Bigtable) command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cbt) | | `--cef` | CEF string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef) | | `--cef-s` | CEF string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef_s) | +| `--certbot` | `certbot` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/certbot) | | `--chage` | `chage --list` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/chage) | | `--cksum` | `cksum` and `sum` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cksum) | | `--clf` | Common and Combined Log Format file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/clf) | diff --git a/completions/jc_bash_completion.sh b/completions/jc_bash_completion.sh index 56763aff..39f7cba7 100644 --- a/completions/jc_bash_completion.sh +++ b/completions/jc_bash_completion.sh @@ -3,8 +3,8 @@ _jc() local cur prev words cword jc_commands jc_parsers jc_options \ jc_about_options jc_about_mod_options jc_help_options jc_special_options - jc_commands=(acpi airport arp blkid bluetoothctl cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo zpool) - jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --ssh-conf --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status) + jc_commands=(acpi airport arp blkid bluetoothctl cbt certbot chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo zpool) + jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --ssh-conf --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status) jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --unbuffer -u --yaml-out -y) jc_about_options=(--about -a) jc_about_mod_options=(--pretty -p --yaml-out -y --monochrome -m --force-color -C) diff --git a/completions/jc_zsh_completion.sh b/completions/jc_zsh_completion.sh index 10d67532..cdf9074c 100644 --- a/completions/jc_zsh_completion.sh +++ b/completions/jc_zsh_completion.sh @@ -9,7 +9,7 @@ _jc() { jc_help_options jc_help_options_describe \ jc_special_options jc_special_options_describe - jc_commands=(acpi airport arp blkid bluetoothctl cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo zpool) + jc_commands=(acpi airport arp blkid bluetoothctl cbt certbot chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo zpool) jc_commands_describe=( 'acpi:run "acpi" command with magic syntax.' 'airport:run "airport" command with magic syntax.' @@ -17,6 +17,7 @@ _jc() { 'blkid:run "blkid" command with magic syntax.' 'bluetoothctl:run "bluetoothctl" command with magic syntax.' 'cbt:run "cbt" command with magic syntax.' + 'certbot:run "certbot" command with magic syntax.' 'chage:run "chage" command with magic syntax.' 'cksum:run "cksum" command with magic syntax.' 'crontab:run "crontab" command with magic syntax.' @@ -105,7 +106,7 @@ _jc() { 'zipinfo:run "zipinfo" command with magic syntax.' 'zpool:run "zpool" command with magic syntax.' ) - jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --ssh-conf --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status) + jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --ssh-conf --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status) jc_parsers_describe=( '--acpi:`acpi` command parser' '--airport:`airport -I` command parser' @@ -118,6 +119,7 @@ _jc() { '--cbt:`cbt` (Google Bigtable) command parser' '--cef:CEF string parser' '--cef-s:CEF string streaming parser' + '--certbot:`certbot` command parser' '--chage:`chage --list` command parser' '--cksum:`cksum` and `sum` command parser' '--clf:Common and Combined Log Format file parser' diff --git a/jc/lib.py b/jc/lib.py index a9639896..80afd720 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -23,6 +23,7 @@ parsers: List[str] = [ 'cbt', 'cef', 'cef-s', + 'certbot', 'chage', 'cksum', 'clf', diff --git a/jc/parsers/certbot.py b/jc/parsers/certbot.py new file mode 100644 index 00000000..5c1ee405 --- /dev/null +++ b/jc/parsers/certbot.py @@ -0,0 +1,255 @@ +"""jc - JSON Convert `certbot` command output parser + +Verbose options are not supported. + +Usage (cli): + + $ certbot show_account | jc --certbot + $ certbot certificates | jc --certbot + +or + + $ jc certbot show_account + $ jc certbot certificates + +Usage (module): + + import jc + result = jc.parse('certbot', certbot_command_output) + +Schema: + + { + "certificates": [ + { + "name": string, + "serial_number": string, + "key_type": string, + "domains": [ + string + ], + "expiration_date": string, + "expiration_date_epoch": integer, + "expiration_date_epoch_utc": integer, + "expiration_date_iso": string, + "validity": string, + "certificate_path": string, + "private_key_path": string + } + ], + "account": { + "server": string, + "url": string, + "email": string + } + } + +Examples: + + $ certbot certificates | jc --certbot -p + { + "certificates": [ + { + "name": "example.com", + "serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "key_type": "RSA", + "domains": [ + "example.com", + "www.example.com" + ], + "expiration_date": "2023-05-11 01:33:10+00:00", + "validity": "63 days", + "certificate_path": "/etc/letsencrypt/live/example.com/chain.pem", + "private_key_path": "/etc/letsencrypt/live/example.com/priv.pem", + "expiration_date_epoch": 1683793990, + "expiration_date_epoch_utc": 1683768790, + "expiration_date_iso": "2023-05-11T01:33:10+00:00" + }, + { + "name": "example.org", + "serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", + "key_type": "RSA", + "domains": [ + "example.org", + "www.example.org" + ], + "expiration_date": "2023-06-12 01:35:30+00:00", + "validity": "63 days", + "certificate_path": "/etc/letsencrypt/live/example.org/chain.pem", + "private_key_path": "/etc/letsencrypt/live/example.org/key.pem", + "expiration_date_epoch": 1686558930, + "expiration_date_epoch_utc": 1686533730, + "expiration_date_iso": "2023-06-12T01:35:30+00:00" + } + ] + } + + $ certbot certificates | jc --certbot -p -r + { + "certificates": [ + { + "name": "example.com", + "serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "key_type": "RSA", + "domains": [ + "example.com", + "www.example.com" + ], + "expiration_date": "2023-05-11 01:33:10+00:00", + "validity": "63 days", + "certificate_path": "/etc/letsencrypt/live/example.com/chain.pem", + "private_key_path": "/etc/letsencrypt/live/example.com/priv.pem" + }, + { + "name": "example.org", + "serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", + "key_type": "RSA", + "domains": [ + "example.org", + "www.example.org" + ], + "expiration_date": "2023-06-12 01:35:30+00:00", + "validity": "63 days", + "certificate_path": "/etc/letsencrypt/live/example.org/chain.pem", + "private_key_path": "/etc/letsencrypt/live/example.org/key.pem" + } + ] + } + + $ certbot show_account | jc --certbot -p + { + "account": { + "server": "https://acme-staging-v02.api.letsencrypt.org/directory", + "url": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/123", + "email": "some@example.com" + } + } +""" +from typing import List, Dict +from jc.jc_types import JSONDictType +import jc.utils + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`certbot` command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['command'] + magic_commands = ['certbot'] + + +__version__ = info.version + + +def _process(proc_data: JSONDictType) -> JSONDictType: + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + Dictionary. Structured to conform to the schema. + """ + if 'certificates' in proc_data: + for cert in proc_data['certificates']: + if 'expiration_date' in cert: + dt = jc.utils.timestamp(cert['expiration_date'], format_hint=(1760,)) + cert['expiration_date_epoch'] = dt.naive + cert['expiration_date_epoch_utc'] = dt.utc + cert['expiration_date_iso'] = dt.iso + return proc_data + + +def parse( + data: str, + raw: bool = False, + quiet: bool = False +) -> JSONDictType: + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + Dictionary. Raw or processed structured data. + """ + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output: Dict = {} + cert_list: List = [] + cert_dict: Dict = {} + acct_dict: Dict = {} + cmd_option = '' + + if jc.utils.has_data(data): + + if 'Found the following certs:\n' in data: + cmd_option = 'certificates' + else: + cmd_option = 'account' + + for line in filter(None, data.splitlines()): + + if cmd_option == 'certificates': + if line.startswith(' Certificate Name:'): + if cert_dict: + cert_list.append(cert_dict) + cert_dict = {} + + cert_dict['name'] = line.split()[-1] + + if line.startswith(' Serial Number:'): + cert_dict['serial_number'] = line.split()[-1] + + if line.startswith(' Key Type:'): + cert_dict['key_type'] = line.split(': ', maxsplit=1)[1] + + if line.startswith(' Domains:'): + splitline = line.split(': ', maxsplit=1)[1] + cert_dict['domains'] = splitline.split() + + if line.startswith(' Expiry Date:'): + splitline = line.split(': ', maxsplit=1)[1] + cert_datetime = splitline.split('(')[0] + validity = splitline.split('(')[1] + cert_dict['expiration_date'] = cert_datetime.strip() + cert_dict['validity'] = validity[:-1].replace('VALID: ', '') + + if line.startswith(' Certificate Path:'): + cert_dict['certificate_path'] = line.split(': ', maxsplit=1)[1] + + if line.startswith(' Private Key Path:'): + cert_dict['private_key_path'] = line.split(': ', maxsplit=1)[1] + + if cmd_option == 'account': + if line.startswith('Account details for server'): + acct_dict['server'] = line.split()[-1][:-1] + + if line.startswith(' Account URL:'): + acct_dict['url'] = line.split()[-1] + + if line.startswith(' Email contact:'): + acct_dict['email'] = line.split()[-1] + + if acct_dict: + raw_output['account'] = acct_dict + + if cert_dict: + cert_list.append(cert_dict) + + if cert_list: + raw_output['certificates'] = cert_list + + return raw_output if raw else _process(raw_output) diff --git a/jc/utils.py b/jc/utils.py index d99e649f..a1c4392f 100644 --- a/jc/utils.py +++ b/jc/utils.py @@ -408,6 +408,7 @@ class timestamp: {'id': 1710, 'format': '%m/%d/%Y, %I:%M:%S %p UTC%z', 'locale': None}, # Windows english format with UTC tz (found in systeminfo cli output): 3/22/2021, 1:15:51 PM (UTC+0000) {'id': 1750, 'format': '%Y/%m/%d-%H:%M:%S.%f', 'locale': None}, # Google Big Table format with no timezone: 1970/01/01-01:00:00.000000 {'id': 1755, 'format': '%Y/%m/%d-%H:%M:%S.%f%z', 'locale': None}, # Google Big Table format with timezone: 1970/01/01-01:00:00.000000+00:00 + {'id': 1760, 'format': '%Y-%m-%d %H:%M:%S%z', 'locale': None}, # certbot format with timezone: 2023-06-12 01:35:30+00:00 {'id': 1800, 'format': '%d/%b/%Y:%H:%M:%S %z', 'locale': None}, # Common Log Format: 10/Oct/2000:13:55:36 -0700 {'id': 2000, 'format': '%a %d %b %Y %I:%M:%S %p %Z', 'locale': None}, # en_US.UTF-8 local format (found in upower cli output): Tue 23 Mar 2021 04:12:11 PM UTC {'id': 3000, 'format': '%a %d %b %Y %I:%M:%S %p', 'locale': None}, # en_US.UTF-8 local format with non-UTC tz (found in upower cli output): Tue 23 Mar 2021 04:12:11 PM IST diff --git a/man/jc.1 b/man/jc.1 index 4d9f3344..932dea13 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -1,4 +1,4 @@ -.TH jc 1 2023-04-22 1.23.2 "JSON Convert" +.TH jc 1 2023-04-29 1.23.2 "JSON Convert" .SH NAME \fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, and strings @@ -92,6 +92,11 @@ CEF string parser \fB--cef-s\fP CEF string streaming parser +.TP +.B +\fB--certbot\fP +`certbot` command parser + .TP .B \fB--chage\fP diff --git a/tests/fixtures/generic/certbot-account.json b/tests/fixtures/generic/certbot-account.json new file mode 100644 index 00000000..f7717c74 --- /dev/null +++ b/tests/fixtures/generic/certbot-account.json @@ -0,0 +1 @@ +{"account":{"server":"https://acme-staging-v02.api.letsencrypt.org/directory","url":"https://acme-staging-v02.api.letsencrypt.org/acme/acct/12345679","email":"some@example.com"}} diff --git a/tests/fixtures/generic/certbot-account.out b/tests/fixtures/generic/certbot-account.out new file mode 100644 index 00000000..3fdfec3c --- /dev/null +++ b/tests/fixtures/generic/certbot-account.out @@ -0,0 +1,5 @@ +Saving debug log to /var/log/letsencrypt/letsencrypt.log +Account details for server https://acme-staging-v02.api.letsencrypt.org/directory: + Account URL: https://acme-staging-v02.api.letsencrypt.org/acme/acct/12345679 + Email contact: some@example.com + diff --git a/tests/fixtures/generic/certbot-certs.json b/tests/fixtures/generic/certbot-certs.json new file mode 100644 index 00000000..cec83684 --- /dev/null +++ b/tests/fixtures/generic/certbot-certs.json @@ -0,0 +1 @@ +{"certificates":[{"name":"example.com","serial_number":"3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","key_type":"RSA","domains":["example.com","www.example.com"],"expiration_date":"2023-05-11 01:33:10+00:00","validity":"63 days","certificate_path":"/etc/letsencrypt/live/example.com/fullchain.pem","private_key_path":"/etc/letsencrypt/live/example.com/privkey.pem","expiration_date_epoch":1683793990,"expiration_date_epoch_utc":1683768790,"expiration_date_iso":"2023-05-11T01:33:10+00:00"},{"name":"example.org","serial_number":"3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy","key_type":"RSA","domains":["example.org","www.example.org"],"expiration_date":"2023-06-12 01:35:30+00:00","validity":"63 days","certificate_path":"/etc/letsencrypt/live/example.org/fullchain.pem","private_key_path":"/etc/letsencrypt/live/example.org/privkey.pem","expiration_date_epoch":1686558930,"expiration_date_epoch_utc":1686533730,"expiration_date_iso":"2023-06-12T01:35:30+00:00"}]} diff --git a/tests/fixtures/generic/certbot-certs.out b/tests/fixtures/generic/certbot-certs.out new file mode 100644 index 00000000..d3ff94c3 --- /dev/null +++ b/tests/fixtures/generic/certbot-certs.out @@ -0,0 +1,20 @@ +Saving debug log to /var/log/letsencrypt/letsencrypt.log + +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Found the following certs: + Certificate Name: example.com + Serial Number: 3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + Key Type: RSA + Domains: example.com www.example.com + Expiry Date: 2023-05-11 01:33:10+00:00 (VALID: 63 days) + Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem + Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem + Certificate Name: example.org + Serial Number: 3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy + Key Type: RSA + Domains: example.org www.example.org + Expiry Date: 2023-06-12 01:35:30+00:00 (VALID: 63 days) + Certificate Path: /etc/letsencrypt/live/example.org/fullchain.pem + Private Key Path: /etc/letsencrypt/live/example.org/privkey.pem +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/tests/test_certbot.py b/tests/test_certbot.py new file mode 100644 index 00000000..27379a73 --- /dev/null +++ b/tests/test_certbot.py @@ -0,0 +1,58 @@ +import os +import unittest +import json +from typing import Dict +from jc.parsers.certbot import parse + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + f_in: Dict = {} + f_json: Dict = {} + + @classmethod + def setUpClass(cls): + fixtures = { + 'account': ( + 'fixtures/generic/certbot-account.out', + 'fixtures/generic/certbot-account.json'), + 'certificates': ( + 'fixtures/generic/certbot-certs.out', + 'fixtures/generic/certbot-certs.json') + } + + for file, filepaths in fixtures.items(): + with open(os.path.join(THIS_DIR, filepaths[0]), 'r', encoding='utf-8') as a, \ + open(os.path.join(THIS_DIR, filepaths[1]), 'r', encoding='utf-8') as b: + cls.f_in[file] = a.read() + cls.f_json[file] = json.loads(b.read()) + + + def test_certbot_nodata(self): + """ + Test 'certbot' with no data + """ + self.assertEqual(parse('', quiet=True), {}) + + + def test_certbot_certificates(self): + """ + Test 'certbot certificates' + """ + self.assertEqual( + parse(self.f_in['certificates'], quiet=True), + self.f_json['certificates'] + ) + + def test_certbot_account(self): + """ + Test 'certbot account' + """ + self.assertEqual( + parse(self.f_in['account'], quiet=True), + self.f_json['account'] + ) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_jc_utils.py b/tests/test_jc_utils.py index bfd051e7..bf72d2b2 100644 --- a/tests/test_jc_utils.py +++ b/tests/test_jc_utils.py @@ -48,6 +48,8 @@ class MyTests(unittest.TestCase): '2000/01/01-01:00:00.000000': {'string': '2000/01/01-01:00:00.000000', 'format': 1750, 'naive': 946717200, 'utc': None}, # Google Big Table format with timezone: '2000/01/01-01:00:00.000000+00:00': {'string': '2000/01/01-01:00:00.000000+00:00', 'format': 1755, 'naive': 946717200, 'utc': 946688400}, + # certbot format with timezone: + '2023-05-11 01:33:10+00:00': {'string': '2023-05-11 01:33:10+00:00', 'format': 1760, 'naive': 1683793990, 'utc': 1683768790}, # Common Log Format '10/Oct/2000:13:55:36 -0700': {'string': '10/Oct/2000:13:55:36 -0700', 'format': 1800, 'naive': 971211336, 'utc': None}, '10/Oct/2000:13:55:36 -0000': {'string': '10/Oct/2000:13:55:36 -0000', 'format': 1800, 'naive': 971211336, 'utc': 971186136},