199 lines
7.4 KiB
Python
199 lines
7.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from six.moves.urllib.parse import quote, unquote, urlparse, urlencode
|
|
import hmac
|
|
import time
|
|
import hashlib
|
|
import logging
|
|
from requests.auth import AuthBase
|
|
from .cos_comm import to_unicode, to_bytes, to_str
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def filter_headers(data):
|
|
"""只设置host content-type 还有x开头的头部.
|
|
|
|
:param data(dict): 所有的头部信息.
|
|
:return(dict): 计算进签名的头部.
|
|
"""
|
|
valid_headers = [
|
|
"cache-control",
|
|
"content-disposition",
|
|
"content-encoding",
|
|
"content-type",
|
|
"content-md5",
|
|
"content-length",
|
|
"expect",
|
|
"expires",
|
|
"host",
|
|
"if-match",
|
|
"if-modified-since",
|
|
"if-none-match",
|
|
"if-unmodified-since",
|
|
"origin",
|
|
"range",
|
|
"transfer-encoding",
|
|
"pic-operations",
|
|
]
|
|
headers = {}
|
|
for i in data:
|
|
if str.lower(i) in valid_headers or str.lower(i).startswith("x-cos-") or str.lower(i).startswith("x-ci-"):
|
|
headers[i] = data[i]
|
|
return headers
|
|
|
|
|
|
class CosS3Auth(AuthBase):
|
|
|
|
def __init__(self, conf, key=None, params={}, expire=10000, sign_host=None):
|
|
self._secret_id = conf._secret_id if conf._secret_id else \
|
|
(conf._credential_inst.secret_id if conf._credential_inst else None)
|
|
self._secret_key = conf._secret_key if conf._secret_key else \
|
|
(conf._credential_inst.secret_key if conf._credential_inst else None)
|
|
self._anonymous = conf._anonymous
|
|
self._expire = expire
|
|
self._params = params
|
|
self._sign_params = conf._sign_params
|
|
|
|
# 如果API指定了是否签名host,则以具体API为准,如果未指定则以配置为准
|
|
if sign_host is not None:
|
|
self._sign_host = bool(sign_host)
|
|
else:
|
|
self._sign_host = conf._sign_host
|
|
|
|
if key:
|
|
key = to_unicode(key)
|
|
if key[0] == u'/':
|
|
self._path = key
|
|
else:
|
|
self._path = u'/' + key
|
|
else:
|
|
self._path = u'/'
|
|
|
|
def __call__(self, r):
|
|
|
|
# 匿名请求直接返回
|
|
if self._anonymous:
|
|
r.headers['Authorization'] = ""
|
|
logger.debug("anonymous reqeust")
|
|
return r
|
|
|
|
path = self._path
|
|
uri_params = {}
|
|
if self._sign_params:
|
|
uri_params = self._params
|
|
headers = filter_headers(r.headers)
|
|
|
|
# 如果headers中不包含host头域,则从url中提取host,并且加入签名计算
|
|
if self._sign_host:
|
|
|
|
# 判断headers中是否包含host头域
|
|
contain_host = False
|
|
for i in headers:
|
|
if str.lower(i) == "host": # 兼容host/Host/HOST等
|
|
contain_host = True
|
|
break
|
|
|
|
# 从url中提取host
|
|
if not contain_host:
|
|
url_parsed = urlparse(r.url)
|
|
if url_parsed.hostname is not None:
|
|
headers["host"] = url_parsed.hostname
|
|
|
|
# reserved keywords in headers urlencode are -_.~, notice that / should be encoded and space should not be encoded to plus sign(+)
|
|
headers = dict([(quote(to_bytes(to_str(k)), '-_.~').lower(), quote(to_bytes(to_str(v)), '-_.~')) for k, v in
|
|
headers.items()]) # headers中的key转换为小写,value进行encode
|
|
uri_params = dict([(quote(to_bytes(to_str(k)), '-_.~').lower(), quote(to_bytes(to_str(v)), '-_.~')) for k, v in
|
|
uri_params.items()])
|
|
format_str = u"{method}\n{host}\n{params}\n{headers}\n".format(
|
|
method=r.method.lower(),
|
|
host=path,
|
|
params='&'.join(map(lambda tupl: "%s=%s" %
|
|
(tupl[0], tupl[1]), sorted(uri_params.items()))),
|
|
headers='&'.join(map(lambda tupl: "%s=%s" %
|
|
(tupl[0], tupl[1]), sorted(headers.items())))
|
|
)
|
|
logger.debug("format str: " + format_str)
|
|
|
|
start_sign_time = int(time.time())
|
|
sign_time = "{bg_time};{ed_time}".format(
|
|
bg_time=start_sign_time - 60, ed_time=start_sign_time + self._expire)
|
|
sha1 = hashlib.sha1()
|
|
sha1.update(to_bytes(format_str))
|
|
|
|
str_to_sign = "sha1\n{time}\n{sha1}\n".format(
|
|
time=sign_time, sha1=sha1.hexdigest())
|
|
logger.debug('str_to_sign: ' + str(str_to_sign))
|
|
sign_key = hmac.new(to_bytes(self._secret_key), to_bytes(
|
|
sign_time), hashlib.sha1).hexdigest()
|
|
sign = hmac.new(to_bytes(sign_key), to_bytes(
|
|
str_to_sign), hashlib.sha1).hexdigest()
|
|
logger.debug('sign_key: ' + str(sign_key))
|
|
logger.debug('sign: ' + str(sign))
|
|
sign_tpl = "q-sign-algorithm=sha1&q-ak={ak}&q-sign-time={sign_time}&q-key-time={key_time}&q-header-list={headers}&q-url-param-list={params}&q-signature={sign}"
|
|
|
|
r.headers['Authorization'] = sign_tpl.format(
|
|
ak=self._secret_id,
|
|
sign_time=sign_time,
|
|
key_time=sign_time,
|
|
params=';'.join(sorted(uri_params.keys())),
|
|
headers=';'.join(sorted(headers.keys())),
|
|
sign=sign
|
|
)
|
|
logger.debug("sign_key" + str(sign_key))
|
|
logger.debug(r.headers['Authorization'])
|
|
logger.debug("request headers: " + str(r.headers))
|
|
return r
|
|
|
|
|
|
class CosRtmpAuth(AuthBase):
|
|
|
|
def __init__(self, conf, bucket=None, channel=None, params={}, expire=3600, presign_expire=0):
|
|
self._secret_id = conf._secret_id
|
|
self._secret_key = conf._secret_key
|
|
self._token = conf._token
|
|
self._anonymous = conf._anonymous
|
|
self._expire = expire
|
|
self._presign_expire = presign_expire
|
|
self._params = params
|
|
if self._token:
|
|
self._params['q-token'] = self._token
|
|
self._path = u'/' + bucket + u'/' + channel
|
|
|
|
def get_rtmp_sign(self):
|
|
# get rtmp string
|
|
canonicalized_param = ''
|
|
for k, v in self._params.items():
|
|
canonicalized_param += '{key}={value}&'.format(key=k, value=v)
|
|
if self._presign_expire >= 60:
|
|
canonicalized_param += 'presign={value}'.format(
|
|
value=self._presign_expire)
|
|
canonicalized_param = canonicalized_param.rstrip('&')
|
|
rtmp_str = u"{path}\n{params}\n".format(
|
|
path=self._path, params=canonicalized_param)
|
|
logger.debug("rtmp str: " + rtmp_str)
|
|
|
|
sha1 = hashlib.sha1()
|
|
sha1.update(to_bytes(rtmp_str))
|
|
# get time
|
|
sign_time = int(time.time())
|
|
sign_time_str = "{start_time};{end_time}".format(
|
|
start_time=sign_time - 60, end_time=sign_time + self._expire)
|
|
str_to_sign = "sha1\n{time}\n{sha1}\n".format(
|
|
time=sign_time_str, sha1=sha1.hexdigest())
|
|
logger.debug('str_to_sign: ' + str(str_to_sign))
|
|
# get sinature
|
|
signature = hmac.new(to_bytes(self._secret_key), to_bytes(
|
|
str_to_sign), hashlib.sha1).hexdigest()
|
|
logger.debug('signature: ' + str(signature))
|
|
rtmp_sign = "q-sign-algorithm=sha1&q-ak={ak}&q-sign-time={sign_time}&q-key-time={key_time}&q-signature={sign}".format(
|
|
ak=self._secret_id, sign_time=sign_time_str, key_time=sign_time_str, sign=signature)
|
|
if canonicalized_param != '':
|
|
return rtmp_sign + "&{params}".format(params=canonicalized_param)
|
|
else:
|
|
return rtmp_sign
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pass
|