Source code for pyvo.auth.authurls

import collections
import logging

from . import securitymethods

__all__ = ["AuthURLs"]


[docs] class AuthURLs(): """ AuthURLs helps determine which security method should be used with a given URL. It learns the security methods through the VOSI capabilities, which are passed in via update_from_capabilities. Three collections are used internally: ``full_urls`` Exact-match entries, populated when a capability declares ``use="full"``. An exact match takes priority and is returned without consulting any other collection. ``_explicit_urls`` Prefix-match entries registered by callers via ``add_security_method_for_url``. All matching entries are combined, so a registration for a base URL propagates to every sub-path beneath it. ``_capability_urls`` Prefix-match entries loaded from VOSI capabilities (``use="base"``). Only the most-specific (longest) matching entry is used. For a given URL ``allowed_auth_methods`` returns the union of: 1. The ``full_urls`` exact match, if one exists (short-circuits). 2. All matching ``_explicit_urls`` entries. 3. The single most-specific matching ``_capability_urls`` entry. """ def __init__(self): self.full_urls = collections.defaultdict(set) self._explicit_urls = collections.defaultdict(set) self._capability_urls = collections.defaultdict(set)
[docs] def update_from_capabilities(self, capabilities): """ Update the URL to security method mapping using the capabilities provided. Parameters ---------- capabilities : object List of `~pyvo.io.vosi.voresource.Capability` """ for c in capabilities: for i in c.interfaces: for u in i.accessurls: url = u.content exact = u.use == 'full' if not i.securitymethods: self._add_capability_method(url, securitymethods.ANONYMOUS, exact) for sm in i.securitymethods: method = sm.standardid or securitymethods.ANONYMOUS self._add_capability_method(url, method, exact)
[docs] def add_security_method_for_url(self, url, security_method, exact=False): """ Add a security method for a url. This is additive with update_from_capabilities. This can be useful to set additional security methods that aren't set in the capabilities for whatever reason. Parameters ---------- url : str URL to set a security method for security_method : str URI of the security method to set exact : bool If True, match only this URL. If False, match all URLs that start with this URL as a base. """ if exact: self.full_urls[url].add(security_method) else: self._explicit_urls[url].add(security_method)
def _add_capability_method(self, url, security_method, exact=False): """Store a security method discovered from VOSI capabilities.""" if exact: self.full_urls[url].add(security_method) else: self._capability_urls[url].add(security_method)
[docs] def allowed_auth_methods(self, url): """ Return the authentication methods allowed for a particular URL. The methods are returned as URIs that represent security methods. Parameters ---------- url : str the URL to determine authentication methods """ logging.debug('Determining auth method for %s', url) # Exact-match entries take unconditional priority. if url in self.full_urls: methods = self.full_urls[url] logging.debug('Matching full url %s, methods %s', url, methods) return methods methods = set() # Union every matching caller-registered prefix. for prefix, prefix_methods in self._sorted(self._explicit_urls): if url.startswith(prefix): logging.debug( 'Matching explicit url %s, methods %s', prefix, prefix_methods ) methods.update(prefix_methods) # Most-specific capability entry wins, stop at first match. for cap_url, cap_methods in self._sorted(self._capability_urls): if url.startswith(cap_url): logging.debug( 'Matching capability url %s, methods %s', cap_url, cap_methods ) methods.update(cap_methods) break if methods: return methods logging.debug('No match, using anonymous auth') return {securitymethods.ANONYMOUS}
def _sorted(self, url_dict): """Yield (url, methods) pairs from ``url_dict``, longest URL first.""" yield from sorted(url_dict.items(), key=lambda x: len(x[0]), reverse=True) def __repr__(self): urls = [] for url, methods in self.full_urls.items(): urls.append('Full match:' + url + ':' + str(methods)) for url, methods in self._sorted(self._explicit_urls): urls.append('Explicit match:' + url + ':' + str(methods)) for url, methods in self._sorted(self._capability_urls): urls.append('Capability match:' + url + ':' + str(methods)) return '\n'.join(urls)