From 2ecff9e0d5024e035b45d91bfe872c0d2b4f97ce Mon Sep 17 00:00:00 2001 From: Anon Date: Fri, 21 Oct 2022 14:16:02 -0700 Subject: [PATCH] Refactored to include a CloudflareAPI class --- src/main.py | 194 +++++++++++++++++++++++++++------------------------- 1 file changed, 100 insertions(+), 94 deletions(-) diff --git a/src/main.py b/src/main.py index 4df11fc..e6f468a 100755 --- a/src/main.py +++ b/src/main.py @@ -24,89 +24,103 @@ import argparse import configparser import contextlib import logging -from collections import OrderedDict +class CloudflairAPI: + domain_name = None + cf_api_key = None + cf_zone_id = None + cf_record_id = None -def get_cf_dns_domain(cf_zone_id): - return "https://api.cloudflare.com/client/v4/zones/{}/dns_records".format( - cf_zone_id - ) + def __init__(self, domain_name, cf_api_key, cf_zone_id, cf_record_id): + self.domain_name = domain_name + self.cf_api_key = cf_api_key + self.cf_zone_id = cf_zone_id + self.cf_record_id = cf_record_id + + def get_missing_config_var(self): + keys = ("domain_name", "cf_api_key", "cf_zone_id", "cf_record_id") + missing_parameter = next( + (key for key in keys if getattr(self, key) is None), + None + ) + return missing_parameter + -def get_cf_dns_domain_record_id(cf_zone_id, cf_record_id): - return "/".join([ - get_cf_dns_domain(cf_zone_id), - cf_record_id - ]) + def get_cf_dns_domain(self): + return "https://api.cloudflare.com/client/v4/zones/{}/dns_records".format( + self.cf_zone_id + ) + + + def get_cf_dns_domain_record_id(self): + return "{}/{}".format( + self.get_cf_dns_domain(), + self.cf_record_id + ) + + # !! Do not use this !! The global Cloudflair API is dangerous. Keeping this here for posterity and info + # def get_headers_global_api(cf_api_key, cf_email): + # return { + # "X-Auth-Key": cf_api_key, + # "X-Auth-Email": cf_email + # } + + + def get_headers(self): + return { + "Authorization": "Bearer {}".format(self.cf_api_key) + } + + + def get_record_id(self): + resp = requests.get( + url=self.get_cf_dns_domain(), + headers=self.get_headers() + ) + failed = resp.status_code != 200 + if not failed: + print(json.dumps(resp.json(), indent=4, sort_keys=True)) + return failed -# !! Do not use this !! The global Cloudflair API is dangerous. Keeping this here for posterity and info -# def get_headers_global_api(cf_api_key, cf_email): -# return { -# "X-Auth-Key": cf_api_key, -# "X-Auth-Email": cf_email -# } - - -def get_headers(cf_api_key): - return { - "Authorization": "Bearer {}".format(cf_api_key) - } - - -def get_record_id(cf_zone_id, cf_api_key): - resp = requests.get( - url=get_cf_dns_domain(cf_zone_id), - headers=get_headers(cf_api_key) - ) - failed = resp.status_code != 200 - if not failed: - print(json.dumps(resp.json(), indent=4, sort_keys=True)) - return failed - - -# A hack to prevent pycharm from bitching -def dictionary_split(s, delim='='): - k, v = s.split(delim, 1) - return k, v + + def get_old_ip(self): + resp = requests.get( + url=self.get_cf_dns_domain_record_id(), + headers=self.get_headers() + ) + if resp.status_code == 200: + return resp.json()["result"]["content"] + return None + + + def put_new_ip(self, ip): + json_payload = { + "type": 'A', + "name": self.domain_name, + "content": ip, + "proxied": True + } + + resp = requests.put( + url=self.get_cf_dns_domain_record_id(), + json=json_payload, + headers=self.get_headers() + ) + success = resp.status_code == 200 + return success # Get the current ip address of the user def get_new_ip(): with contextlib.suppress(Exception): a = requests.get("https://1.1.1.1/cdn-cgi/trace").text.split("\n") - ip = dict(dictionary_split(item) for item in a if item != "")["ip"] + ip = dict(item.split('=', 1) for item in filter(None, a))["ip"] return ip return None -def get_old_ip(cf_zone_id, cf_record_id, cf_api_key): - resp = requests.get( - url=get_cf_dns_domain_record_id(cf_zone_id, cf_record_id), - headers=get_headers(cf_api_key) - ) - if resp.status_code == 200: - return resp.json()["result"]["content"] - return None - - -def put_new_ip(domain_name, ip, cf_zone_id, cf_record_id, cf_api_key): - json_payload = { - "type": 'A', - "name": domain_name, - "content": ip, - "proxied": True - } - - resp = requests.put( - url=get_cf_dns_domain_record_id(cf_zone_id, cf_record_id), - json=json_payload, - headers=get_headers(cf_api_key) - ) - success = resp.status_code == 200 - return success - - def config_parser_has_option(config, profile, option): # 'DEFAULT' is a special value in ConfigParser config_name = "" if profile == "DEFAULT" else profile @@ -162,36 +176,28 @@ def main(): config = configparser.ConfigParser() config.read(arguments.config) - options = OrderedDict([ - ("domain_name", config_parser_has_option(config, arguments.profile, "domain_name")), - ("cf_api_key", config_parser_has_option(config, arguments.profile, "cf_api_key")), - ("cf_zone_id", config_parser_has_option(config, arguments.profile, "cf_zone_id")), - ("cf_record_id", config_parser_has_option(config, arguments.profile, "cf_record_id")) - ]) + cloudflair_api = CloudflairAPI( + domain_name = config_parser_has_option(config, arguments.profile, "domain_name"), + cf_api_key = config_parser_has_option(config, arguments.profile, "cf_api_key"), + cf_zone_id = config_parser_has_option(config, arguments.profile, "cf_zone_id"), + cf_record_id = config_parser_has_option(config, arguments.profile, "cf_record_id") + ) - # Validate configuration file - missing_parameter = next((i for i in options if options[i] is None and i != "cf_record_id"), None) - if missing_parameter is not None: - logging.error("Configuration: {} | Profile: {} | Option: {}".format( + missing_parameter = cloudflair_api.get_missing_config_var() + + # If cf_zone_id is None, print out all record ids. The user will have to update this setting in their config file + # This will have to be done once + if missing_parameter == "cf_record_id" or arguments.get_record_ids: + return int(cloudflair_api.get_record_id()) + elif missing_parameter is not None: + logging.error("Missing Configuration - Configuration: {} | Profile: {} | Option: {}".format( arguments.config, arguments.profile, missing_parameter )) return 1 - # If cf_zone_id is None, print out all record ids. The user will have to update this setting in their config file - # This will have to be done at least once - if arguments.get_record_ids or options["cf_record_id"] is None: - return int(get_record_id(options["cf_zone_id"], options["cf_api_key"])) - # Get the current dns record from cloudflair (old) - old_ip = get_old_ip(options["cf_zone_id"], options["cf_record_id"], options["cf_api_key"]) - - # Get the current ip address of the server (new) - new_ip = None - - if arguments.set: - new_ip = arguments.set - else: - new_ip = get_new_ip() + old_ip = cloudflair_api.get_old_ip() + new_ip = arguments.set or get_new_ip() if new_ip is None: logging.error("Could not determine new IP") @@ -207,10 +213,10 @@ def main(): elif old_ip == new_ip: logging.info("New IP matches old IP: {}".format(new_ip)) else: - if put_new_ip(options["domain_name"], new_ip, options["cf_zone_id"], options["cf_record_id"], options["cf_api_key"]): - logging.info("IP updated to: {} from: {}".format(old_ip, new_ip)) + if cloudflair_api.put_new_ip(new_ip): + logging.info("IP updated to: {} from: {}".format(new_ip, old_ip)) else: - logging.error("Failed to update to: {} from: {}".format(old_ip, new_ip)) + logging.error("Failed to update to: {} from: {}".format(new_ip, old_ip)) return 1 return 0