def is_allowed_cname(zone_id, type_id, owner, rdata, ttl_id): """Check is given CNAME record is allowed. 1. Check for duplicate record 2. Check for the same owner 3. Check for the same A owner """ # duplicate record NOT allowed is_allowed(zone_id, type_id, owner, rdata, ttl_id) # 1. same owner NOT allowed query = '"type_id"=%(type_id)s AND "owner"=%(owner)s' value = {"zone_id": zone_id, "type_id": type_id, "owner": owner} rules = rules_model.Rules(query, value) is_unique = rules.is_unique() if not is_unique: raise ValueError("A CNAME record already exist with that owner") # 2. owner CAN'T coexist with the same A owner a_type_id = type_model.get_typeid_by_rtype("A") query = '"type_id" IN (%(type1)s,%(type2)s) AND "owner"=%(owner)s' value = {"zone_id": zone_id, "type1": type_id, "type2": a_type_id, "owner": owner} rules = rules_model.Rules(query, value) is_coexist = rules.is_coexist() if is_coexist: raise ValueError("An A record already exist with that owner")
def is_allowed_cname_edit(zone_id, type_id, owner, rdata, ttl_id, record_id=None): """Check is given CNAME record is allowed. This function separated from `cname_add` because it needs to exclude its id while searching for other records. """ # duplicate record NOT allowed is_allowed(zone_id, type_id, owner, rdata, ttl_id) # 1. same owner NOT allowed query = '"type_id"=%(type_id)s AND "owner"=%(owner)s AND "id"<>%(record_id)s' value = { "zone_id": zone_id, "type_id": type_id, "owner": owner, "record_id": record_id, } rules = rules_model.Rules(query, value) is_unique = rules.is_unique() if not is_unique: raise ValueError("A CNAME record already exist with that owner") # 2. owner CAN'T coexist with the same A owner a_type_id = type_model.get_typeid_by_rtype("A") query = '"type_id" IN (%(type1)s,%(type2)s) AND "owner"=%(owner)s AND "id"<>%(record_id)s' value = { "zone_id": zone_id, "type1": type_id, "type2": a_type_id, "owner": owner, "record_id": record_id, } rules = rules_model.Rules(query, value) is_coexist = rules.is_coexist() if is_coexist: raise ValueError("An A record already exist with that owner") # 3. owner can't be root zone = zone_model.get_zone(zone_id) if owner == f"{zone}." or owner == "@": raise ValueError("A CNAME owner can't be root")
def is_allowed_a(zone_id, type_id, owner, rdata, ttl_id, record_id=None): """Check is given A record is allowed. 1. Check for duplicate record 2. Check for the same CNAME owner """ # duplicate record NOT allowed is_allowed(zone_id, type_id, owner, rdata, ttl_id) # 2. owner CAN'T coexist with the same CNAME owner cname_type_id = type_model.get_typeid_by_rtype("CNAME") query = '"type_id"=%(type_id)s AND "owner"=%(owner)s' value = {"zone_id": zone_id, "type_id": cname_type_id, "owner": owner} rules = rules_model.Rules(query, value) is_coexist = rules.is_coexist() if is_coexist: raise ValueError("A CNAME record already exist with that owner")
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)
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)