2021SC@SDUSC
http_authenticator.py。是用于HTTP基本和摘要认证的WSGI中间件。
用法:从http_authenticator导入httpauthentication
WSGIApp = HTTPAuthenticator(ProtectedWSGIApp, domain_controller, accept_basic,
accept_digest default_to_digest)
ProtectedWSGIApp是需要身份验证访问的应用程序
Domain_controller是满足特定要求的域控制器对象
Accept_basic是一个布尔值,指示是否使用基本身份验证方案(默认= True)
Accept_digest是一个布尔值,指示是否使用摘要身份验证方案(default = True)
Default_to_digest是一个布尔值。如果为True,则未经身份验证的请求将发送需要验证的摘要响应,否则为未验证。请求将发送基本的认证要求响应(默认= True)
HTTPAuthenticator将把以下经过验证的信息放入
环境字典:
[" wsgidav.auth包围。Realm "] =域名
[" wsgidav.auth包围。user_name "] = user_name
[" wsgidav.auth包围。角色"]= (可选)
[" wsgidav.auth包围。权限"]= (可选)
HTTP基本和摘要身份验证方案基于以下内容概念:
每个请求的相对URI都可以解析到一个域进行身份验证,
例如:
/fac_eng/courses/ee5903/timetable.pdf ->可能会解决领域“工程通用”
/fac_eng/examsolns/ee5903/thisyearssolns.pdf ->可能会解决“工程讲师”领域
/med_sci/courses/m500/surgery.htm ->可能会解决到领域“医学科学一般”
每个领域都有一组user_name和密码对允许访问资源。
域控制器将此信息提供给HTTPAuthenticator。这允许开发人员编写他们自己的域控制器,
例如,与自己的用户数据库接口。对于简单的应用程序,提供了一个SimpleDomainController,它将执行在单个域名(用于显示)和user_name(键)的单个字典中和密码(值)字符串对
用法:
从wsgidav.dc,simple_dc进入SimpleDomainController
users = dict(({'John Smith': 'YouNeverGuessMe', 'Dan Brown': 'DontGuessMeEither'})
realm = '示例领域'
domain_controller = SimpleDomainController(用户,领域)
域控制器必须提供中描述的方法
' ' wsgidav.interfaces.domaincontrollerinterface ' ' (interface_)
.. / domaincontrollerinterface.py _interface:接口
这里的environ变量是WSGI 'environ'字典。它被传递给域控制器的所有方法作为开发人员传递信息的手段,从以前的中间件或服务器配置(如果需要)。
改文件主要是使域控制器,和HTTP身份验证两个部分。HTTP身份验证已经在上面详细描述了。
域控制器主要代码。
def make_domain_controller(wsgidav_app, config):
dc = config.get("http_authenticator", {}).get("domain_controller")
org_dc = dc
if dc is True or not dc:
# True or null:
dc = SimpleDomainController
elif compat.is_basestring(dc):
# If a plain string is passed, try to import it as class
dc = dynamic_import_class(dc)
if inspect.isclass(dc):
# If a class is passed, instantiate that
dc = dc(wsgidav_app, config)
else:
raise RuntimeError(
"Could not resolve domain controller class (got {})".format(org_dc)
)
# print("make_domain_controller", dc)
return dc
def compute_digest_response是在http身份验证中起到重要作用的函数。其主要负责计算消化散列。A1 (HA1)部分的计算委托直流接口法进行“digest_auth_user()”。
参数:
realm(str):
user_name (str):
method(str): WebDAV请求方法
uri (str):
Nonce (str):服务器生成的Nonce值
Cnonce (str):客户端生成的Cnonce值
Qop (str):防护质量
Nc (str) (number), nonce计数器由客户端递增
返回:MD5哈希字符串,如果用户被域控制器拒绝,则为False
代码
def compute_digest_response(
self, realm, user_name, method, uri, nonce, cnonce, qop, nc, environ
):
def md5h(data):
return md5(compat.to_bytes(data)).hexdigest()
def md5kd(secret, data):
return md5h(secret + ":" + data)
A1 = self.domain_controller.digest_auth_user(realm, user_name, environ)
if not A1:
return False
A2 = method + ":" + uri
if qop:
res = md5kd(
A1, nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + md5h(A2)
)
else:
res = md5kd(A1, nonce + ":" + md5h(A2))
return res
lock_manager.py。实现提供锁定功能的‘LockManager’对象。
LockManager需要一个LockStorage对象来实现持久性。在lock_storage模块中定义了两个替代的锁存储类:wsgidav.lock_storage.LockStorageDict和wsgidav.lock_storage.LockStorageShelve。
锁数据模型是一个包含以下字段的字典:
root:资源URL。
principal:创建锁的经过身份验证的用户名。
type:必须是“写”。
scope:必须是“共享的”或“独占的”。
depth:必须是“0”或“无穷大”。
owner:标识所有者的字符串。
timeout:距离锁定过期还有几秒。这个值传递给create()和refresh()
expire:转换的持久性超时:expire = time() + timeout。
token:自动生成的唯一标记。
文件中名为LockManager的类是核心部分。其主要负责使用自定义存储层实现锁定功能。
函数acquire负责检查权限并获取锁。成功返回新的锁字典。如果出现错误,引发带有嵌入式DAVErrorCondition的DAVError。
代码
def acquire(self,url,lock_type,lock_scope,lock_depth,lock_owner,timeout,principal,token_list,):
url = normalize_lock_root(url)
self._lock.acquire_write()
try:
# Raises DAVError on conflict:
self._check_lock_permission(
url, lock_type, lock_scope, lock_depth, token_list, principal
)
return self._generate_lock(
principal, lock_type, lock_scope, lock_depth, lock_owner, url, timeout
)
finally:
self._lock.release()
函数get_lock,返回lock_dict,如果没有找到或无效,返回None。副作用:如果锁过期,它将被清除并返回None。关键:将被返回的锁属性名称,而不是一个字典。
def get_lock(self, token, key=None):
assert key in (
None,
"type",
"scope",
"depth",
"owner",
"root",
"timeout",
"principal",
"token",
)
lock = self.storage.get(token)
if key is None or lock is None:
return lock
return lock[key]
函数get_indirect_url_lock_list,返回一个有效的lockdict列表,该列表直接或间接地保护。如果给定一个主体,则只返回该主体拥有的锁。副作用:此路径的锁过期,所有父路径都会被清除。
def get_indirect_url_lock_list(self, url, principal=None):
"""Return a list of valid lockDicts, that protect <path> directly or indirectly.
If a principal is given, only locks owned by this principal are returned.
Side effect: expired locks for this path and all parents are purged.
"""
url = normalize_lock_root(url)
lockList = []
u = url
while u:
lock_list = self.storage.get_lock_list(
u, include_root=True, include_children=False, token_only=False
)
for lock in lock_list:
if u != url and lock["depth"] != "infinity":
continue # We only consider parents with Depth: infinity
# TODO: handle shared locks in some way?
# if (lock["scope"] == "shared" and lock_scope == "shared"
# and principal != lock["principal"]):
# continue # Only compatible with shared locks by other users
if principal is None or principal == lock["principal"]:
lockList.append(lock)
u = util.get_uri_parent(u)
return lockList
函数_check_lock_permission检查,如果可以锁定,否则将引发错误。如果锁定会创建冲突,则会引发DAVError(HTTP_LOCKED)。嵌入的DAVErrorCondition包含冲突资源。
代码
def _check_lock_permission(
self, url, lock_type, lock_scope, lock_depth, token_list, principal
):
errcond = DAVErrorCondition(PRECONDITION_CODE_LockConflict)
self._lock.acquire_read()
try:
# Check url and all parents for conflicting locks
u = url
while u:
lock_list = self.get_url_lock_list(u)
for lock in lock_list:
_logger.debug(
" check parent {}, {}".format(u, lock_string(lock))
)
if u != url and lock["depth"] != "infinity":
# We only consider parents with Depth: infinity
continue
elif lock["scope"] == "shared" and lock_scope == "shared":
# Only compatible with shared locks (even by same
# principal)
continue
# Lock conflict
_logger.debug(
" -> DENIED due to locked parent {}".format(lock_string(lock))
)
errcond.add_href(lock["root"])
u = util.get_uri_parent(u)
if lock_depth == "infinity":
# Check child URLs for conflicting locks
child_ocks = self.storage.get_lock_list(
url, include_root=False, include_children=True, token_only=False
)
for lock in child_ocks:
assert util.is_child_uri(url, lock["root"])
# if util.is_child_uri(url, lock["root"]):
_logger.debug(
" -> DENIED due to locked child {}".format(lock_string(lock))
)
errcond.add_href(lock["root"])
finally:
self._lock.release()
# If there were conflicts, raise HTTP_LOCKED for <url>, and pass
# conflicting resource with 'no-conflicting-lock' precondition
if len(errcond.hrefs) > 0:
raise DAVError(HTTP_LOCKED, err_condition=errcond)
return