def get_other_data(record): try: rdata = model.get_one(table="rdata", field="record_id", value=record["id"]) zone = model.get_one(table="zone", field="id", value=record["zone_id"]) ttl = model.get_one(table="ttl", field="id", value=record["ttl_id"]) type_ = model.get_one(table="type", field="id", value=record["type_id"]) if rdata: rdata = helpers.exclude_keys(rdata, {"id", "record_id"}) rdata = rdata.get("rdata") zone = helpers.exclude_keys( zone, {"id", "is_committed", "user_id", "record_id"}) data = { "id": record["id"], "owner": record["owner"], "rdata": rdata, "zone": zone["zone"], "type": type_["type"], "ttl": ttl["ttl"], } return data except Exception as e: current_app.logger.error(f"{e}")
def get_type_by_recordid(record_id): """Get record type by record id.""" try: record = model.get_one(table="record", field="id", value=record_id) type_id = record["type_id"] type_ = model.get_one(table="type", field="id", value=type_id) return type_["type"] except Exception: raise ValueError("Unrecognized Record Type")
def get_zone_id(zone): zone = model.get_one(table="zone", field="zone", value=f"{zone}") if not zone: raise ValueError(f"Zone Not Found") zone_id = zone["id"] return zone_id
def get_ttlid_by_ttl(ttl): """Get type id by record record type.""" ttl_ = model.get_one(table="ttl", field="ttl", value=ttl) if not ttl_: raise ValueError("TTL Not Found") ttl_id = ttl_["id"] return ttl_id
def get_typeid_by_rtype(rtype): """Get type id by record record type.""" type_ = model.get_one(table="type", field="type", value=rtype.upper()) if not type_: raise ValueError("Type Not Found") type_id = type_["id"] return type_id
def get_zone(zone_id): """Get zone name by ID""" zone = model.get_one(table="zone", field="id", value=f"{zone_id}") if not zone: raise ValueError("Zone Not Found") zone = zone["zone"] return zone
def get_other_data(record_id): """Return other record data from given record id.""" try: record = model.get_one(table="record", field="id", value=record_id) zone_id = record["zone_id"] type_id = record["type_id"] ttl_id = record["ttl_id"] zone = model.get_one(table="zone", field="id", value=zone_id) type_ = model.get_one(table="type", field="id", value=type_id) ttl = model.get_one(table="ttl", field="id", value=ttl_id) rdata = model.get_one(table="rdata", field="record_id", value=record_id) return (record, zone, type_, ttl, rdata) except Exception as error: raise ValueError(f"{error}")
def get(self): user_id = request.args.get("id") email = request.args.get("email") try: if not any((user_id, email)): return response(422, "Problems parsing parameters") if user_id: user = model.get_one(table="user", field="id", value=user_id) if email: user = model.get_one(table="user", field="email", value=email) if not user: return response(404) return response(200, data=user) except Exception as e: current_app.logger.error(f"{e}") return response(500)
def get(self, ttl_id): try: ttl = model.get_one(table="ttl", field="id", value=ttl_id) if not ttl: return response(404) return response(200, data=ttl) except Exception as e: return response(500, message=f"{e}")
def get(self, type_id): try: type_ = model.get_one(table="type", field="id", value=type_id) if not type_: return response(404) return response(200, data=type_) except Exception: return response(500)
def get(self, record_id): try: record = model.get_one(table="record", field="id", value=record_id) if not record: return response(404) data = record_model.get_other_data(record) return response(200, data=data) except Exception as e: current_app.logger.error(f"{e}") return response(500)
def get(self): zone_id = request.args.get("id") zone_name = request.args.get("name") if not any((zone_id, zone_name)): return response(422, "Problems parsing parameters") try: if zone_id: zone = model.get_one(table="zone", field="id", value=zone_id) if zone_name: zone = model.get_one(table="zone", field="zone", value=zone_name) if not zone: return response(404) data = domain_model.get_other_data(zone) return response(200, data=data) except Exception as e: current_app.logger.error(f"{e}") return response(500)
def post(self): """Add new domain (zone) with additional default record. note: SOA, NS, and CNAME records are added automatically when adding new domain """ parser = reqparse.RequestParser() parser.add_argument("zone", type=str, required=True) parser.add_argument("user_id", type=int, required=True) args = parser.parse_args() zone = args["zone"] user_id = args["user_id"] # Validation if not model.is_unique(table="zone", field="zone", value=f"{zone}"): return response(409, message="Duplicate Zone") user = model.get_one(table="user", field="id", value=user_id) if not user: return response(404, message=f"User Not Found") try: validator.validate("ZONE", zone) except Exception as e: return response(422, message=f"{e}") try: zone_id = insert_zone(zone, user_id) # create zone config command.set_config(zone, zone_id, "conf-set") # create default records soa_record_id = insert_soa_default(zone_id) ns_record_ids = insert_ns_default(zone_id) cname_record_id = insert_cname_default(zone_id, zone) record_ids = [soa_record_id, *ns_record_ids, cname_record_id] command.set_default_zone(record_ids) command.delegate(zone, zone_id, "conf-set", "master") command.delegate(zone, zone_id, "conf-set", "slave") data_ = {"id": zone_id, "zone": zone} return response(201, data=data_) except Exception as e: current_app.logger.error(f"{e}") return response(500)
def get_serial_resource(zone): soa_record = record_model.get_soa_record(zone) rdata_record = model.get_one( table="rdata", field="record_id", value=soa_record["id"] ) rdatas = rdata_record["rdata"].split(" ") serial = rdatas[2] # `serial_counter` is the last two digit of serial value (YYYYMMDDnn) serial_counter = serial[-2:] serial_date = serial[:-2] return { "soa_record": soa_record, "rdata_record": rdata_record, "serial": serial, "serial_counter": serial_counter, "serial_date": serial_date, }
def is_duplicate(self, zone_id, type_id, owner, rdata, ttl_id): """Check duplicate record exists.""" base_query = 'SELECT * FROM "record" WHERE "zone_id"=%(zone_id)s AND' query = ( base_query + '"type_id"=%(type_id)s AND "owner"=%(owner)s AND "ttl_id"=%(ttl_id)s' ) value = { "zone_id": zone_id, "type_id": type_id, "owner": owner, "ttl_id": ttl_id, } records = model.plain_get("record", query, value) for record in records: rdata_record = model.get_one(table="rdata", field="record_id", value=record["id"]) if rdata == rdata_record["rdata"]: raise ValueError("The record already exists")
def get_other_data(zone): if zone is None: return user = model.get_one(table="user", field="id", value=zone["user_id"]) user = helpers.exclude_keys(user, {"created_at"}) records = record_model.get_records_by_zone(zone["zone"]) records_detail = [] for record in records: record_detail = record_model.get_other_data(record) record_detail = helpers.exclude_keys(record_detail, {"zone"}) records_detail.append(record_detail) data = { "zone_id": zone["id"], "zone": zone["zone"], "user": user, "records": records_detail, } return data
def put(self, record_id): parser = reqparse.RequestParser() parser.add_argument("zone", type=str, required=True) parser.add_argument("owner", type=str, required=True) parser.add_argument("rtype", type=str, required=True) parser.add_argument("rdata", type=str, required=True) parser.add_argument("ttl", type=str, required=True) args = parser.parse_args() owner = args["owner"].lower() rtype = args["rtype"].lower() rdata = args["rdata"] zone = args["zone"] ttl = args["ttl"] try: ttl_id = ttl_model.get_ttlid_by_ttl(ttl) record_model.is_exists(record_id) type_id = type_model.get_typeid_by_rtype(rtype) zone_id = zone_model.get_zone_id(zone) except Exception as e: return response(404, message=f"{e}") try: rules.check_edit(rtype, zone_id, type_id, owner, rdata, ttl_id, record_id) except Exception as e: return response(409, message=f"{e}") try: validator.validate(rtype, rdata) validator.validate("owner", owner) except Exception as e: return response(422, message=f"{e}") try: serial_resource = get_serial_resource(zone) check_serial_limit(serial_resource) except Exception as e: return response(429, message=f"{e}") try: data = { "where": {"id": record_id}, "data": { "owner": owner, "zone_id": zone_id, "type_id": type_id, "ttl_id": ttl_id, }, } content_data = { "where": {"record_id": record_id}, "data": {"rdata": rdata, "record_id": record_id}, } command.set_zone(record_id, "zone-unset") model.update("rdata", data=content_data) model.update("record", data=data) command.set_zone(record_id, "zone-set") # increment serial after adding new record rtype = type_model.get_type_by_recordid(record_id) if rtype != "SOA": update_serial(serial_resource, "02") record = model.get_one(table="record", field="id", value=record_id) data_ = record_model.get_other_data(record) return response(200, data=data_) except Exception as e: current_app.logger.error(f"{e}") return response(500)
def is_exists(type_id): type_ = model.get_one(table="type", field="id", value=type_id) if not type_: raise ValueError("Type Not Found")
def is_exists(record_id): record = model.get_one(table="record", field="id", value=record_id) if not record: raise ValueError("Record Not Found")
def is_exists(ttl_id): ttl_ = model.get_one(table="ttl", field="id", value=ttl_id) if not ttl_: raise ValueError("TTL Not Found")
def get_zone_by_record(record_id): record = model.get_one(table="record", field="id", value=f"{record_id}") zone_id = record["zone_id"] zone = model.get_one(table="zone", field="id", value=f"{zone_id}") return zone
def post(self): """Add new record. note: Adding any record with other record is allowed. IETF best practice is not handled automatically. Knot didn't handle this too, and let the user know the standards themselves. See https://tools.ietf.org/html/rfc1912 """ parser = reqparse.RequestParser() parser.add_argument("zone", type=str, required=True) parser.add_argument("owner", type=str, required=True) parser.add_argument("rtype", type=str, required=True) parser.add_argument("rdata", type=str, required=True) parser.add_argument("ttl", type=str, required=True) args = parser.parse_args() owner = args["owner"].lower() rtype = args["rtype"].lower() rdata = args["rdata"] zone = args["zone"] ttl = args["ttl"] try: ttl_id = ttl_model.get_ttlid_by_ttl(ttl) type_id = type_model.get_typeid_by_rtype(rtype) zone_id = zone_model.get_zone_id(zone) except Exception as e: return response(404, message=f"{e}") try: rules.check_add(rtype, zone_id, type_id, owner, rdata, ttl_id) except Exception as e: return response(409, message=f"{e}") try: # rtype no need to be validated & no need to check its length # `get_typeid` will raise error for non existing rtype validator.validate(rtype, rdata) validator.validate("owner", owner) except Exception as e: return response(422, message=f"{e}") try: serial_resource = get_serial_resource(zone) check_serial_limit(serial_resource) except Exception as e: return response(429, message=f"{e}") try: data = { "owner": owner, "zone_id": zone_id, "type_id": type_id, "ttl_id": ttl_id, } record_id = model.insert(table="record", data=data) content_data = {"rdata": rdata, "record_id": record_id} model.insert(table="rdata", data=content_data) command.set_zone(record_id, "zone-set") # increment serial after adding new record rtype = type_model.get_type_by_recordid(record_id) if rtype != "SOA": update_serial(serial_resource) record = model.get_one(table="record", field="id", value=record_id) data = record_model.get_other_data(record) return response(201, data=data) except Exception as e: current_app.logger.error(f"{e}") return response(500)