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
|