# encoding=utf8
from __future__ import unicode_literals
import sys
import logging
import pyotp
from mohand.hands import hand
if sys.version > '3':
PY3 = True
else:
PY3 = False
LOG_FORMAT = "[%(asctime)s][%(name)s:%(lineno)s][%(levelname)s] %(message)s"
logging.basicConfig(
level=logging.WARN,
format=LOG_FORMAT,
stream=sys.stdout,
)
log = logging.getLogger(__name__)
[文档]def otp(*dargs, **dkwargs):
"""
将被装饰函数封装为一个 :class:`click.core.Command` 类,成为 ``mohand`` 的子命令
该装饰器被作为一个包含定制其行为的含参数装饰器使用(如: ``@hand.otp(secret='xxoo')`` )
.. note::
该装饰器最终会通过插件系统被注册到 :data:`.hands.hand` 中。
此处的 ``otp`` 装饰器本身是应该不支持无参数装饰的,但考虑到其作为样例实现,
故将其实现为兼容两种传参的装饰器
:param int log_level: 当前子命令的日志输出等级,默认为: ``logging.INFO``
:param str secret: 用于构造基于时间的 OTP 的秘钥字串
:return: 被封装后的函数
:rtype: function
"""
invoked = bool(len(dargs) == 1 and not dkwargs and callable(dargs[0]))
if invoked:
func = dargs[0]
def wrapper(func):
@hand._click.command(
name=func.__name__.lower(),
help=func.__doc__)
def _wrapper(*args, **kwargs):
log_level = dkwargs.pop('log_level', logging.INFO)
log.setLevel(log_level)
log.debug("decrator param: {} {}".format(dargs, dkwargs))
log.debug("function param: {} {}".format(args, kwargs))
with OTP(*dargs, **dkwargs) as o:
func(o, *args, **kwargs)
return _wrapper
return wrapper if not invoked else wrapper(func)
[文档]class OTP(object):
"""
pyotp实例化后的对象封装,支持一系列接口方法
"""
def __init__(self, secret=None):
if not secret:
raise ValueError('secret 值错误,不可为空')
self.otp = pyotp.TOTP(secret)
def __enter__(self):
return self
[文档] def now(self):
"""
获取当前密码
:return: OTP 密码
:rtype: str
"""
return self.otp.now()
def __exit__(self, exception_type, exception_value, traceback):
if exception_type is None:
return False
elif exception_type is ValueError:
# 返回 False 将异常抛出
return False
else:
log.error('other error: {}\n{}'.format(exception_value, traceback))
return False