Files
yakpanel-core/class/sslModel/huaweicloudModel.py

339 lines
12 KiB
Python
Raw Normal View History

2026-04-07 02:04:22 +05:30
import json
from sslModel.base import sslBase
import public
import copy
import sys
import hashlib
import hmac
import binascii
from datetime import datetime
import requests
class main(sslBase):
dns_provider_name = "huaweicloud"
_type = 0
def __init__(self):
super().__init__()
self.region = "cn-south-1"
self.BasicDateFormat = "%Y%m%dT%H%M%SZ"
self.Algorithm = "SDK-HMAC-SHA256"
self.HeaderXDate = "X-Sdk-Date"
self.HeaderHost = "host"
self.HeaderAuthorization = "Authorization"
self.HeaderContentSha256 = "x-sdk-content-sha256"
self.url = "https://dns.cn-north-1.myhuaweicloud.com"
def __init_data(self, data):
self.ak = data["AccessKey"]
self.sk = data["SecretKey"]
self.project_id = data["project_id"]
def sign_to_response(self, dns_id, method="", url="", headers=None, body=""):
url = self.url + url
self.__init_data(self.get_dns_data(None)[dns_id])
if sys.version_info.major < 3:
body = body
from urllib import quote, unquote
def hmacsha256(keyByte, message):
return hmac.new(keyByte, message, digestmod=hashlib.sha256).digest()
# Create a "String to Sign".
def StringToSign(canonicalRequest, t):
bytes = HexEncodeSHA256Hash(canonicalRequest)
return "%s\n%s\n%s" % (self.Algorithm, datetime.strftime(t, self.BasicDateFormat), bytes)
else:
body = body.encode("utf-8")
from urllib.parse import quote, unquote
def hmacsha256(keyByte, message):
return hmac.new(keyByte.encode('utf-8'), message.encode('utf-8'), digestmod=hashlib.sha256).digest()
# Create a "String to Sign".
def StringToSign(canonicalRequest, t):
bytes = HexEncodeSHA256Hash(canonicalRequest.encode('utf-8'))
return "%s\n%s\n%s" % (self.Algorithm, datetime.strftime(t, self.BasicDateFormat), bytes)
def HexEncodeSHA256Hash(data):
sha256 = hashlib.sha256()
sha256.update(data)
return sha256.hexdigest()
def findHeader(headers, header):
for k in headers:
if k.lower() == header.lower():
return headers[k]
return None
def CanonicalQueryString(query):
keys = []
for key in query:
keys.append(key)
keys.sort()
a = []
for key in keys:
k = quote(key, safe='~')
value = query[key]
if type(value) is list:
value.sort()
for v in value:
kv = k + "=" + quote(str(v), safe='~')
a.append(kv)
else:
kv = k + "=" + quote(str(value), safe='~')
a.append(kv)
return '&'.join(a)
def SignStringToSign(stringToSign, signingKey):
hm = hmacsha256(signingKey, stringToSign)
return binascii.hexlify(hm).decode()
def AuthHeaderValue(signature, AppKey, signedHeaders):
return "%s Access=%s, SignedHeaders=%s, Signature=%s" % (
self.Algorithm, AppKey, ";".join(signedHeaders), signature)
spl = url.split("://", 1)
scheme = 'http'
if len(spl) > 1:
scheme = spl[0]
url = spl[1]
query = {}
spl = url.split('?', 1)
url = spl[0]
if len(spl) > 1:
for kv in spl[1].split("&"):
spl = kv.split("=", 1)
key = spl[0]
value = ""
if len(spl) > 1:
value = spl[1]
if key != '':
key = unquote(key)
value = unquote(value)
if key in query:
query[key].append(value)
else:
query[key] = [value]
spl = url.split('/', 1)
host = spl[0]
if len(spl) > 1:
url = '/' + spl[1]
else:
url = '/'
if headers is None:
headers = {"content-type": "application/json"}
else:
headers = copy.deepcopy(headers)
headerTime = findHeader(headers, self.HeaderXDate)
if headerTime is None:
t = datetime.utcnow()
headers[self.HeaderXDate] = datetime.strftime(t, self.BasicDateFormat)
else:
t = datetime.strptime(headerTime, self.BasicDateFormat)
haveHost = False
for key in headers:
if key.lower() == 'host':
haveHost = True
break
if not haveHost:
headers["host"] = host
signedHeaders = []
for key in headers:
signedHeaders.append(key.lower())
signedHeaders.sort()
a = []
__headers = {}
for key in headers:
keyEncoded = key.lower()
value = headers[key]
valueEncoded = value.strip()
__headers[keyEncoded] = valueEncoded
if sys.version_info.major == 3:
headers[key] = valueEncoded.encode("utf-8").decode('iso-8859-1')
for key in signedHeaders:
a.append(key + ":" + __headers[key])
canonicalHeaders = '\n'.join(a) + "\n"
hexencode = findHeader(headers, self.HeaderContentSha256)
if hexencode is None:
hexencode = HexEncodeSHA256Hash(body)
pattens = unquote(url).split('/')
CanonicalURI = []
for v in pattens:
CanonicalURI.append(quote(v, safe="~"))
CanonicalURL = "/".join(CanonicalURI)
if CanonicalURL[-1] != '/':
CanonicalURL = CanonicalURL + "/" # always end with /
canonicalRequest = "%s\n%s\n%s\n%s\n%s\n%s" % (method.upper(), CanonicalURL, CanonicalQueryString(query),
canonicalHeaders, ";".join(signedHeaders), hexencode)
stringToSign = StringToSign(canonicalRequest, t)
signature = SignStringToSign(stringToSign, self.sk)
authValue = AuthHeaderValue(signature, self.ak, signedHeaders)
headers[self.HeaderAuthorization] = authValue
headers["content-length"] = str(len(body))
queryString = CanonicalQueryString(query)
if queryString != "":
url = url + "?" + queryString
res = requests.request(method, scheme + "://" + host + url, headers=headers, data=body)
return res
def create_dns_record(self, get):
domain_name = get.domain_name
domain_dns_value = get.domain_dns_value
record_type = 'TXT'
if 'record_type' in get:
record_type = get.record_type
if record_type == 'TXT':
domain_dns_value = "\"{}\"".format(domain_dns_value)
root_domain, sub_domain, _ = self.extract_zone(domain_name)
if sub_domain == "@":
domain_name = root_domain
try:
zone_dic = self.get_zoneid_dict(get.dns_id)
zone_id = zone_dic.get(root_domain)
body = json.dumps({
"name": domain_name,
"type": record_type,
"records": [domain_dns_value],
})
res = self.sign_to_response(get.dns_id, "POST", "/v2/zones/{}/recordsets".format(zone_id), body=body)
if res.status_code != 202:
return public.returnMsg(False, self.get_error(res.text))
return public.returnMsg(True, '添加成功')
except Exception as e:
return public.returnMsg(False, self.get_error(str(e)))
def delete_dns_record(self, get):
domain_name = get.domain_name
RecordId = get.RecordId
root_domain, _, sub_domain = self.extract_zone(domain_name)
zone_dic = self.get_zoneid_dict(get.dns_id)
zone_id = zone_dic[root_domain]
try:
res = self.sign_to_response(get.dns_id, "DELETE", "/v2/zones/{}/recordsets/{}".format(zone_id, RecordId))
if res.status_code != 202:
return public.returnMsg(False, self.get_error(res.text))
return public.returnMsg(True, '删除成功')
except Exception as e:
return public.returnMsg(False, self.get_error(str(e)))
def get_zoneid_dict(self, dns_id):
"""
获取所有域名对应id
"""
res = self.sign_to_response(dns_id, "GET", "/v2/zones")
if res.status_code != 200:
return {}
response = res.json()
data = {i["name"][:-1]: i["id"] for i in response["zones"]}
return data
def get_dns_record(self, get):
domain_name = get.domain_name
root_domain, _, sub_domain = self.extract_zone(domain_name)
data = {}
try:
zone_dic = self.get_zoneid_dict(get.dns_id)
limit = 100
offset = 0
zone_id = zone_dic[root_domain]
res = self.sign_to_response(get.dns_id, "GET",
"/v2/zones/{}/recordsets?limit={}&offset={}".format(zone_id, limit, offset))
if res.status_code != 200:
return {}
response = res.json()
data["list"] = [
{
"RecordId": i["id"],
"name": i["name"][:-1],
"value": '\r\n'.join(i["records"]) if i["type"] != "TXT" else '\r\n'.join([j.replace('"', '') for j in i["records"]]),
"line": "默认",
"ttl": i["ttl"],
"type": i["type"],
"status": "启用"if i["status"] == "ACTIVE" else "暂停" if i["status"] == "DISABLE" else i["status"],
"mx": "",
"updated_on": i["update_at"],
"remark": i.get("description") or "",
}
for i in response["recordsets"]
]
data["info"] = {
'record_total': response['metadata']['total_count']
}
except Exception as e:
pass
self.set_record_data({root_domain: data})
return data
def update_dns_record(self, get):
domain_name = get.domain_name
RecordId = get.RecordId
record_type = get.record_type
domain_dns_value = get.domain_dns_value
root_domain, _, sub_domain = self.extract_zone(domain_name)
if sub_domain == "@":
domain_name = root_domain
if record_type == 'TXT':
domain_dns_value = "\"{}\"".format(domain_dns_value)
zone_dic = self.get_zoneid_dict(get.dns_id)
zone_id = zone_dic[root_domain]
try:
body = json.dumps({
"name": domain_name,
"type": record_type,
"records": [domain_dns_value],
})
res = self.sign_to_response(get.dns_id, "PUT", "/v2/zones/{}/recordsets/{}".format(zone_id, RecordId), body=body)
if res.status_code != 202:
return public.returnMsg(False, self.get_error(res.text))
return public.returnMsg(True, '修改成功')
except Exception as e:
return public.returnMsg(False, self.get_error(str(e)))
def get_error(self, error):
if "DNS.0317" in error:
return "此记录为系统默认域名记录值,不能删除。"
elif "DNS.0318" in error:
return "此记录为系统默认域名记录值,不能更新。"
elif "DNS.0321" in error:
return "子域名级别超过限制。"
elif "DNS.0324" in error:
return "系统默认解析记录不能操作。"
elif "DNS.0308" in error:
return "记录集类型非法。"
elif "DNS.0307" in error:
return "记录集的值非法。"
elif "DNS.0302" in error:
return "这个华为云账户下面不存在这个域名请检查dns接口配置后重试"
elif "APIGW.0301" in error:
return "dns账号信息有误"
elif "DNS.0312" in error:
return "存在同名的主机记录"
elif "DNS.0313" in error:
return "不存在这条记录"
else:
return error