diff options
author | Aarni Koskela <akx@iki.fi> | 2022-10-27 15:13:08 +0300 |
---|---|---|
committer | Ivan Kanakarakis <ivan.kanak@gmail.com> | 2022-11-15 13:06:41 +0200 |
commit | 88feeba03c2f891a31a86cbb24b210070aab1fdc (patch) | |
tree | 4bf48f6b2ca8e86aac23e825fbc6e84b134c400c /src | |
parent | 4fa20a92a9d7fccc2ca34f1f6ad777cc0fd36ef7 (diff) | |
download | pysaml2-88feeba03c2f891a31a86cbb24b210070aab1fdc.tar.gz |
Run pyupgrade --py36-plus + black + isort
Diffstat (limited to 'src')
55 files changed, 395 insertions, 433 deletions
diff --git a/src/saml2/__init__.py b/src/saml2/__init__.py index 34272fe8..0e38e551 100644 --- a/src/saml2/__init__.py +++ b/src/saml2/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """Contains base classes representing SAML elements. @@ -26,7 +25,7 @@ from saml2.version import version as __version__ try: - from xml.etree import cElementTree as ElementTree + from xml.etree import ElementTree as ElementTree if ElementTree.VERSION < "1.3.0": # cElementTree has no support for register_namespace @@ -79,7 +78,7 @@ BINDING_URI = "urn:oasis:names:tc:SAML:2.0:bindings:URI" def class_name(instance): - return "%s:%s" % (instance.c_namespace, instance.c_tag) + return f"{instance.c_namespace}:{instance.c_tag}" def create_class_from_xml_string(target_class, xml_string): @@ -96,7 +95,7 @@ def create_class_from_xml_string(target_class, xml_string): the contents of the XML - or None if the root XML tag and namespace did not match those of the target class. """ - if not isinstance(xml_string, six.binary_type): + if not isinstance(xml_string, bytes): xml_string = xml_string.encode("utf-8") tree = defusedxml.ElementTree.fromstring(xml_string) return create_class_from_element_tree(target_class, tree) @@ -126,7 +125,7 @@ def create_class_from_element_tree(target_class, tree, namespace=None, tag=None) namespace = target_class.c_namespace if tag is None: tag = target_class.c_tag - if tree.tag == "{%s}%s" % (namespace, tag): + if tree.tag == f"{{{namespace}}}{tag}": target = target_class() target.harvest_element_tree(tree) return target @@ -144,7 +143,7 @@ class SAMLError(Exception): pass -class ExtensionElement(object): +class ExtensionElement: """XML which is not part of the SAML specification, these are called extension elements. If a classes parser encounters an unexpected XML construct, it is translated into an @@ -184,7 +183,7 @@ class ExtensionElement(object): element_tree = ElementTree.Element("") if self.namespace is not None: - element_tree.tag = "{%s}%s" % (self.namespace, self.tag) + element_tree.tag = f"{{{self.namespace}}}{self.tag}" else: element_tree.tag = self.tag @@ -300,7 +299,7 @@ def _extension_element_from_element_tree(element_tree): return extension -class ExtensionContainer(object): +class ExtensionContainer: c_tag = "" c_namespace = "" @@ -464,8 +463,7 @@ class SamlBase(ExtensionContainer): def _get_all_c_children_with_order(self): if len(self.c_child_order) > 0: - for child in self.c_child_order: - yield child + yield from self.c_child_order else: for _, values in iter(self.__class__.c_children.items()): yield values[0] @@ -542,7 +540,7 @@ class SamlBase(ExtensionContainer): should not be called on in this class. """ - new_tree = ElementTree.Element("{%s}%s" % (self.__class__.c_namespace, self.__class__.c_tag)) + new_tree = ElementTree.Element(f"{{{self.__class__.c_namespace}}}{self.__class__.c_tag}") self._add_members_to_element_tree(new_tree) return new_tree @@ -689,7 +687,7 @@ class SamlBase(ExtensionContainer): def __str__(self): # Yes this is confusing. http://bugs.python.org/issue10942 x = self.to_string() - if not isinstance(x, six.string_types): + if not isinstance(x, str): x = x.decode("utf-8") return x @@ -742,7 +740,7 @@ class SamlBase(ExtensionContainer): self.text = "true" if val else "false" elif isinstance(val, int): self.text = str(val) - elif isinstance(val, six.string_types): + elif isinstance(val, str): self.text = val elif val is None: pass @@ -817,7 +815,7 @@ class SamlBase(ExtensionContainer): continue svals = self.__dict__[key] ovals = other.__dict__[key] - if isinstance(svals, six.string_types): + if isinstance(svals, str): if svals != ovals: return False elif isinstance(svals, list): diff --git a/src/saml2/assertion.py b/src/saml2/assertion.py index 237bca7b..40ae7112 100644 --- a/src/saml2/assertion.py +++ b/src/saml2/assertion.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import copy import importlib import logging @@ -40,7 +39,7 @@ def _filter_values(vals, vlist=None, must=False): if vals is None: # cannot iterate over None, return early return vals - if isinstance(vlist, six.string_types): + if isinstance(vlist, str): vlist = [vlist] res = [] @@ -148,7 +147,7 @@ def filter_on_demands(ava, required=None, optional=None): if required is None: required = {} - lava = dict([(k.lower(), k) for k in ava.keys()]) + lava = {k.lower(): k for k in ava.keys()} for attr, vals in required.items(): attr = attr.lower() @@ -156,9 +155,9 @@ def filter_on_demands(ava, required=None, optional=None): if vals: for val in vals: if val not in ava[lava[attr]]: - raise MissingValue("Required attribute value missing: %s,%s" % (attr, val)) + raise MissingValue(f"Required attribute value missing: {attr},{val}") else: - raise MissingValue("Required attribute missing: %s" % (attr,)) + raise MissingValue(f"Required attribute missing: {attr}") if optional is None: optional = {} @@ -184,7 +183,7 @@ def filter_on_wire_representation(ava, acs, required=None, optional=None): :param optional: A list of saml.Attributes :return: Dictionary of expected/wanted attributes and values """ - acsdic = dict([(ac.name_format, ac) for ac in acs]) + acsdic = {ac.name_format: ac for ac in acs} if required is None: required = [] @@ -239,7 +238,7 @@ def filter_attribute_value_assertions(ava, attribute_restrictions=None): else: if _rests is None: continue - if isinstance(vals, six.string_types): + if isinstance(vals, str): vals = [vals] rvals = [] for restr in _rests: @@ -308,7 +307,7 @@ def compile(restrictions): return restrictions -class Policy(object): +class Policy: """Handles restrictions on assertions.""" def __init__(self, restrictions=None, mds=None): @@ -582,7 +581,7 @@ class Policy(object): ) -class EntityCategories(object): +class EntityCategories: pass diff --git a/src/saml2/attribute_converter.py b/src/saml2/attribute_converter.py index 900384f9..505d1564 100644 --- a/src/saml2/attribute_converter.py +++ b/src/saml2/attribute_converter.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # from importlib import import_module @@ -131,7 +130,7 @@ def list_to_local(acs, attrlist, allow_unknown_attributes=False): acs = [AttributeConverter()] acsd = {"": acs} else: - acsd = dict([(a.name_format, a) for a in acs]) + acsd = {a.name_format: a for a in acs} ava = {} for attr in attrlist: @@ -227,7 +226,7 @@ def d_to_local_name(acs, attr): raise ConverterError("Could not find local name for %s" % attr) -class AttributeConverter(object): +class AttributeConverter: """Converts from an attribute statement to a key,value dictionary and vice-versa""" @@ -242,9 +241,9 @@ class AttributeConverter(object): """ if self._fro is None and self._to is not None: - self._fro = dict([(value.lower(), key) for key, value in self._to.items()]) + self._fro = {value.lower(): key for key, value in self._to.items()} if self._to is None and self._fro is not None: - self._to = dict([(value.lower(), key) for key, value in self._fro.items()]) + self._to = {value.lower(): key for key, value in self._fro.items()} def from_dict(self, mapdict): """Import the attribute map from a dictionary @@ -254,11 +253,11 @@ class AttributeConverter(object): self.name_format = mapdict["identifier"] try: - self._fro = dict([(k.lower(), v) for k, v in mapdict["fro"].items()]) + self._fro = {k.lower(): v for k, v in mapdict["fro"].items()} except KeyError: pass try: - self._to = dict([(k.lower(), v) for k, v in mapdict["to"].items()]) + self._to = {k.lower(): v for k, v in mapdict["to"].items()} except KeyError: pass diff --git a/src/saml2/attribute_resolver.py b/src/saml2/attribute_resolver.py index c89dd095..e555c58c 100644 --- a/src/saml2/attribute_resolver.py +++ b/src/saml2/attribute_resolver.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # """ @@ -17,7 +16,7 @@ logger = logging.getLogger(__name__) DEFAULT_BINDING = BINDING_SOAP -class AttributeResolver(object): +class AttributeResolver: def __init__(self, saml2client, metadata=None, config=None): self.metadata = metadata self.saml2client = saml2client diff --git a/src/saml2/authn.py b/src/saml2/authn.py index 156c3795..1ed41152 100644 --- a/src/saml2/authn.py +++ b/src/saml2/authn.py @@ -1,10 +1,10 @@ import logging import time +from urllib.parse import parse_qs +from urllib.parse import urlencode +from urllib.parse import urlsplit import six -from six.moves.urllib.parse import parse_qs -from six.moves.urllib.parse import urlencode -from six.moves.urllib.parse import urlsplit from saml2 import SAMLError import saml2.cryptography.symmetric @@ -28,7 +28,7 @@ class EncodeError(SAMLError): pass -class UserAuthnMethod(object): +class UserAuthnMethod: def __init__(self, srv): self.srv = srv @@ -80,7 +80,7 @@ def create_return_url(base, query, **kwargs): for key, values in parse_qs(query).items(): if key in kwargs: - if isinstance(kwargs[key], six.string_types): + if isinstance(kwargs[key], str): kwargs[key] = [kwargs[key]] kwargs[key].extend(values) else: @@ -89,7 +89,7 @@ def create_return_url(base, query, **kwargs): if part.query: for key, values in parse_qs(part.query).items(): if key in kwargs: - if isinstance(kwargs[key], six.string_types): + if isinstance(kwargs[key], str): kwargs[key] = [kwargs[key]] kwargs[key].extend(values) else: @@ -101,7 +101,7 @@ def create_return_url(base, query, **kwargs): logger.debug("kwargs: %s" % kwargs) - return "%s?%s" % (_pre, url_encode_params(kwargs)) + return f"{_pre}?{url_encode_params(kwargs)}" class UsernamePasswordMako(UserAuthnMethod): @@ -166,7 +166,7 @@ class UsernamePasswordMako(UserAuthnMethod): """ # logger.debug("verify(%s)" % request) - if isinstance(request, six.string_types): + if isinstance(request, str): _dict = parse_qs(request) elif isinstance(request, dict): _dict = request @@ -225,7 +225,7 @@ class SocialService(UserAuthnMethod): return self.social.callback(server_env, cookie, sid, query, **kwargs) -class AuthnMethodChooser(object): +class AuthnMethodChooser: def __init__(self, methods=None): self.methods = methods diff --git a/src/saml2/authn_context/__init__.py b/src/saml2/authn_context/__init__.py index 8209f7df..76649529 100644 --- a/src/saml2/authn_context/__init__.py +++ b/src/saml2/authn_context/__init__.py @@ -26,7 +26,7 @@ AL4 = "http://idmanagement.gov/icam/2009/12/saml_2.0_profile/assurancelevel4" CMP_TYPE = ["exact", "minimum", "maximum", "better"] -class AuthnBroker(object): +class AuthnBroker: def __init__(self): self.db = {"info": {}, "key": {}} self.next = 0 diff --git a/src/saml2/cache.py b/src/saml2/cache.py index dab91546..172afe98 100644 --- a/src/saml2/cache.py +++ b/src/saml2/cache.py @@ -30,7 +30,7 @@ class CacheError(SAMLError): pass -class Cache(object): +class Cache: def __init__(self, filename=None): if filename: self._db = shelve.open(filename, writeback=True, protocol=2) @@ -108,7 +108,7 @@ class Cache(object): if check_not_on_or_after and time_util.after(timestamp): raise TooOld("past %s" % str(timestamp)) - if "name_id" in info and isinstance(info["name_id"], six.string_types): + if "name_id" in info and isinstance(info["name_id"], str): info["name_id"] = decode(info["name_id"]) return info or None @@ -123,7 +123,7 @@ class Cache(object): :param not_on_or_after: A time after which the assertion is not valid. """ info = dict(info) - if "name_id" in info and not isinstance(info["name_id"], six.string_types): + if "name_id" in info and not isinstance(info["name_id"], str): # make friendly to (JSON) serialization info["name_id"] = code(name_id) diff --git a/src/saml2/cert.py b/src/saml2/cert.py index 2354a714..08f0bd25 100644 --- a/src/saml2/cert.py +++ b/src/saml2/cert.py @@ -25,7 +25,7 @@ class PayloadError(Exception): pass -class OpenSSLWrapper(object): +class OpenSSLWrapper: def __init__(self): pass @@ -165,15 +165,15 @@ class OpenSSLWrapper(object): tmp_key = None if cipher_passphrase is not None: passphrase = cipher_passphrase["passphrase"] - if isinstance(cipher_passphrase["passphrase"], six.string_types): + if isinstance(cipher_passphrase["passphrase"], str): passphrase = passphrase.encode("utf-8") tmp_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, k, cipher_passphrase["cipher"], passphrase) else: tmp_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, k) if write_to_file: - with open(c_f, "wt") as fc: + with open(c_f, "w") as fc: fc.write(tmp_cert.decode("utf-8")) - with open(k_f, "wt") as fk: + with open(k_f, "w") as fk: fk.write(tmp_key.decode("utf-8")) return c_f, k_f return tmp_cert, tmp_key @@ -181,7 +181,7 @@ class OpenSSLWrapper(object): raise CertificateError("Certificate cannot be generated.", ex) def write_str_to_file(self, file, str_data): - with open(file, "wt") as f: + with open(file, "w") as f: f.write(str_data) def read_str_from_file(self, file, type="pem"): @@ -256,7 +256,7 @@ class OpenSSLWrapper(object): cert.sign(ca_key, hash_alg) cert_dump = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) - if isinstance(cert_dump, six.string_types): + if isinstance(cert_dump, str): return cert_dump return cert_dump.decode("utf-8") @@ -323,9 +323,8 @@ class OpenSSLWrapper(object): return False, ("CN may not be equal for CA certificate and the " "signed certificate.") cert_algorithm = cert.get_signature_algorithm() - if six.PY3: - cert_algorithm = cert_algorithm.decode("ascii") - cert_str = cert_str.encode("ascii") + cert_algorithm = cert_algorithm.decode("ascii") + cert_str = cert_str.encode("ascii") cert_crypto = saml2.cryptography.pki.load_pem_x509_certificate(cert_str) diff --git a/src/saml2/client.py b/src/saml2/client.py index bf07cf6a..018ae6ef 100644 --- a/src/saml2/client.py +++ b/src/saml2/client.py @@ -1,5 +1,4 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- # import six @@ -88,9 +87,7 @@ class Saml2Client(Base): ) if negotiated_binding != binding: - raise ValueError( - "Negotiated binding '{}' does not match binding to use '{}'".format(negotiated_binding, binding) - ) + raise ValueError(f"Negotiated binding '{negotiated_binding}' does not match binding to use '{binding}'") return reqid, info @@ -212,7 +209,7 @@ class Saml2Client(Base): conversation. """ - if isinstance(name_id, six.string_types): + if isinstance(name_id, str): name_id = decode(name_id) logger.debug("logout request for: %s", name_id) @@ -379,7 +376,7 @@ class Saml2Client(Base): if not_done: # upstream should try later - raise LogoutError("%s" % (entity_ids,)) + raise LogoutError(f"{entity_ids}") return responses @@ -505,9 +502,9 @@ class Saml2Client(Base): srvs = self.metadata.assertion_id_request_service(entity_id, BINDING_SOAP) if not srvs: - raise NoServiceDefined("%s: %s" % (entity_id, "assertion_id_request_service")) + raise NoServiceDefined("{}: {}".format(entity_id, "assertion_id_request_service")) - if isinstance(assertion_ids, six.string_types): + if isinstance(assertion_ids, str): assertion_ids = [assertion_ids] _id_refs = [AssertionIDRef(_id) for _id in assertion_ids] diff --git a/src/saml2/client_base.py b/src/saml2/client_base.py index 69f1b3af..146ae45c 100644 --- a/src/saml2/client_base.py +++ b/src/saml2/client_base.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # """Contains classes and functions that a SAML2.0 Service Provider (SP) may use @@ -9,12 +8,12 @@ import logging import threading import time from typing import Mapping +from urllib.parse import parse_qs +from urllib.parse import urlencode +from urllib.parse import urlparse from warnings import warn as _warn import six -from six.moves.urllib.parse import parse_qs -from six.moves.urllib.parse import urlencode -from six.moves.urllib.parse import urlparse import saml2 from saml2 import BINDING_HTTP_POST @@ -383,7 +382,7 @@ class Base(Entity): if isinstance(_item, _msg.child_class(param)): args[param] = _item else: - raise ValueError("Wrong type for param {name}".format(name=param)) + raise ValueError(f"Wrong type for param {param}") # NameIDPolicy nameid_policy_format_config = self.config.getattr("name_id_policy_format", "sp") @@ -513,7 +512,7 @@ class Base(Entity): pass else: raise AttributeError("Missing required parameter") - elif isinstance(name_id, six.string_types): + elif isinstance(name_id, str): name_id = saml.NameID(text=name_id) for key in ["sp_name_qualifier", "name_qualifier", "format"]: try: @@ -627,7 +626,7 @@ class Base(Entity): """ if action: - if isinstance(action, six.string_types): + if isinstance(action, str): _action = [saml.Action(text=action)] else: _action = [saml.Action(text=a) for a in action] @@ -657,7 +656,7 @@ class Base(Entity): :return: One ID ref """ - if isinstance(assertion_id_refs, six.string_types): + if isinstance(assertion_id_refs, str): return 0, assertion_id_refs else: return 0, assertion_id_refs[0] @@ -1003,9 +1002,9 @@ class Base(Entity): params = urlencode({k: v for k, v in args.items() if v}) # url can already contain some parameters if "?" in url: - return "%s&%s" % (url, params) + return f"{url}&{params}" else: - return "%s?%s" % (url, params) + return f"{url}?{params}" @staticmethod def parse_discovery_service_response(url="", query="", returnIDParam="entityID"): diff --git a/src/saml2/config.py b/src/saml2/config.py index a92642d9..dcf8fd75 100644 --- a/src/saml2/config.py +++ b/src/saml2/config.py @@ -163,7 +163,7 @@ class ConfigurationError(SAMLError): pass -class Config(object): +class Config: def_context = "" def __init__(self, homedir="."): @@ -234,7 +234,7 @@ class Config(object): if context == "": setattr(self, attr, val) else: - setattr(self, "_%s_%s" % (context, attr), val) + setattr(self, f"_{context}_{attr}", val) def getattr(self, attr, context=None): if context is None: @@ -243,7 +243,7 @@ class Config(object): if context == "": return getattr(self, attr, None) else: - return getattr(self, "_%s_%s" % (context, attr), None) + return getattr(self, f"_{context}_{attr}", None) def load_special(self, cnf, typ): for arg in SPEC[typ]: diff --git a/src/saml2/country_codes.py b/src/saml2/country_codes.py index bf4b60a4..55271d7b 100644 --- a/src/saml2/country_codes.py +++ b/src/saml2/country_codes.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# This Python file uses the following encoding: utf-8 # ISO 3166-1 country names and codes from http://opencountrycodes.appspot.com/python COUNTRIES = ( diff --git a/src/saml2/cryptography/symmetric.py b/src/saml2/cryptography/symmetric.py index 636bc340..ea4565b8 100644 --- a/src/saml2/cryptography/symmetric.py +++ b/src/saml2/cryptography/symmetric.py @@ -18,7 +18,7 @@ from .errors import SymmetricCryptographyError logger = logging.getLogger(__name__) -class Fernet(object): +class Fernet: """The default symmetric cryptography method.""" @staticmethod @@ -97,7 +97,7 @@ class Fernet(object): _warn(_deprecation_msg, DeprecationWarning) -class AESCipher(object): +class AESCipher: """[deprecated] Symmetric cryptography method using AES. The default parameter set is AES 128bit in CBC mode. @@ -142,18 +142,18 @@ class AESCipher(object): iv = _os.urandom(self.AES_BLOCK_SIZE) if len(iv) != self.AES_BLOCK_SIZE: - raise Exception("Wrong iv size: {}".format(len(iv))) + raise Exception(f"Wrong iv size: {len(iv)}") if bits not in _ciphers.algorithms.AES.key_sizes: - raise Exception("Unsupported key length: {}".format(bits)) + raise Exception(f"Unsupported key length: {bits}") if len(self.key) != bits / 8: - raise Exception("Wrong Key length: {}".format(len(self.key))) + raise Exception(f"Wrong Key length: {len(self.key)}") try: mode = self.POSTFIX_MODE[cmode] except KeyError: - raise Exception("Unsupported chaining mode: {}".format(cmode)) + raise Exception(f"Unsupported chaining mode: {cmode}") cipher = _ciphers.Cipher(_ciphers.algorithms.AES(self.key), mode(iv)) diff --git a/src/saml2/discovery.py b/src/saml2/discovery.py index f7de497b..d4be1ef3 100644 --- a/src/saml2/discovery.py +++ b/src/saml2/discovery.py @@ -1,4 +1,4 @@ -from six.moves.urllib import parse +from urllib import parse from saml2.entity import Entity from saml2.response import VerificationError @@ -28,7 +28,7 @@ class DiscoveryServer(Entity): for key in ["isPassive", "return", "returnIDParam", "policy", "entityID"]: try: if len(dsr[key]) != 1: - raise Exception("Invalid DS request keys: {k}".format(k=key)) + raise Exception(f"Invalid DS request keys: {key}") dsr[key] = dsr[key][0] except KeyError: pass @@ -77,9 +77,9 @@ class DiscoveryServer(Entity): part = parse.urlparse(return_url) if part.query: # Iff there is a query part add the new info at the end - return_url = "%s&%s" % (return_url, qp) + return_url = f"{return_url}&{qp}" else: - return_url = "%s?%s" % (return_url, qp) + return_url = f"{return_url}?{qp}" return return_url diff --git a/src/saml2/ecp.py b/src/saml2/ecp.py index 676544c4..4615fc49 100644 --- a/src/saml2/ecp.py +++ b/src/saml2/ecp.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # """ @@ -31,7 +30,7 @@ logger = logging.getLogger(__name__) def ecp_capable(headers): if MIME_PAOS in headers["Accept"]: if "PAOS" in headers: - if 'ver="%s";"%s"' % (paos.NAMESPACE, SERVICE) in headers["PAOS"]: + if f'ver="{paos.NAMESPACE}";"{SERVICE}"' in headers["PAOS"]: return True return False @@ -70,7 +69,7 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=None, sign_alg=Non # <samlp:AuthnRequest> # ---------------------------------------- - logger.info("entityid: %s, binding: %s" % (entityid, BINDING_SOAP)) + logger.info(f"entityid: {entityid}, binding: {BINDING_SOAP}") location = cls._sso_location(entityid, binding=BINDING_SOAP) req_id, authn_req = cls.create_authn_request( diff --git a/src/saml2/ecp_client.py b/src/saml2/ecp_client.py index 8286ccea..ce1fca2c 100644 --- a/src/saml2/ecp_client.py +++ b/src/saml2/ecp_client.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # """ @@ -7,10 +6,9 @@ Contains a class that can do SAML ECP Authentication for other python programs. """ +from http import cookiejar as cookielib import logging -from six.moves import http_cookiejar as cookielib - from saml2 import BINDING_SOAP from saml2 import SAMLError from saml2 import saml @@ -28,7 +26,7 @@ from saml2.s_utils import BadRequest SERVICE = "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp" -PAOS_HEADER_INFO = 'ver="%s";"%s"' % (paos.NAMESPACE, SERVICE) +PAOS_HEADER_INFO = f'ver="{paos.NAMESPACE}";"{SERVICE}"' logger = logging.getLogger(__name__) @@ -139,9 +137,7 @@ class Client(Entity): logger.debug("[P2] Got IdP response: %s", response) if response.status_code != 200: - raise SAMLError( - "Request to IdP failed ({status}): {text}".format(status=response.status_code, text=response.text) - ) + raise SAMLError(f"Request to IdP failed ({response.status_code}): {response.text}") # SAMLP response in a SOAP envelope body, ecp response in headers respdict = self.parse_soap_message(response.text) @@ -322,7 +318,7 @@ class Client(Entity): raise if response.status_code >= 400: - raise SAMLError("Error performing operation: %s" % (response.text,)) + raise SAMLError(f"Error performing operation: {response.text}") return response diff --git a/src/saml2/entity.py b/src/saml2/entity.py index aac23afc..b56388d9 100644 --- a/src/saml2/entity.py +++ b/src/saml2/entity.py @@ -117,11 +117,11 @@ def create_artifact(entity_id, message_handle, endpoint_index=0): :param endpoint_index: :return: """ - if not isinstance(entity_id, six.binary_type): + if not isinstance(entity_id, bytes): entity_id = entity_id.encode("utf-8") sourceid = sha1(entity_id) - if not isinstance(message_handle, six.binary_type): + if not isinstance(message_handle, bytes): message_handle = message_handle.encode("utf-8") ter = b"".join((ARTIFACT_TYPECODE, ("%.2x" % endpoint_index).encode("ascii"), sourceid.digest(), message_handle)) return base64.b64encode(ter).decode("ascii") @@ -182,7 +182,7 @@ class Entity(HTTPBase): self.sec = security_context(self.config) if virtual_organization: - if isinstance(virtual_organization, six.string_types): + if isinstance(virtual_organization, str): self.vorg = self.config.vorg[virtual_organization] elif isinstance(virtual_organization, VirtualOrg): self.vorg = virtual_organization @@ -268,7 +268,7 @@ class Entity(HTTPBase): sign = sign if sign is not None else self.should_sign sign_alg = sigalg or self.signing_algorithm if sign_alg not in [long_name for short_name, long_name in SIG_ALLOWED_ALG]: - raise Exception("Signature algo not in allowed list: {algo}".format(algo=sign_alg)) + raise Exception(f"Signature algo not in allowed list: {sign_alg}") # unless if BINDING_HTTP_ARTIFACT if response: @@ -499,9 +499,9 @@ class Entity(HTTPBase): sign_alg = sign_alg or self.signing_algorithm digest_alg = digest_alg or self.digest_algorithm if sign_alg not in [long_name for short_name, long_name in SIG_ALLOWED_ALG]: - raise Exception("Signature algo not in allowed list: {algo}".format(algo=sign_alg)) + raise Exception(f"Signature algo not in allowed list: {sign_alg}") if digest_alg not in [long_name for short_name, long_name in DIGEST_ALLOWED_ALG]: - raise Exception("Digest algo not in allowed list: {algo}".format(algo=digest_alg)) + raise Exception(f"Digest algo not in allowed list: {digest_alg}") if msg.signature is None: msg.signature = pre_signature_part(msg.id, self.sec.my_cert, 1, sign_alg=sign_alg, digest_alg=digest_alg) diff --git a/src/saml2/eptid.py b/src/saml2/eptid.py index de32015e..65108d4c 100644 --- a/src/saml2/eptid.py +++ b/src/saml2/eptid.py @@ -14,7 +14,7 @@ import six logger = logging.getLogger(__name__) -class Eptid(object): +class Eptid: def __init__(self, secret): self._db = {} self.secret = secret @@ -23,27 +23,27 @@ class Eptid(object): md5 = hashlib.md5() for arg in args: md5.update(arg.encode("utf-8")) - if isinstance(sp, six.binary_type): + if isinstance(sp, bytes): md5.update(sp) else: md5.update(sp.encode("utf-8")) - if isinstance(self.secret, six.binary_type): + if isinstance(self.secret, bytes): md5.update(self.secret) else: md5.update(self.secret.encode("utf-8")) md5.digest() hashval = md5.hexdigest() - if isinstance(hashval, six.binary_type): + if isinstance(hashval, bytes): hashval = hashval.decode("ascii") return "!".join([idp, sp, hashval]) def __getitem__(self, key): - if six.PY3 and isinstance(key, six.binary_type): + if six.PY3 and isinstance(key, bytes): key = key.decode("utf-8") return self._db[key] def __setitem__(self, key, value): - if six.PY3 and isinstance(key, six.binary_type): + if six.PY3 and isinstance(key, bytes): key = key.decode("utf-8") self._db[key] = value @@ -64,9 +64,8 @@ class Eptid(object): class EptidShelve(Eptid): def __init__(self, secret, filename): Eptid.__init__(self, secret) - if six.PY3: - if filename.endswith(".db"): - filename = filename.rsplit(".db", 1)[0] + if filename.endswith(".db"): + filename = filename.rsplit(".db", 1)[0] self._db = shelve.open(filename, writeback=True, protocol=2) def close(self): diff --git a/src/saml2/filter.py b/src/saml2/filter.py index 6aad1000..849cfe5d 100644 --- a/src/saml2/filter.py +++ b/src/saml2/filter.py @@ -1,7 +1,7 @@ __author__ = "roland" -class Filter(object): +class Filter: def __init__(self): pass @@ -16,7 +16,7 @@ class AllowDescriptor(Filter): :param allow: List of allowed descriptors :return: """ - super(AllowDescriptor, self).__init__() + super().__init__() self.allow = allow def __call__(self, entity_descriptor): diff --git a/src/saml2/httpbase.py b/src/saml2/httpbase.py index 4c0a5ad4..f183cf8e 100644 --- a/src/saml2/httpbase.py +++ b/src/saml2/httpbase.py @@ -1,14 +1,14 @@ import calendar import copy +from http.cookies import SimpleCookie import logging import re import time +from urllib.parse import urlencode +from urllib.parse import urlparse import requests from six.moves import http_cookiejar -from six.moves.http_cookies import SimpleCookie -from six.moves.urllib.parse import urlencode -from six.moves.urllib.parse import urlparse from saml2 import SAMLError from saml2 import class_name @@ -80,7 +80,7 @@ def _since_epoch(cdate): break if t == -1: - err = 'ValueError: Date "{0}" does not match any of: {1}'.format(cdate, TIME_FORMAT) + err = f'ValueError: Date "{cdate}" does not match any of: {TIME_FORMAT}' raise Exception(err) return calendar.timegm(t) @@ -94,7 +94,7 @@ def dict2set_list(dic): return [(k, v) for k, v in dic.items()] -class HTTPBase(object): +class HTTPBase: def __init__(self, verify=True, ca_bundle=None, key_file=None, cert_file=None, http_client_timeout=None): self.request_args = {"allow_redirects": False} # self.cookies = {} @@ -250,7 +250,7 @@ class HTTPBase(object): query = urlencode({"SAMLart": message, "RelayState": relay_state}) else: query = urlencode({"SAMLart": message}) - info = {"data": "", "url": "%s?%s" % (destination, query)} + info = {"data": "", "url": f"{destination}?{query}"} return info @staticmethod @@ -274,7 +274,7 @@ class HTTPBase(object): query = urlencode({"ID": message, "RelayState": relay_state}) else: query = urlencode({"ID": message}) - info = {"data": "", "url": "%s?%s" % (destination, query)} + info = {"data": "", "url": f"{destination}?{query}"} else: raise NotImplementedError diff --git a/src/saml2/httputil.py b/src/saml2/httputil.py index 2e5e7c27..e0d9bd24 100644 --- a/src/saml2/httputil.py +++ b/src/saml2/httputil.py @@ -1,13 +1,13 @@ import cgi import hashlib import hmac +from http.cookies import SimpleCookie import logging import time +from urllib.parse import parse_qs +from urllib.parse import quote import six -from six.moves.http_cookies import SimpleCookie -from six.moves.urllib.parse import parse_qs -from six.moves.urllib.parse import quote from saml2 import BINDING_HTTP_ARTIFACT from saml2 import BINDING_HTTP_POST @@ -23,7 +23,7 @@ __author__ = "rohe0002" logger = logging.getLogger(__name__) -class Response(object): +class Response: _template = None _status = "200 OK" _content_type = "text/html" @@ -63,9 +63,9 @@ class Response(object): mte = self.mako_lookup.get_template(self.mako_template) message = mte.render(**argv) - if isinstance(message, six.string_types): + if isinstance(message, str): return [message.encode("utf-8")] - elif isinstance(message, six.binary_type): + elif isinstance(message, bytes): return [message] else: return message @@ -159,7 +159,7 @@ class BadGateway(Response): _status = "502 Bad Gateway" -class HttpParameters(object): +class HttpParameters: """GET or POST signature parameters for Redirect or POST-SimpleSign bindings because they are not contained in XML unlike the POST binding """ @@ -253,13 +253,13 @@ def get_response(environ, start_response): def unpack_redirect(environ): if "QUERY_STRING" in environ: _qs = environ["QUERY_STRING"] - return dict([(k, v[0]) for k, v in parse_qs(_qs).items()]) + return {k: v[0] for k, v in parse_qs(_qs).items()} else: return None def unpack_post(environ): - return dict([(k, v[0]) for k, v in parse_qs(get_post(environ))]) + return {k: v[0] for k, v in parse_qs(get_post(environ))} def unpack_soap(environ): diff --git a/src/saml2/ident.py b/src/saml2/ident.py index d52051d1..d94dd756 100644 --- a/src/saml2/ident.py +++ b/src/saml2/ident.py @@ -2,10 +2,10 @@ import copy from hashlib import sha256 import logging import shelve +from urllib.parse import quote +from urllib.parse import unquote import six -from six.moves.urllib.parse import quote -from six.moves.urllib.parse import unquote from saml2 import SAMLError from saml2.s_utils import PolicyError @@ -52,7 +52,7 @@ def code_binary(item): Return a binary 'code' suitable for hashing. """ code_str = code(item) - if isinstance(code_str, six.string_types): + if isinstance(code_str, str): return code_str.encode("utf-8") return code_str @@ -73,13 +73,13 @@ def decode(txt): return _nid -class IdentDB(object): +class IdentDB: """A class that handles identifiers of entities Keeps a list of all nameIDs returned per SP """ def __init__(self, db, domain="", name_qualifier=""): - if isinstance(db, six.string_types): + if isinstance(db, str): self.db = shelve.open(db, protocol=2) else: self.db = db @@ -88,15 +88,15 @@ class IdentDB(object): def _create_id(self, nformat, name_qualifier="", sp_name_qualifier=""): _id = sha256(rndbytes(32)) - if not isinstance(nformat, six.binary_type): + if not isinstance(nformat, bytes): nformat = nformat.encode("utf-8") _id.update(nformat) if name_qualifier: - if not isinstance(name_qualifier, six.binary_type): + if not isinstance(name_qualifier, bytes): name_qualifier = name_qualifier.encode("utf-8") _id.update(name_qualifier) if sp_name_qualifier: - if not isinstance(sp_name_qualifier, six.binary_type): + if not isinstance(sp_name_qualifier, bytes): sp_name_qualifier = sp_name_qualifier.encode("utf-8") _id.update(sp_name_qualifier) return _id.hexdigest() @@ -160,7 +160,7 @@ class IdentDB(object): if nformat == NAMEID_FORMAT_PERSISTENT: nameid = self.match_local_id(userid, sp_name_qualifier, name_qualifier) if nameid: - logger.debug("Found existing persistent NameId %s for user %s" % (nameid, userid)) + logger.debug(f"Found existing persistent NameId {nameid} for user {userid}") return nameid _id = self.create_id(nformat, name_qualifier, sp_name_qualifier) @@ -169,7 +169,7 @@ class IdentDB(object): if not self.domain: raise SAMLError("Can't issue email nameids, unknown domain") - _id = "%s@%s" % (_id, self.domain) + _id = f"{_id}@{self.domain}" nameid = NameID( format=nformat, diff --git a/src/saml2/mcache.py b/src/saml2/mcache.py index f89e616c..9540d0f0 100644 --- a/src/saml2/mcache.py +++ b/src/saml2/mcache.py @@ -16,10 +16,10 @@ logger = logging.getLogger(__name__) def _key(prefix, name): - return "%s_%s" % (prefix, name) + return f"{prefix}_{name}" -class Cache(object): +class Cache: def __init__(self, servers, debug=0): self._cache = memcache.Client(servers, debug) diff --git a/src/saml2/mdbcache.py b/src/saml2/mdbcache.py index 01b50e13..20215bb6 100644 --- a/src/saml2/mdbcache.py +++ b/src/saml2/mdbcache.py @@ -17,7 +17,7 @@ from saml2.time_util import TIME_FORMAT logger = logging.getLogger(__name__) -class Cache(object): +class Cache: def __init__(self, server=None, debug=0, db=None): if server: connection = MongoClient(server) diff --git a/src/saml2/mdie.py b/src/saml2/mdie.py index 9a741495..c5d30557 100644 --- a/src/saml2/mdie.py +++ b/src/saml2/mdie.py @@ -26,7 +26,7 @@ def _eval(val, onts, mdb_safe): :param onts: Schemas to be used in the conversion :return: The basic dictionary """ - if isinstance(val, six.string_types): + if isinstance(val, str): val = val.strip() if not val: return None @@ -57,7 +57,7 @@ def to_dict(_dict, onts, mdb_safe=False): """ res = {} if isinstance(_dict, SamlBase): - res["__class__"] = "%s&%s" % (_dict.c_namespace, _dict.c_tag) + res["__class__"] = f"{_dict.c_namespace}&{_dict.c_tag}" for key in _dict.keyswv(): if key in IMP_SKIP: continue @@ -67,7 +67,7 @@ def to_dict(_dict, onts, mdb_safe=False): _val = [_eval(_v, onts, mdb_safe) for _v in _eel] elif key == "extension_attributes": if mdb_safe: - _val = dict([(k.replace(".", "__"), v) for k, v in val.items()]) + _val = {k.replace(".", "__"): v for k, v in val.items()} # _val = {k.replace(".", "__"): v for k, v in val.items()} else: _val = val @@ -86,7 +86,7 @@ def to_dict(_dict, onts, mdb_safe=False): for _val in [_eval(val, onts, mdb_safe)] if _val } - res["__class__"] = "%s&%s" % (_dict.namespace, _dict.tag) + res["__class__"] = f"{_dict.namespace}&{_dict.tag}" else: for key, val in _dict.items(): _val = _eval(val, onts, mdb_safe) @@ -110,11 +110,11 @@ def _kwa(val, onts, mdb_safe=False): :return: A converted dictionary """ if not mdb_safe: - return dict([(k, from_dict(v, onts)) for k, v in val.items() if k not in EXP_SKIP]) + return {k: from_dict(v, onts) for k, v in val.items() if k not in EXP_SKIP} else: _skip = ["_id"] _skip.extend(EXP_SKIP) - return dict([(k.replace("__", "."), from_dict(v, onts)) for k, v in val.items() if k not in _skip]) + return {k.replace("__", "."): from_dict(v, onts) for k, v in val.items() if k not in _skip} def from_dict(val, onts, mdb_safe=False): @@ -151,7 +151,7 @@ def from_dict(val, onts, mdb_safe=False): key = key.replace("__", ".") res[key] = from_dict(v, onts) return res - elif isinstance(val, six.string_types): + elif isinstance(val, str): return val elif isinstance(val, list): return [from_dict(v, onts) for v in val] diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py index 3030f6a7..8dc9e636 100644 --- a/src/saml2/mdstore.py +++ b/src/saml2/mdstore.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import hashlib from hashlib import sha1 import importlib @@ -69,21 +67,21 @@ from saml2.validate import valid_instance logger = logging.getLogger(__name__) classnames = { - "mdattr_entityattributes": "{ns}&{tag}".format(ns=NS_MDATTR, tag=EntityAttributes.c_tag), - "algsupport_signing_method": "{ns}&{tag}".format(ns=NS_ALGSUPPORT, tag=SigningMethod.c_tag), - "algsupport_digest_method": "{ns}&{tag}".format(ns=NS_ALGSUPPORT, tag=DigestMethod.c_tag), - "mdui_uiinfo": "{ns}&{tag}".format(ns=NS_MDUI, tag=UIInfo.c_tag), - "mdui_uiinfo_display_name": "{ns}&{tag}".format(ns=NS_MDUI, tag=DisplayName.c_tag), - "mdui_uiinfo_description": "{ns}&{tag}".format(ns=NS_MDUI, tag=Description.c_tag), - "mdui_uiinfo_information_url": "{ns}&{tag}".format(ns=NS_MDUI, tag=InformationURL.c_tag), - "mdui_uiinfo_privacy_statement_url": "{ns}&{tag}".format(ns=NS_MDUI, tag=PrivacyStatementURL.c_tag), - "mdui_uiinfo_logo": "{ns}&{tag}".format(ns=NS_MDUI, tag=Logo.c_tag), - "service_artifact_resolution": "{ns}&{tag}".format(ns=NS_MD, tag=ArtifactResolutionService.c_tag), - "service_single_sign_on": "{ns}&{tag}".format(ns=NS_MD, tag=SingleSignOnService.c_tag), - "service_nameid_mapping": "{ns}&{tag}".format(ns=NS_MD, tag=NameIDMappingService.c_tag), - "mdrpi_registration_info": "{ns}&{tag}".format(ns=NS_MDRPI, tag=RegistrationInfo.c_tag), - "mdrpi_registration_policy": "{ns}&{tag}".format(ns=NS_MDRPI, tag=RegistrationPolicy.c_tag), - "shibmd_scope": "{ns}&{tag}".format(ns=NS_SHIBMD, tag=Scope.c_tag), + "mdattr_entityattributes": f"{NS_MDATTR}&{EntityAttributes.c_tag}", + "algsupport_signing_method": f"{NS_ALGSUPPORT}&{SigningMethod.c_tag}", + "algsupport_digest_method": f"{NS_ALGSUPPORT}&{DigestMethod.c_tag}", + "mdui_uiinfo": f"{NS_MDUI}&{UIInfo.c_tag}", + "mdui_uiinfo_display_name": f"{NS_MDUI}&{DisplayName.c_tag}", + "mdui_uiinfo_description": f"{NS_MDUI}&{Description.c_tag}", + "mdui_uiinfo_information_url": f"{NS_MDUI}&{InformationURL.c_tag}", + "mdui_uiinfo_privacy_statement_url": f"{NS_MDUI}&{PrivacyStatementURL.c_tag}", + "mdui_uiinfo_logo": f"{NS_MDUI}&{Logo.c_tag}", + "service_artifact_resolution": f"{NS_MD}&{ArtifactResolutionService.c_tag}", + "service_single_sign_on": f"{NS_MD}&{SingleSignOnService.c_tag}", + "service_nameid_mapping": f"{NS_MD}&{NameIDMappingService.c_tag}", + "mdrpi_registration_info": f"{NS_MDRPI}&{RegistrationInfo.c_tag}", + "mdrpi_registration_policy": f"{NS_MDRPI}&{RegistrationPolicy.c_tag}", + "shibmd_scope": f"{NS_SHIBMD}&{Scope.c_tag}", } ENTITY_CATEGORY = "http://macedir.org/entity-category" @@ -242,7 +240,7 @@ def repack_cert(cert): return "\n".join([s.strip() for s in part]) -class MetaData(object): +class MetaData: def __init__(self, attrc, metadata="", node_name=None, check_validity=True, security=None, **kwargs): self.attrc = attrc self.metadata = metadata @@ -380,7 +378,7 @@ class MetaData(object): for entid, item in self.items(): hit = False try: - descr = item["{}sso_descriptor".format(typ)] + descr = item[f"{typ}sso_descriptor"] except KeyError: continue else: @@ -507,7 +505,7 @@ class MetaData(object): class InMemoryMetaData(MetaData): def __init__(self, attrc, metadata="", node_name=None, check_validity=True, security=None, **kwargs): - super(InMemoryMetaData, self).__init__(attrc, metadata=metadata) + super().__init__(attrc, metadata=metadata) self.entity = {} self.security = security self.node_name = node_name @@ -696,7 +694,7 @@ class InMemoryMetaData(MetaData): try: for srv in ent[desc]: if "artifact_resolution_service" in srv: - if isinstance(eid, six.string_types): + if isinstance(eid, str): eid = eid.encode("utf-8") s = sha1(eid) res[s.digest()] = ent @@ -769,7 +767,7 @@ class MetaDataFile(InMemoryMetaData): """ def __init__(self, attrc, filename=None, cert=None, **kwargs): - super(MetaDataFile, self).__init__(attrc, **kwargs) + super().__init__(attrc, **kwargs) if not filename: raise SAMLError("No file specified.") self.filename = filename @@ -791,7 +789,7 @@ class MetaDataLoader(MetaDataFile): """ def __init__(self, attrc, loader_callable, cert=None, security=None, **kwargs): - super(MetaDataLoader, self).__init__(attrc, **kwargs) + super().__init__(attrc, **kwargs) self.metadata_provider_callable = self.get_metadata_loader(loader_callable) self.cert = cert self.security = security @@ -806,15 +804,15 @@ class MetaDataLoader(MetaDataFile): try: mod = importlib.import_module(module) except Exception as e: - raise RuntimeError('Cannot find metadata provider function %s: "%s"' % (func, e)) + raise RuntimeError(f'Cannot find metadata provider function {func}: "{e}"') try: metadata_loader = getattr(mod, attr) except AttributeError: - raise RuntimeError('Module "%s" does not define a "%s" metadata loader' % (module, attr)) + raise RuntimeError(f'Module "{module}" does not define a "{attr}" metadata loader') if not callable(metadata_loader): - raise RuntimeError("Metadata loader %s.%s must be callable" % (module, attr)) + raise RuntimeError(f"Metadata loader {module}.{attr} must be callable") return metadata_loader @@ -836,7 +834,7 @@ class MetaDataExtern(InMemoryMetaData): :params cert: CertificMDloaderate used to sign the metadata :params http: """ - super(MetaDataExtern, self).__init__(attrc, **kwargs) + super().__init__(attrc, **kwargs) if not url: raise SAMLError("URL not specified.") else: @@ -869,7 +867,7 @@ class MetaDataMD(InMemoryMetaData): """ def __init__(self, attrc, filename, **kwargs): - super(MetaDataMD, self).__init__(attrc, **kwargs) + super().__init__(attrc, **kwargs) self.filename = filename def load(self, *args, **kwargs): @@ -889,7 +887,7 @@ class MetaDataMDX(InMemoryMetaData): @staticmethod def sha1_entity_transform(entity_id): entity_id_sha1 = hashlib.sha1(entity_id.encode("utf-8")).hexdigest() - transform = "{{sha1}}{digest}".format(digest=entity_id_sha1) + transform = f"{{sha1}}{entity_id_sha1}" return transform def __init__( @@ -915,7 +913,7 @@ class MetaDataMDX(InMemoryMetaData): https://www.w3.org/TR/xmlschema-2/#duration :params http_client_timeout: timeout of http requests """ - super(MetaDataMDX, self).__init__(None, **kwargs) + super().__init__(None, **kwargs) if not url: raise SAMLError("URL for MDQ server not specified.") @@ -938,26 +936,26 @@ class MetaDataMDX(InMemoryMetaData): # <EntitiesDescriptor> element but we will not currently support # that use case since it is unlikely to be leveraged for most # flows. - self.node_name = "{ns}:{tag}".format(ns=EntityDescriptor.c_namespace, tag=EntityDescriptor.c_tag) + self.node_name = f"{EntityDescriptor.c_namespace}:{EntityDescriptor.c_tag}" def load(self, *args, **kwargs): # Do nothing pass def _fetch_metadata(self, item): - mdx_url = "{url}/entities/{id}".format(url=self.url, id=self.entity_transform(item)) + mdx_url = f"{self.url}/entities/{self.entity_transform(item)}" response = requests.get( mdx_url, headers={"Accept": SAML_METADATA_CONTENT_TYPE}, timeout=self.http_client_timeout ) if response.status_code != 200: - error_msg = "Fething {item}: Got response status {status}".format(item=item, status=response.status_code) + error_msg = f"Fething {item}: Got response status {response.status_code}" logger.warning(error_msg) raise KeyError(error_msg) _txt = response.content if not self.parse_and_check_signature(_txt): - error_msg = "Fething {item}: invalid signature".format(item=item) + error_msg = f"Fething {item}: invalid signature" logger.error(error_msg) raise KeyError(error_msg) @@ -972,7 +970,7 @@ class MetaDataMDX(InMemoryMetaData): if item not in self.entity: entity = self._fetch_metadata(item) elif not self._is_metadata_fresh(item): - msg = "Metadata for {} have expired; refreshing metadata".format(item) + msg = f"Metadata for {item} have expired; refreshing metadata" logger.info(msg) _ = self.entity.pop(item) entity = self._fetch_metadata(item) @@ -1282,7 +1280,10 @@ class MetadataStore(MetaData): if binding is None: binding = BINDING_DISCO return self.ext_service( - entity_id, "spsso_descriptor", "%s&%s" % (DiscoveryResponse.c_namespace, DiscoveryResponse.c_tag), binding + entity_id, + "spsso_descriptor", + f"{DiscoveryResponse.c_namespace}&{DiscoveryResponse.c_tag}", + binding, ) def attribute_requirement(self, entity_id, index=None): @@ -1646,7 +1647,7 @@ class MetadataStore(MetaData): def __str__(self): _str = ["{"] for key, val in self.metadata.items(): - _str.append("%s: %s" % (key, val)) + _str.append(f"{key}: {val}") _str.append("}") return "\n".join(_str) diff --git a/src/saml2/metadata.py b/src/saml2/metadata.py index f8aa2c9c..707e1d54 100644 --- a/src/saml2/metadata.py +++ b/src/saml2/metadata.py @@ -153,7 +153,7 @@ def do_organization_info(ava): for dkey, (ckey, klass) in ORG_ATTR_TRANSL.items(): if ckey not in ava: continue - if isinstance(ava[ckey], six.string_types): + if isinstance(ava[ckey], str): setattr(org, dkey, [_localized_name(ava[ckey], klass)]) elif isinstance(ava[ckey], list): setattr(org, dkey, [_localized_name(n, klass) for n in ava[ckey]]) @@ -210,7 +210,7 @@ def do_requested_attribute(attributes, acs, is_required="false", name_format=NAM for attr in attributes: attr = from_local_name(acs, attr, name_format) args = {} - if isinstance(attr, six.string_types): + if isinstance(attr, str): args["name"] = attr else: for key in attr.keyswv(): @@ -231,7 +231,7 @@ def do_uiinfo(_uiinfo): aclass = uii.child_class(attr) inst = getattr(uii, attr) - if isinstance(val, six.string_types): + if isinstance(val, str): ainst = aclass(text=val) inst.append(ainst) elif isinstance(val, dict): @@ -241,7 +241,7 @@ def do_uiinfo(_uiinfo): inst.append(ainst) else: for value in val: - if isinstance(value, six.string_types): + if isinstance(value, str): ainst = aclass(text=value) inst.append(ainst) elif isinstance(value, dict): @@ -281,7 +281,7 @@ def do_uiinfo(_uiinfo): if isinstance(val, list): for value in val: keyw = mdui.Keywords() - if isinstance(value, six.string_types): + if isinstance(value, str): keyw.text = value elif isinstance(value, dict): keyw.text = " ".join(value["text"]) @@ -369,7 +369,7 @@ def _do_nameid_format(cls, conf, typ): if not name_id_format: return - if isinstance(name_id_format, six.string_types): + if isinstance(name_id_format, str): name_id_format = [name_id_format] formats = [md.NameIDFormat(text=format) for format in name_id_format] @@ -384,7 +384,7 @@ def do_endpoints(conf, endpoints): servs = [] i = 1 for args in conf[endpoint]: - if isinstance(args, six.string_types): # Assume it's the location + if isinstance(args, str): # Assume it's the location args = {"location": args, "binding": DEFAULT_BINDING[endpoint]} elif isinstance(args, tuple) or isinstance(args, list): if len(args) == 2: # (location, binding) @@ -520,7 +520,7 @@ def do_spsso_descriptor(conf, cert=None, enc_cert=None): if val is None: setattr(spsso, key, DEFAULT[key]) # default ?! else: - strval = "{0:>s}".format(str(val)) + strval = f"{str(val):>s}" setattr(spsso, key, strval.lower()) except KeyError: setattr(spsso, key, DEFAULTS[key]) diff --git a/src/saml2/mongo_store.py b/src/saml2/mongo_store.py index cfadd1c3..230a0d09 100644 --- a/src/saml2/mongo_store.py +++ b/src/saml2/mongo_store.py @@ -38,7 +38,7 @@ def context_match(cfilter, cntx): return True -class SessionStorageMDB(object): +class SessionStorageMDB: """Session information is stored in a MongoDB database""" def __init__(self, database="", collection="assertion", **kwargs): @@ -193,7 +193,7 @@ class IdentMDB(IdentDB): return self.construct_nameid(_id, name_id_policy=name_id_policy) -class MDB(object): +class MDB: primary_key = "mdb" def __init__(self, database, collection, **kwargs): @@ -322,14 +322,14 @@ def protect(dic): res = {} for key, val in dic.items(): key = key.replace(".", "__") - if isinstance(val, six.string_types): + if isinstance(val, str): pass elif isinstance(val, dict): val = protect(val) elif isinstance(val, list): li = [] for va in val: - if isinstance(va, six.string_types): + if isinstance(va, str): pass elif isinstance(va, dict): va = protect(va) @@ -347,14 +347,14 @@ def unprotect(dic): pass else: key = key.replace("__", ".") - if isinstance(val, six.string_types): + if isinstance(val, str): pass elif isinstance(val, dict): val = unprotect(val) elif isinstance(val, list): li = [] for va in val: - if isinstance(va, six.string_types): + if isinstance(va, str): pass elif isinstance(val, dict): va = unprotect(va) @@ -377,7 +377,7 @@ def export_mdstore_to_mongo_db(mds, database, collection, sub_collection=""): class MetadataMDB(InMemoryMetaData): def __init__(self, attrc, database="", collection=""): - super(MetadataMDB, self).__init__(attrc) + super().__init__(attrc) self.mdb = MDB(database, collection) self.mdb.primary_key = "entity_id" diff --git a/src/saml2/pack.py b/src/saml2/pack.py index c065d2d3..f8328b5c 100644 --- a/src/saml2/pack.py +++ b/src/saml2/pack.py @@ -16,10 +16,10 @@ except Exception: import cgi as html import logging +from urllib.parse import urlencode +from urllib.parse import urlparse import six -from six.moves.urllib.parse import urlencode -from six.moves.urllib.parse import urlparse import saml2 from saml2.s_utils import deflate_and_base64_encode @@ -29,7 +29,7 @@ from saml2.xmldsig import SIG_ALLOWED_ALG try: - from xml.etree import cElementTree as ElementTree + from xml.etree import ElementTree as ElementTree if ElementTree.VERSION < "1.3.0": # cElementTree has no support for register_namespace @@ -89,9 +89,9 @@ def http_form_post_message(message, location, relay_state="", typ="SAMLRequest", :param relay_state: for preserving and conveying state information :return: A tuple containing header information and a HTML message. """ - if not isinstance(message, six.string_types): + if not isinstance(message, str): message = str(message) - if not isinstance(message, six.binary_type): + if not isinstance(message, bytes): message = message.encode("utf-8") if typ == "SAMLRequest" or typ == "SAMLResponse": @@ -122,9 +122,9 @@ def http_post_message(message, relay_state="", typ="SAMLRequest", **kwargs): :param relay_state: for preserving and conveying state information :return: A tuple containing header information and a HTML message. """ - if not isinstance(message, six.string_types): + if not isinstance(message, str): message = str(message) - if not isinstance(message, six.binary_type): + if not isinstance(message, bytes): message = message.encode("utf-8") if typ == "SAMLRequest" or typ == "SAMLResponse": @@ -166,8 +166,8 @@ def http_redirect_message( :return: A tuple containing header information and a HTML message. """ - if not isinstance(message, six.string_types): - message = "%s" % (message,) + if not isinstance(message, str): + message = f"{message}" _order = None if typ in ["SAMLRequest", "SAMLResponse"]: @@ -187,10 +187,10 @@ def http_redirect_message( if sign: # sigalgs, should be one defined in xmldsig if sigalg not in [long_name for short_name, long_name in SIG_ALLOWED_ALG]: - raise Exception("Signature algo not in allowed list: {algo}".format(algo=sigalg)) + raise Exception(f"Signature algo not in allowed list: {sigalg}") signer = backend.get_signer(sigalg) if sign and sigalg else None if not signer: - raise Exception("Could not init signer fro algo {algo}".format(algo=sigalg)) + raise Exception(f"Could not init signer fro algo {sigalg}") args["SigAlg"] = sigalg string = "&".join(urlencode({k: args[k]}) for k in _order if k in args) @@ -232,7 +232,7 @@ def make_soap_enveloped_saml_thingy(thingy, header_parts=None): body.tag = "{%s}Body" % NAMESPACE envelope.append(body) - if isinstance(thingy, six.string_types): + if isinstance(thingy, str): # remove the first XML version/encoding line if thingy[0:5].lower() == "<?xml": logger.debug("thingy0: %s", thingy) @@ -244,7 +244,7 @@ def make_soap_enveloped_saml_thingy(thingy, header_parts=None): _child.tag = "{%s}FuddleMuddle" % DUMMY_NAMESPACE body.append(_child) _str = ElementTree.tostring(envelope, encoding="UTF-8") - if isinstance(_str, six.binary_type): + if isinstance(_str, bytes): _str = _str.decode("utf-8") logger.debug("SOAP precursor: %s", _str) # find an remove the namespace definition @@ -252,7 +252,7 @@ def make_soap_enveloped_saml_thingy(thingy, header_parts=None): j = _str.rfind("xmlns:", 0, i) cut1 = _str[j : i + len(DUMMY_NAMESPACE) + 1] _str = _str.replace(cut1, "") - first = _str.find("<%s:FuddleMuddle" % (cut1[6:9],)) + first = _str.find(f"<{cut1[6:9]}:FuddleMuddle") last = _str.find(">", first + 14) cut2 = _str[first : last + 1] return _str.replace(cut2, thingy) @@ -287,9 +287,7 @@ def parse_soap_enveloped_saml(text, body_class, header_class=None): envelope_tag = "{%s}Envelope" % NAMESPACE if envelope.tag != envelope_tag: - raise ValueError( - "Invalid envelope tag '{invalid}' should be '{valid}'".format(invalid=envelope.tag, valid=envelope_tag) - ) + raise ValueError(f"Invalid envelope tag '{envelope.tag}' should be '{envelope_tag}'") # print(len(envelope)) body = None @@ -310,7 +308,7 @@ def parse_soap_enveloped_saml(text, body_class, header_class=None): # print(">>",sub.tag) for klass in header_class: # print("?{%s}%s" % (klass.c_namespace,klass.c_tag)) - if sub.tag == "{%s}%s" % (klass.c_namespace, klass.c_tag): + if sub.tag == f"{{{klass.c_namespace}}}{klass.c_tag}": header[sub.tag] = saml2.create_class_from_element_tree(klass, sub) break diff --git a/src/saml2/population.py b/src/saml2/population.py index 72baa830..8c066d63 100644 --- a/src/saml2/population.py +++ b/src/saml2/population.py @@ -8,10 +8,10 @@ from saml2.cache import Cache logger = logging.getLogger(__name__) -class Population(object): +class Population: def __init__(self, cache=None): if cache: - if isinstance(cache, six.string_types): + if isinstance(cache, str): self.cache = Cache(cache) else: self.cache = cache diff --git a/src/saml2/request.py b/src/saml2/request.py index 1d62d0ed..6b8474e6 100644 --- a/src/saml2/request.py +++ b/src/saml2/request.py @@ -18,7 +18,7 @@ def _dummy(data, **_arg): return "" -class Request(object): +class Request: def __init__(self, sec_context, receiver_addrs, attribute_converters=None, timeslack=0): self.sec = sec_context self.receiver_addrs = receiver_addrs @@ -126,9 +126,7 @@ class Request(object): def _verify(self): valid_version = "2.0" if self.message.version != valid_version: - raise VersionMismatch( - "Invalid version {invalid} should be {valid}".format(invalid=self.message.version, valid=valid_version) - ) + raise VersionMismatch(f"Invalid version {self.message.version} should be {valid_version}") if self.message.destination and self.receiver_addrs and self.message.destination not in self.receiver_addrs: logger.error("%s not in %s", self.message.destination, self.receiver_addrs) diff --git a/src/saml2/response.py b/src/saml2/response.py index 403bb72b..d6500336 100644 --- a/src/saml2/response.py +++ b/src/saml2/response.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # import calendar import logging @@ -220,7 +219,7 @@ def for_me(conditions, myself): if audience.text and audience.text.strip() == myself: return True else: - logger.debug("AudienceRestriction - One condition not satisfied: %s != %s" % (audience.text, myself)) + logger.debug(f"AudienceRestriction - One condition not satisfied: {audience.text} != {myself}") logger.debug("AudienceRestrictions not satisfied!") return False @@ -277,7 +276,7 @@ def attribute_response(conf, return_addrs, timeslack=0, asynchop=False, test=Fal ) -class StatusResponse(object): +class StatusResponse: msgtype = "status_response" def __init__(self, sec_context, return_addrs=None, timeslack=0, request_id=0, asynchop=True, conv_info=None): @@ -344,7 +343,7 @@ class StatusResponse(object): def _loads(self, xmldata, decode=True, origxml=None): # own copy - if isinstance(xmldata, six.binary_type): + if isinstance(xmldata, bytes): self.xmlstr = xmldata[:].decode("utf-8") else: self.xmlstr = xmldata[:] @@ -365,7 +364,7 @@ class StatusResponse(object): origdoc=origxml, must=self.require_signature, require_response_signature=self.require_response_signature, - **args + **args, ) except TypeError: @@ -389,7 +388,7 @@ class StatusResponse(object): err_msg = status.status_message.text if status.status_message else err_code or "Unknown error" err_cls = STATUSCODE2EXCEPTION.get(err_code, StatusError) - msg = "Unsuccessful operation: {status}\n{msg} from {code}".format(status=status, msg=err_msg, code=err_code) + msg = f"Unsuccessful operation: {status}\n{err_msg} from {err_code}" logger.debug(msg) raise err_cls(msg) @@ -493,7 +492,7 @@ class AuthnResponse(StatusResponse): want_assertions_or_response_signed=False, want_response_signed=False, conv_info=None, - **kwargs + **kwargs, ): StatusResponse.__init__(self, sec_context, return_addrs, timeslack, asynchop=asynchop, conv_info=conv_info) @@ -561,7 +560,7 @@ class AuthnResponse(StatusResponse): if optional: return True else: - msg = "Invalid number of AuthnStatement found in Response: {n}".format(n=n_authn_statements) + msg = f"Invalid number of AuthnStatement found in Response: {n_authn_statements}" raise ValueError(msg) authn_statement = self.assertion.authn_statement[0] @@ -675,7 +674,7 @@ class AuthnResponse(StatusResponse): if _assertion.attribute_statement: logger.debug("Assertion contains %s attribute statement(s)", (len(self.assertion.attribute_statement))) for _attr_statem in _assertion.attribute_statement: - logger.debug("Attribute Statement: %s" % (_attr_statem,)) + logger.debug(f"Attribute Statement: {_attr_statem}") ava.update(self.read_attribute_statement(_attr_statem)) if not ava: logger.debug("Assertion contains no attribute statements") @@ -732,7 +731,7 @@ class AuthnResponse(StatusResponse): raise ValueError("Missing assertion") if not self.assertion.subject: - raise ValueError("Invalid assertion subject: {subject}".format(subject=self.assertion.subject)) + raise ValueError(f"Invalid assertion subject: {self.assertion.subject}") subject = self.assertion.subject subjconf = [] @@ -751,7 +750,7 @@ class AuthnResponse(StatusResponse): elif subject_confirmation.method == SCM_SENDER_VOUCHES: pass else: - raise ValueError("Unknown subject confirmation method: %s" % (subject_confirmation.method,)) + raise ValueError(f"Unknown subject confirmation method: {subject_confirmation.method}") _recip = _data.recipient if not _recip or not self.verify_recipient(_recip): @@ -914,9 +913,7 @@ class AuthnResponse(StatusResponse): n_assertions = len(self.response.assertion) n_assertions_enc = len(self.response.encrypted_assertion) if n_assertions != 1 and n_assertions_enc != 1 and self.assertion is None: - raise InvalidAssertion( - "Invalid number of assertions in Response: {n}".format(n=n_assertions + n_assertions_enc) - ) + raise InvalidAssertion(f"Invalid number of assertions in Response: {n_assertions + n_assertions_enc}") if self.response.assertion: logger.debug("***Unencrypted assertion***") @@ -941,7 +938,7 @@ class AuthnResponse(StatusResponse): resp = samlp.response_from_string(decr_text) # check and prepare for comparison between str and unicode if type(decr_text_old) != type(decr_text): - if isinstance(decr_text_old, six.binary_type): + if isinstance(decr_text_old, bytes): decr_text_old = decr_text_old.decode("utf-8") else: decr_text_old = decr_text_old.encode("utf-8") @@ -962,7 +959,7 @@ class AuthnResponse(StatusResponse): _enc_assertions = self.decrypt_assertions(resp.encrypted_assertion, decr_text, verified=True) # check and prepare for comparison between str and unicode if type(decr_text_old) != type(decr_text): - if isinstance(decr_text_old, six.binary_type): + if isinstance(decr_text_old, bytes): decr_text_old = decr_text_old.decode("utf-8") else: decr_text_old = decr_text_old.encode("utf-8") @@ -1350,7 +1347,7 @@ def response_factory( # A class of it's own -class AssertionIDResponse(object): +class AssertionIDResponse: msgtype = "assertion_id_response" def __init__(self, sec_context, attribute_converters, timeslack=0, **kwargs): diff --git a/src/saml2/s2repoze/__init__.py b/src/saml2/s2repoze/__init__.py index 766c3cb1..1b3570ab 100644 --- a/src/saml2/s2repoze/__init__.py +++ b/src/saml2/s2repoze/__init__.py @@ -1,2 +1 @@ -# -*- coding: utf-8 -*- # Created by Roland Hedberg diff --git a/src/saml2/s2repoze/plugins/__init__.py b/src/saml2/s2repoze/plugins/__init__.py index 40a96afc..e69de29b 100644 --- a/src/saml2/s2repoze/plugins/__init__.py +++ b/src/saml2/s2repoze/plugins/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/src/saml2/s2repoze/plugins/entitlement.py b/src/saml2/s2repoze/plugins/entitlement.py index bd5fae51..81bc0cc5 100644 --- a/src/saml2/s2repoze/plugins/entitlement.py +++ b/src/saml2/s2repoze/plugins/entitlement.py @@ -6,7 +6,7 @@ from repoze.who.interfaces import IMetadataProvider from zope.interface import implements -class EntitlementMetadataProvider(object): +class EntitlementMetadataProvider: implements(IMetadataProvider) @@ -63,13 +63,13 @@ class EntitlementMetadataProvider(object): vorg = environ["myapp.vo"] try: ents = user["entitlement"][vorg] - identity["user"] = {"entitlement": ["%s:%s" % (vorg, e) for e in ents]} + identity["user"] = {"entitlement": [f"{vorg}:{e}" for e in ents]} except KeyError: pass except KeyError: res = [] for vorg, ents in user["entitlement"].items(): - res.extend(["%s:%s" % (vorg, e) for e in ents]) + res.extend([f"{vorg}:{e}" for e in ents]) identity["user"] = res diff --git a/src/saml2/s2repoze/plugins/formswithhidden.py b/src/saml2/s2repoze/plugins/formswithhidden.py index 02e2ad95..1483af92 100644 --- a/src/saml2/s2repoze/plugins/formswithhidden.py +++ b/src/saml2/s2repoze/plugins/formswithhidden.py @@ -1,3 +1,5 @@ +from urllib.parse import urlencode + from paste.httpexceptions import HTTPFound from paste.httpheaders import CONTENT_LENGTH from paste.httpheaders import CONTENT_TYPE @@ -8,7 +10,6 @@ from paste.request import parse_formvars from repoze.who.interfaces import IChallenger from repoze.who.interfaces import IIdentifier from repoze.who.plugins.form import FormPlugin -from six.moves.urllib.parse import urlencode from zope.interface import implements @@ -114,7 +115,7 @@ def make_plugin(login_form_qs="__do_login", rememberer_name=None, form=None): if rememberer_name is None: raise ValueError("must include rememberer key (name of another IIdentifier plugin)") if form is not None: - with open(form, "r") as f: + with open(form) as f: form = f.read() plugin = FormHiddenPlugin(login_form_qs, rememberer_name, form) return plugin diff --git a/src/saml2/s2repoze/plugins/ini.py b/src/saml2/s2repoze/plugins/ini.py index 74dcc111..ef36c6f4 100644 --- a/src/saml2/s2repoze/plugins/ini.py +++ b/src/saml2/s2repoze/plugins/ini.py @@ -5,7 +5,7 @@ from repoze.who.interfaces import IMetadataProvider from zope.interface import implements -class INIMetadataProvider(object): +class INIMetadataProvider: implements(IMetadataProvider) diff --git a/src/saml2/s2repoze/plugins/sp.py b/src/saml2/s2repoze/plugins/sp.py index ea3cd388..e3d25bfe 100644 --- a/src/saml2/s2repoze/plugins/sp.py +++ b/src/saml2/s2repoze/plugins/sp.py @@ -5,11 +5,13 @@ and SAML2 attribute aggregations as metadata collector in your WSGI application. """ +from io import StringIO import logging import platform import shelve import sys import traceback +from urllib import parse from paste.httpexceptions import HTTPInternalServerError from paste.httpexceptions import HTTPNotImplemented @@ -22,8 +24,6 @@ from repoze.who.interfaces import IChallenger from repoze.who.interfaces import IIdentifier from repoze.who.interfaces import IMetadataProvider import six -from six import StringIO -from six.moves.urllib import parse from zope.interface import implementer from saml2 import BINDING_HTTP_POST @@ -50,7 +50,7 @@ from saml2.samlp import Extensions logger = logging.getLogger(__name__) -PAOS_HEADER_INFO = 'ver="%s";"%s"' % (paos.NAMESPACE, ECP_SERVICE) +PAOS_HEADER_INFO = f'ver="{paos.NAMESPACE}";"{ECP_SERVICE}"' def construct_came_from(environ): @@ -66,11 +66,11 @@ def construct_came_from(environ): def exception_trace(tag, exc, log): message = traceback.format_exception(*sys.exc_info()) - log.error("[%s] ExcList: %s" % (tag, "".join(message))) - log.error("[%s] Exception: %s" % (tag, exc)) + log.error("[{}] ExcList: {}".format(tag, "".join(message))) + log.error(f"[{tag}] Exception: {exc}") -class ECP_response(object): +class ECP_response: code = 200 title = "OK" @@ -79,12 +79,12 @@ class ECP_response(object): # noinspection PyUnusedLocal def __call__(self, environ, start_response): - start_response("%s %s" % (self.code, self.title), [("Content-Type", "text/xml")]) + start_response(f"{self.code} {self.title}", [("Content-Type", "text/xml")]) return [self.content] @implementer(IChallenger, IIdentifier, IAuthenticator, IMetadataProvider) -class SAML2Plugin(object): +class SAML2Plugin: def __init__( self, rememberer_name, @@ -163,7 +163,7 @@ class SAML2Plugin(object): sid_ = sid() self.outstanding_queries[sid_] = came_from logger.info("Redirect to WAYF function: %s", self.wayf) - return -1, HTTPSeeOther(headers=[("Location", "%s?%s" % (self.wayf, sid_))]) + return -1, HTTPSeeOther(headers=[("Location", f"{self.wayf}?{sid_}")]) # noinspection PyUnusedLocal def _pick_idp(self, environ, came_from): @@ -409,12 +409,12 @@ class SAML2Plugin(object): ) except Exception as excp: - logger.exception("Exception: %s" % (excp,)) + logger.exception(f"Exception: {excp}") raise session_info = authresp.session_info() except TypeError as excp: - logger.exception("Exception: %s" % (excp,)) + logger.exception(f"Exception: {excp}") return None if session_info["came_from"]: @@ -553,7 +553,7 @@ class SAML2Plugin(object): def add_metadata(self, environ, identity): """Add information to the knowledge I have about the user""" name_id = identity["repoze.who.userid"] - if isinstance(name_id, six.string_types): + if isinstance(name_id, str): try: # Make sure that userids authenticated by another plugin # don't cause problems here. diff --git a/src/saml2/s_utils.py b/src/saml2/s_utils.py index 6e2fd9c2..1df61572 100644 --- a/src/saml2/s_utils.py +++ b/src/saml2/s_utils.py @@ -160,7 +160,7 @@ def deflate_and_base64_encode(string_val): :param string_val: The string to deflate and encode :return: The deflated and encoded string """ - if not isinstance(string_val, six.binary_type): + if not isinstance(string_val, bytes): string_val = string_val.encode("utf-8") return base64.b64encode(zlib.compress(string_val)[2:-4]) @@ -183,7 +183,7 @@ def rndbytes(size=16, alphabet=""): Returns rndstr always as a binary type """ x = rndstr(size, alphabet) - if isinstance(x, six.string_types): + if isinstance(x, str): return x.encode("utf-8") return x @@ -307,7 +307,7 @@ def _attrval(val, typ=""): def do_ava(val, typ=""): - if isinstance(val, six.string_types): + if isinstance(val, str): ava = saml.AttributeValue() ava.set_text(val) attrval = [ava] @@ -335,7 +335,7 @@ def do_attribute(val, typ, key): if attrval: attr.attribute_value = attrval - if isinstance(key, six.string_types): + if isinstance(key, str): attr.name = key elif isinstance(key, tuple): # 3-tuple or 2-tuple try: @@ -391,11 +391,11 @@ def factory(klass, **kwargs): def signature(secret, parts): """Generates a signature. All strings are assumed to be utf-8""" - if not isinstance(secret, six.binary_type): + if not isinstance(secret, bytes): secret = secret.encode("utf-8") newparts = [] for part in parts: - if not isinstance(part, six.binary_type): + if not isinstance(part, bytes): part = part.encode("utf-8") newparts.append(part) parts = newparts diff --git a/src/saml2/saml.py b/src/saml2/saml.py index d8ac648b..d3cca114 100644 --- a/src/saml2/saml.py +++ b/src/saml2/saml.py @@ -231,7 +231,7 @@ class AttributeValueBase(SamlBase): raise ValueError(msg) # only work with six.string_types - if isinstance(value, six.binary_type): + if isinstance(value, bytes): value = value.decode("utf-8") type_to_xsd = { @@ -344,7 +344,7 @@ class AttributeValueBase(SamlBase): _wrong_type_value(xsd=xsd_type, value=value) text = to_text(value) - self.set_type("{ns}:{type}".format(ns=xsd_ns, type=xsd_type) if xsd_ns else xsd_type if xsd_type else "") + self.set_type(f"{xsd_ns}:{xsd_type}" if xsd_ns else xsd_type if xsd_type else "") SamlBase.__setattr__(self, "text", text) return self diff --git a/src/saml2/sdb.py b/src/saml2/sdb.py index 4349b310..df9613a4 100644 --- a/src/saml2/sdb.py +++ b/src/saml2/sdb.py @@ -15,7 +15,7 @@ def context_match(cfilter, cntx): # The key to the stored authn statement is placed encrypted in the cookie -class SessionStorage(object): +class SessionStorage: """In memory storage of session information""" def __init__(self): diff --git a/src/saml2/server.py b/src/saml2/server.py index 2b1cbf66..153ea91b 100644 --- a/src/saml2/server.py +++ b/src/saml2/server.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # """Contains classes and functions that a SAML2.0 Identity provider (IdP) @@ -110,7 +109,7 @@ class Server(Entity): _spec = self.config.getattr("session_storage", "idp") if not _spec: return SessionStorage() - elif isinstance(_spec, six.string_types): + elif isinstance(_spec, str): if _spec.lower() == "memory": return SessionStorage() else: # Should be tuple @@ -137,7 +136,7 @@ class Server(Entity): typ = "" if not dbspec: idb = {} - elif isinstance(dbspec, six.string_types): + elif isinstance(dbspec, str): idb = _shelve_compat(dbspec, writeback=True, protocol=2) else: # database spec is a a 2-tuple (type, address) # print(>> sys.stderr, "DBSPEC: %s" % (dbspec,)) @@ -164,7 +163,7 @@ class Server(Entity): elif idb is not None: self.ident = IdentDB(idb) elif dbspec: - raise Exception("Couldn't open identity database: %s" % (dbspec,)) + raise Exception(f"Couldn't open identity database: {dbspec}") try: _domain = self.config.getattr("domain", "idp") @@ -404,7 +403,7 @@ class Server(Entity): if authn: # expected to be a dictionary # Would like to use dict comprehension but ... - authn_args = dict([(AUTHN_DICT_MAP[k], v) for k, v in authn.items() if k in AUTHN_DICT_MAP]) + authn_args = {AUTHN_DICT_MAP[k]: v for k, v in authn.items() if k in AUTHN_DICT_MAP} authn_args.update(kwargs) assertion = ast.construct( @@ -848,7 +847,7 @@ class Server(Entity): pefim=pefim, **kwargs, ) - except IOError as exc: + except OSError as exc: response = self.create_error_response( in_response_to, destination=destination, diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index b8004ce4..06836cf6 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -27,9 +27,10 @@ if sys.version_info[:2] >= (3, 9): else: from importlib_resources import files as _resource_files +from urllib import parse + from OpenSSL import crypto import pytz -from six.moves.urllib import parse from saml2 import ExtensionElement from saml2 import SamlBase @@ -204,7 +205,7 @@ def get_xmlsec_binary(paths=None): except OSError: pass - raise SigverError("Cannot find {binary}".format(binary=bin_name)) + raise SigverError(f"Cannot find {bin_name}") def _get_xmlsec_cryptobackend(path=None, search_paths=None, delete_tmpfiles=True): @@ -326,7 +327,7 @@ def signed_instance_factory(instance, seccont, elements_to_sign=None): return instance signed_xml = instance - if not isinstance(instance, six.string_types): + if not isinstance(instance, str): signed_xml = instance.to_string() for (node_name, nodeid) in elements_to_sign: @@ -353,7 +354,7 @@ def make_temp(content, suffix="", decode=True, delete_tmpfiles=True): close the file) and filename (which is for instance needed by the xmlsec function). """ - content_encoded = content.encode("utf-8") if not isinstance(content, six.binary_type) else content + content_encoded = content.encode("utf-8") if not isinstance(content, bytes) else content content_raw = base64.b64decode(content_encoded) if decode else content_encoded ntf = NamedTemporaryFile(suffix=suffix, delete=delete_tmpfiles) ntf.write(content_raw) @@ -492,7 +493,7 @@ def sha1_digest(msg): return hashlib.sha1(msg).digest() -class Signer(object): +class Signer: """Abstract base class for signing algorithms.""" def __init__(self, key): @@ -540,7 +541,7 @@ RESP_ORDER = [ ] -class RSACrypto(object): +class RSACrypto: def __init__(self, key): self.key = key @@ -594,7 +595,7 @@ def verify_redirect_signature(saml_msg, crypto, cert=None, sigkey=None): return bool(signer.verify(string, _sign, _key)) -class CryptoBackend(object): +class CryptoBackend: def version(self): raise NotImplementedError() @@ -614,9 +615,7 @@ class CryptoBackend(object): raise NotImplementedError() -ASSERT_XPATH = "".join( - ["/*[local-name()='{name}']".format(name=n) for n in ["Response", "EncryptedAssertion", "Assertion"]] -) +ASSERT_XPATH = "".join([f"/*[local-name()='{n}']" for n in ["Response", "EncryptedAssertion", "Assertion"]]) class CryptoBackendXmlSec1(CryptoBackend): @@ -629,7 +628,7 @@ class CryptoBackendXmlSec1(CryptoBackend): def __init__(self, xmlsec_binary, delete_tmpfiles=True, **kwargs): CryptoBackend.__init__(self, **kwargs) - if not isinstance(xmlsec_binary, six.string_types): + if not isinstance(xmlsec_binary, str): raise ValueError("xmlsec_binary should be of type string") self.xmlsec = xmlsec_binary self.delete_tmpfiles = delete_tmpfiles @@ -678,7 +677,7 @@ class CryptoBackendXmlSec1(CryptoBackend): try: (_stdout, _stderr, output) = self._run_xmlsec(com_list, [template]) except XmlsecError as e: - six.raise_from(EncryptError(com_list), e) + raise EncryptError(com_list) from e return output @@ -721,7 +720,7 @@ class CryptoBackendXmlSec1(CryptoBackend): try: (_stdout, _stderr, output) = self._run_xmlsec(com_list, [tmp2.name]) except XmlsecError as e: - six.raise_from(EncryptError(com_list), e) + raise EncryptError(com_list) from e return output.decode("utf-8") @@ -748,7 +747,7 @@ class CryptoBackendXmlSec1(CryptoBackend): try: (_stdout, _stderr, output) = self._run_xmlsec(com_list, [tmp.name]) except XmlsecError as e: - six.raise_from(DecryptError(com_list), e) + raise DecryptError(com_list) from e return output.decode("utf-8") @@ -802,7 +801,7 @@ class CryptoBackendXmlSec1(CryptoBackend): :param node_id: The identifier of the node :return: Boolean True if the signature was correct otherwise False. """ - if not isinstance(signedtext, six.binary_type): + if not isinstance(signedtext, bytes): signedtext = signedtext.encode("utf-8") tmp = make_temp(signedtext, suffix=".xml", decode=False, delete_tmpfiles=self.delete_tmpfiles) @@ -814,7 +813,7 @@ class CryptoBackendXmlSec1(CryptoBackend): "empty,same-doc", "--enabled-key-data", "raw-x509-cert", - "--pubkey-cert-{type}".format(type=cert_type), + f"--pubkey-cert-{cert_type}", cert_file, "--id-attr:ID", node_name, @@ -826,7 +825,7 @@ class CryptoBackendXmlSec1(CryptoBackend): try: (_stdout, stderr, _output) = self._run_xmlsec(com_list, [tmp.name]) except XmlsecError as e: - six.raise_from(SignatureError(com_list), e) + raise SignatureError(com_list) from e return parse_xmlsec_output(stderr) @@ -900,7 +899,7 @@ class CryptoBackendXMLSecurity(CryptoBackend): xml = xmlsec.parse_xml(statement) signed = xmlsec.sign(xml, key_file) signed_str = lxml.etree.tostring(signed, xml_declaration=False, encoding="UTF-8") - if not isinstance(signed_str, six.string_types): + if not isinstance(signed_str, str): signed_str = signed_str.decode("utf-8") return signed_str @@ -970,7 +969,7 @@ def security_context(conf): try: rsa_key = import_rsa_key_from_file(_file_name) except Exception as err: - logger.error("Cannot import key from {file}: {err_msg}".format(file=_file_name, err_msg=err)) + logger.error(f"Cannot import key from {_file_name}: {err}") raise else: sec_backend = RSACrypto(rsa_key) @@ -1029,7 +1028,7 @@ def encrypt_cert_from_item(item): return _encrypt_cert -class CertHandlerExtra(object): +class CertHandlerExtra: def __init__(self): pass @@ -1048,7 +1047,7 @@ class CertHandlerExtra(object): # Excepts to return True/False -class CertHandler(object): +class CertHandler: def __init__( self, security_context, @@ -1164,7 +1163,7 @@ class CertHandler(object): # How to get a rsa pub key fingerprint from a certificate # openssl x509 -inform pem -noout -in server.crt -pubkey > publickey.pem # openssl rsa -inform pem -noout -in publickey.pem -pubin -modulus -class SecurityContext(object): +class SecurityContext: my_cert = None def __init__( @@ -1292,7 +1291,7 @@ class SecurityContext(object): keys = [keys] keys_filtered = (key for key in keys if key) - keys_encoded = (key.encode("ascii") if not isinstance(key, six.binary_type) else key for key in keys_filtered) + keys_encoded = (key.encode("ascii") if not isinstance(key, bytes) else key for key in keys_filtered) key_files = list(make_temp(key, decode=False, delete_tmpfiles=self.delete_tmpfiles) for key in keys_encoded) key_file_names = list(tmp.name for tmp in key_files) @@ -1369,7 +1368,7 @@ class SecurityContext(object): certs = [] for cert_name, cert in _certs: - if isinstance(cert, six.string_types): + if isinstance(cert, str): content = pem_format(cert) tmp = make_temp(content, suffix=".pem", decode=False, delete_tmpfiles=self.delete_tmpfiles) certs.append(tmp) @@ -1422,9 +1421,9 @@ class SecurityContext(object): and references[0].uri.startswith("#") and len(references[0].uri) > 1 ) - the_anchor_points_to_the_enclosing_element_ID_attribute = the_URI_attribute_contains_an_anchor and references[ - 0 - ].uri == "#{id}".format(id=item.id) + the_anchor_points_to_the_enclosing_element_ID_attribute = ( + the_URI_attribute_contains_an_anchor and references[0].uri == f"#{item.id}" + ) # SAML implementations SHOULD use Exclusive Canonicalization, # with or without comments @@ -1541,13 +1540,13 @@ class SecurityContext(object): :return: """ - attr = "{type}_from_string".format(type=msgtype) + attr = f"{msgtype}_from_string" _func = getattr(saml, attr, None) _func = getattr(samlp, attr, _func) msg = _func(decoded_xml) if not msg: - raise TypeError("Not a {type}".format(type=msgtype)) + raise TypeError(f"Not a {msgtype}") if not msg.signature: if must: @@ -1778,7 +1777,7 @@ def pre_signature_part( digest_method = ds.DigestMethod(algorithm=digest_alg) reference = ds.Reference( - uri="#{id}".format(id=ident), digest_value=ds.DigestValue(), transforms=transforms, digest_method=digest_method + uri=f"#{ident}", digest_value=ds.DigestValue(), transforms=transforms, digest_method=digest_method ) signed_info = ds.SignedInfo( @@ -1788,7 +1787,7 @@ def pre_signature_part( signature = ds.Signature(signed_info=signed_info, signature_value=ds.SignatureValue()) if identifier: - signature.id = "Signature{n}".format(n=identifier) + signature.id = f"Signature{identifier}" # XXX remove - do not embed the cert if public_key: @@ -1836,8 +1835,8 @@ def pre_encryption_part( encrypted_data_id=None, encrypt_cert=None, ): - ek_id = encrypted_key_id or "EK_{id}".format(id=gen_random_key()) - ed_id = encrypted_data_id or "ED_{id}".format(id=gen_random_key()) + ek_id = encrypted_key_id or f"EK_{gen_random_key()}" + ed_id = encrypted_data_id or f"ED_{gen_random_key()}" msg_encryption_method = EncryptionMethod(algorithm=msg_enc) key_encryption_method = EncryptionMethod(algorithm=key_enc) diff --git a/src/saml2/soap.py b/src/saml2/soap.py index dc2e75e5..866bff14 100644 --- a/src/saml2/soap.py +++ b/src/saml2/soap.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # """ @@ -14,7 +13,7 @@ from saml2.schema import soapenv try: - from xml.etree import cElementTree as ElementTree + from xml.etree import ElementTree as ElementTree except ImportError: try: import cElementTree as ElementTree @@ -138,9 +137,7 @@ def parse_soap_enveloped_saml_thingy(text, expected_tags): envelope_tag = "{%s}Envelope" % soapenv.NAMESPACE if envelope.tag != envelope_tag: - raise ValueError( - "Invalid envelope tag '{invalid}' should be '{valid}'".format(invalid=envelope.tag, valid=envelope_tag) - ) + raise ValueError(f"Invalid envelope tag '{envelope.tag}' should be '{envelope_tag}'") if len(envelope) < 1: raise Exception("No items in envelope.") @@ -150,7 +147,7 @@ def parse_soap_enveloped_saml_thingy(text, expected_tags): if part.tag == "{%s}Body" % soapenv.NAMESPACE: n_children = len(part) if n_children != 1: - raise Exception("Expected a single child element, found {n}".format(n=n_children)) + raise Exception(f"Expected a single child element, found {n_children}") body = part break @@ -161,7 +158,7 @@ def parse_soap_enveloped_saml_thingy(text, expected_tags): if saml_part.tag in expected_tags: return ElementTree.tostring(saml_part, encoding="UTF-8") else: - raise WrongMessageType("Was '%s' expected one of %s" % (saml_part.tag, expected_tags)) + raise WrongMessageType(f"Was '{saml_part.tag}' expected one of {expected_tags}") NS_AND_TAG = re.compile(r"\{([^}]+)\}(.*)") @@ -177,7 +174,7 @@ def instanciate_class(item, modules): return create_class_from_element_tree(target, item) except KeyError: continue - raise Exception("Unknown class: ns='%s', tag='%s'" % (ns, tag)) + raise Exception(f"Unknown class: ns='{ns}', tag='{tag}'") def class_instances_from_soap_enveloped_saml_thingies(text, modules): @@ -195,9 +192,7 @@ def class_instances_from_soap_enveloped_saml_thingies(text, modules): envelope_tag = "{%s}Envelope" % soapenv.NAMESPACE if envelope.tag != envelope_tag: - raise ValueError( - "Invalid envelope tag '{invalid}' should be '{valid}'".format(invalid=envelope.tag, valid=envelope_tag) - ) + raise ValueError(f"Invalid envelope tag '{envelope.tag}' should be '{envelope_tag}'") if len(envelope) < 1: raise Exception("No items in envelope.") @@ -229,9 +224,7 @@ def open_soap_envelope(text): envelope_tag = "{%s}Envelope" % soapenv.NAMESPACE if envelope.tag != envelope_tag: - raise ValueError( - "Invalid envelope tag '{invalid}' should be '{valid}'".format(invalid=envelope.tag, valid=envelope_tag) - ) + raise ValueError(f"Invalid envelope tag '{envelope.tag}' should be '{envelope_tag}'") if len(envelope) < 1: raise Exception("No items in envelope.") diff --git a/src/saml2/time_util.py b/src/saml2/time_util.py index c85fdf73..2ef2adb6 100644 --- a/src/saml2/time_util.py +++ b/src/saml2/time_util.py @@ -1,11 +1,9 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # """ Implements some usefull functions when dealing with validity of different types of information. """ -from __future__ import print_function import calendar from datetime import datetime @@ -71,7 +69,7 @@ def parse_duration(duration): raise ValueError("Parse Duration is not valid.") index += 1 - dic = dict([(typ, 0) for (code, typ) in D_FORMAT if typ]) + dic = {typ: 0 for (code, typ) in D_FORMAT if typ} dlen = len(duration) for code, typ in D_FORMAT: @@ -239,7 +237,7 @@ def str_to_time(timestr, format=TIME_FORMAT): try: elem = TIME_FORMAT_WITH_FRAGMENT.match(timestr) except Exception as exc: - print("Exception: %s on %s" % (exc, timestr), file=sys.stderr) + print(f"Exception: {exc} on {timestr}", file=sys.stderr) raise then = time.strptime(elem.groups()[0] + "Z", TIME_FORMAT) @@ -272,7 +270,7 @@ def before(point): if not point: return True - if isinstance(point, six.string_types): + if isinstance(point, str): point = str_to_time(point) elif isinstance(point, int): point = time.gmtime(point) @@ -304,12 +302,12 @@ def utc_time_sans_frac(): def later_than(after, before): """True if then is later or equal to that""" - if isinstance(after, six.string_types): + if isinstance(after, str): after = str_to_time(after) elif isinstance(after, int): after = time.gmtime(after) - if isinstance(before, six.string_types): + if isinstance(before, str): before = str_to_time(before) elif isinstance(before, int): before = time.gmtime(before) diff --git a/src/saml2/tools/parse_xsd2.py b/src/saml2/tools/parse_xsd2.py index db775548..ce9f433f 100644 --- a/src/saml2/tools/parse_xsd2.py +++ b/src/saml2/tools/parse_xsd2.py @@ -13,7 +13,7 @@ import six __version__ = 0.5 -from xml.etree import cElementTree as ElementTree +from xml.etree import ElementTree as ElementTree INDENT = 4 * " " @@ -65,9 +65,9 @@ def def_init(imports, attributes): _name = elem[0] if elem[2]: - line.append("%s%s='%s'," % (indent3, _name, elem[2])) + line.append(f"{indent3}{_name}='{elem[2]}',") else: - line.append("%s%s=%s," % (indent3, _name, elem[2])) + line.append(f"{indent3}{_name}={elem[2]},") for _, elems in imports.items(): for elem in elems: @@ -75,7 +75,7 @@ def def_init(imports, attributes): _name = elem + "_" else: _name = elem - line.append("%s%s=None," % (indent3, _name)) + line.append(f"{indent3}{_name}=None,") line.append("%stext=None," % indent3) line.append("%sextension_elements=None," % indent3) @@ -94,12 +94,12 @@ def base_init(imports): _name = attr + "_" else: _name = attr - line.append("%s%s=%s," % (indent4, _name, _name)) + line.append(f"{indent4}{_name}={_name},") line.append("%s)" % indent4) else: # TODO have to keep apart which properties come from which superior for sup, elems in imports.items(): - line.append("%s%s.__init__(self, " % (INDENT + INDENT, sup)) + line.append(f"{INDENT + INDENT}{sup}.__init__(self, ") lattr = elems[:] lattr.extend(BASE_ELEMENT) for attr in lattr: @@ -107,7 +107,7 @@ def base_init(imports): _name = attr + "_" else: _name = attr - line.append("%s%s=%s," % (indent4, _name, _name)) + line.append(f"{indent4}{_name}={_name},") line.append("%s)" % indent4) return line @@ -126,7 +126,7 @@ def initialize(attributes): else: _vname = val - line.append("%sself.%s=%s" % (indent, _name, _vname)) + line.append(f"{indent}self.{_name}={_vname}") return line @@ -211,7 +211,7 @@ def klass_namn(obj): return obj.name -class PyObj(object): +class PyObj: def __init__(self, name=None, pyname=None, root=None): self.name = name self.done = False @@ -235,15 +235,15 @@ class PyObj(object): def child_spec(self, target_namespace, prop, mod, typ, lista): if mod: namesp = external_namespace(self.root.modul[mod]) - pkey = "{%s}%s" % (namesp, prop.name) - typ = "%s.%s" % (mod, typ) + pkey = f"{{{namesp}}}{prop.name}" + typ = f"{mod}.{typ}" else: - pkey = "{%s}%s" % (target_namespace, prop.name) + pkey = f"{{{target_namespace}}}{prop.name}" if lista: - return "c_children['%s'] = ('%s', [%s])" % (pkey, prop.pyname, typ) + return f"c_children['{pkey}'] = ('{prop.pyname}', [{typ}])" else: - return "c_children['%s'] = ('%s', %s)" % (pkey, prop.pyname, typ) + return f"c_children['{pkey}'] = ('{prop.pyname}', {typ})" def knamn(self, sup, cdict): cname = cdict[sup].class_name @@ -251,7 +251,7 @@ class PyObj(object): (namesp, tag) = cdict[sup].name.split(".") if namesp: ctag = self.root.modul[namesp].factory(tag).__class__.__name__ - cname = "%s.%s" % (namesp, ctag) + cname = f"{namesp}.{ctag}" else: cname = tag + "_" return cname @@ -267,7 +267,7 @@ class PyObj(object): for prop in own: if isinstance(prop, PyAttribute): - line.append("%sc_attributes['%s'] = %s" % (INDENT, prop.name, prop.spec())) + line.append(f"{INDENT}c_attributes['{prop.name}'] = {prop.spec()}") if prop.fixed: args.append((prop.pyname, prop.fixed, None)) else: @@ -290,20 +290,20 @@ class PyObj(object): if prop.name in ignore: pass else: - line.append("%s%s" % (INDENT, self.child_spec(target_namespace, prop, mod, cname, lista))) + line.append(f"{INDENT}{self.child_spec(target_namespace, prop, mod, cname, lista)}") pmin = int(getattr(prop, "min", 1)) if pmax == 1 and pmin == 1: pass elif prop.max == "unbounded": - line.append("%sc_cardinality['%s'] = {\"min\":%s}" % (INDENT, prop.pyname, pmin)) + line.append(f"{INDENT}c_cardinality['{prop.pyname}'] = {{\"min\":{pmin}}}") else: line.append('%sc_cardinality[\'%s\'] = {"min":%s, "max":%d}' % (INDENT, prop.pyname, pmin, pmax)) child.append(prop.pyname) if lista: - args.append((prop.pyname, "%s or []" % (prop.pyname,), None)) + args.append((prop.pyname, f"{prop.pyname} or []", None)) else: args.append((prop.pyname, prop.pyname, None)) @@ -341,38 +341,38 @@ class PyObj(object): c_name = klass_namn(self) if not superior: - line.append("class %s(SamlBase):" % (c_name,)) + line.append(f"class {c_name}(SamlBase):") else: - line.append("class %s(%s):" % (c_name, ",".join(sups))) + line.append("class {}({}):".format(c_name, ",".join(sups))) if hasattr(self, "scoped"): pass else: - line.append('%s"""The %s:%s element """' % (INDENT, target_namespace, self.name)) + line.append(f'{INDENT}"""The {target_namespace}:{self.name} element """') line.append("") - line.append("%sc_tag = '%s'" % (INDENT, self.name)) - line.append("%sc_namespace = NAMESPACE" % (INDENT,)) + line.append(f"{INDENT}c_tag = '{self.name}'") + line.append(f"{INDENT}c_namespace = NAMESPACE") try: if self.value_type: - if isinstance(self.value_type, six.string_types): - line.append("%sc_value_type = '%s'" % (INDENT, self.value_type)) + if isinstance(self.value_type, str): + line.append(f"{INDENT}c_value_type = '{self.value_type}'") else: - line.append("%sc_value_type = %s" % (INDENT, self.value_type)) + line.append(f"{INDENT}c_value_type = {self.value_type}") except AttributeError: pass if not superior: for var, cps in CLASS_PROP: - line.append("%s%s = SamlBase.%s%s" % (INDENT, var, var, cps)) + line.append(f"{INDENT}{var} = SamlBase.{var}{cps}") else: for sup in sups: for var, cps in CLASS_PROP: - line.append("%s%s = %s.%s%s" % (INDENT, var, sup, var, cps)) + line.append(f"{INDENT}{var} = {sup}.{var}{cps}") (args, child, inh) = self._do_properties(line, cdict, ignore, target_namespace) if child: - line.append("%sc_child_order.extend([%s])" % (INDENT, "'" + "', '".join(child) + "'")) + line.append("{}c_child_order.extend([{}])".format(INDENT, "'" + "', '".join(child) + "'")) if args: if inh: @@ -386,7 +386,7 @@ class PyObj(object): line.append("") if not self.abstract or not self.class_name.endswith("_"): line.append("def %s_from_string(xml_string):" % pyify(self.class_name)) - line.append("%sreturn saml2.create_class_from_xml_string(%s, xml_string)" % (INDENT, self.class_name)) + line.append(f"{INDENT}return saml2.create_class_from_xml_string({self.class_name}, xml_string)") line.append("") self.done = True @@ -436,7 +436,7 @@ class PyElement(PyObj): def __init__(self, name=None, pyname=None, root=None, parent=""): PyObj.__init__(self, name, pyname, root) if parent: - self.class_name = "%s_%s" % (leading_uppercase(parent), self.name) + self.class_name = f"{leading_uppercase(parent)}_{self.name}" else: self.class_name = leading_uppercase(self.name) self.ref = None @@ -499,7 +499,7 @@ class PyElement(PyObj): def _external_class(self, mod, typ, cdict, child, target_namespace, ignore): # Will raise exception if class can't be found cname = self.root.modul[mod].factory(typ).__class__.__name__ - imp_name = "%s.%s" % (mod, cname) + imp_name = f"{mod}.{cname}" if imp_name not in cdict: # create import object so I can get the properties from it @@ -554,7 +554,7 @@ class PyElement(PyObj): if verify_import(self.root.modul[mod], typ): return req, text else: - raise Exception("Import attempted on %s from %s module failed - wasn't there" % (typ, mod)) + raise Exception(f"Import attempted on {typ} from {mod} module failed - wasn't there") elif not child: self.superior = [typ] text = self.class_definition(target_namespace, cdict, ignore=ignore) @@ -580,7 +580,7 @@ def _do(obj, target_namespace, cdict, prep): else: obj.done = True if req: - if isinstance(req, six.string_types): + if isinstance(req, str): prep.append(req) else: prep.extend(req) @@ -716,12 +716,12 @@ class PyAttribute(PyObj): def spec(self): if isinstance(self.type, PyObj): - return "('%s', %s, %s)" % (self.pyname, self.type.class_name, self.required) + return f"('{self.pyname}', {self.type.class_name}, {self.required})" else: if self.type: - return "('%s', '%s', %s)" % (self.pyname, self.type, self.required) + return f"('{self.pyname}', '{self.type}', {self.required})" else: - return "('%s', '%s', %s)" % (self.pyname, self.base, self.required) + return f"('{self.pyname}', '{self.base}', {self.required})" class PyAny(PyObj): @@ -730,14 +730,14 @@ class PyAny(PyObj): self.done = True -class PyAttributeGroup(object): +class PyAttributeGroup: def __init__(self, name, root): self.name = name self.root = root self.properties = [] -class PyGroup(object): +class PyGroup: def __init__(self, name, root): self.name = name self.root = root @@ -828,7 +828,7 @@ def _spec(elem): def _do_from_string(name): print print("def %s_from_string(xml_string):" % pyify(name)) - print("%sreturn saml2.create_class_from_xml_string(%s, xml_string)" % (INDENT, name)) + print(f"{INDENT}return saml2.create_class_from_xml_string({name}, xml_string)") def _namespace_and_tag(obj, param, top): @@ -847,7 +847,7 @@ def _namespace_and_tag(obj, param, top): # ----------------------------------------------------------------------------- -class Simple(object): +class Simple: def __init__(self, elem): self.default = None self.fixed = None @@ -905,10 +905,10 @@ class Attribute(Simple): name = tag else: external = True - name = "{%s}%s" % (self.xmlns_map[namespace], tag) + name = f"{{{self.xmlns_map[namespace]}}}{tag}" else: if namespace == "xml": - name = "{%s}%s" % (XML_NAMESPACE, tag) + name = f"{{{XML_NAMESPACE}}}{tag}" except AttributeError: name = self.name pyname = pyify(name) @@ -965,7 +965,7 @@ class Attribute(Simple): pass if DEBUG: - print("#--ATTR py_attr:%s" % (objekt,)) + print(f"#--ATTR py_attr:{objekt}") return objekt @@ -1036,7 +1036,7 @@ def name_or_ref(elem, top): return elem.name -class Complex(object): +class Complex: def __init__(self, elem): self.value_of = "" self.parts = [] @@ -1146,7 +1146,7 @@ def min_max(cls, objekt, argv): class Element(Complex): def __str__(self): - return "%s" % (self.__dict__,) + return f"{self.__dict__}" def klass(self, top): xns = None @@ -1187,7 +1187,7 @@ class Element(Complex): if ctyp: return ctyp.elements(top) elif xns: - return ["%s.%s" % (xns, name)] + return [f"{xns}.{name}"] else: return [] @@ -1207,7 +1207,7 @@ class Element(Complex): myname = "" if DEBUG: - print("#Element.repr '%s' (child=%s) [%s]" % (myname, child, self._generated)) + print(f"#Element.repr '{myname}' (child={child}) [{self._generated}]") self.py_class = objekt = PyElement(myname, root=top) min_max(self, objekt, argv) @@ -1265,7 +1265,7 @@ class Element(Complex): raise if parent: - objekt.class_name = "%s_%s" % (leading_uppercase(parent), objekt.name) + objekt.class_name = f"{leading_uppercase(parent)}_{objekt.name}" objekt.scoped = True return objekt @@ -1409,7 +1409,7 @@ class ComplexType(Complex): new_sup = None value_type = name else: - new_sup = "%s.%s" % (namespace, name) + new_sup = f"{namespace}.{name}" # print("#Superior: %s" % new_sup) if new_sup: @@ -1647,7 +1647,7 @@ def output(elem, target_namespace, eldict, ignore=None): for prep in preps: if prep: done = 1 - if isinstance(prep, six.string_types): + if isinstance(prep, str): print(prep) else: for item in prep: @@ -1745,7 +1745,7 @@ class Schema(Complex): lista = False spec = objekt.child_spec(self.target_namespace, prop, mod, cname, lista) - lines = ["%s.%s" % (objekt.class_name, spec)] + lines = [f"{objekt.class_name}.{spec}"] tup.append((prop, lines, spec)) return tup @@ -1796,7 +1796,7 @@ class Schema(Complex): for sup in sups: if sup.name == ref: for tup in tups: - tup[1].append("%s.%s" % (objekt.class_name, tup[2])) + tup[1].append(f"{objekt.class_name}.{tup[2]}") break else: pass @@ -1842,7 +1842,7 @@ class Schema(Complex): continue if elem.abstract: continue - print("%s%s.c_tag: %s_from_string," % (INDENT, elem.class_name, pyify(elem.class_name))) + print(f"{INDENT}{elem.class_name}.c_tag: {pyify(elem.class_name)}_from_string,") print("}") print() @@ -1855,14 +1855,14 @@ class Schema(Complex): if elem.abstract: continue lcen = elem.name - print("%s'%s': %s," % (INDENT, lcen, elem.class_name)) + print(f"{INDENT}'{lcen}': {elem.class_name},") listed.append(lcen) for elem in self.elems: if isinstance(elem, PyAttribute) or isinstance(elem, PyGroup): continue lcen = elem.name if elem.abstract and lcen not in listed: - print("%s'%s': %s," % (INDENT, lcen, elem.class_name)) + print(f"{INDENT}'{lcen}': {elem.class_name},") listed.append(lcen) print("}") print @@ -1892,7 +1892,7 @@ class Schema(Complex): print("from %s import *" % modul) for _namespace, (mod, namn) in self.impo.items(): if namn: - print("import %s as %s" % (mod, namn)) + print(f"import {mod} as {namn}") print() print("NAMESPACE = '%s'" % self.target_namespace) print @@ -1928,9 +1928,9 @@ class Schema(Complex): print("AG_%s = [" % attrgrp.name) for prop in attrgrp.properties[0]: if isinstance(prop.type, PyObj): - print("%s('%s', %s_, %s)," % (INDENT, prop.name, prop.type.name, prop.required)) + print(f"{INDENT}('{prop.name}', {prop.type.name}_, {prop.required}),") else: - print("%s('%s', '%s', %s)," % (INDENT, prop.name, prop.type, prop.required)) + print(f"{INDENT}('{prop.name}', '{prop.type}', {prop.required}),") print("]") print() @@ -1982,7 +1982,7 @@ ELEMENTFUNCTION = {} for nsp in NAMESPACE_BASE: for nskey, func in _MAP.items(): - ELEMENTFUNCTION["{%s}%s" % (nsp, nskey)] = func + ELEMENTFUNCTION[f"{{{nsp}}}{nskey}"] = func def evaluate(typ, elem): @@ -2084,12 +2084,12 @@ def find_and_replace(base, mods): def read_schema(doc, add, defs, impo, modul, ignore, sdir): for path in sdir: - fil = "%s%s" % (path, doc) + fil = f"{path}{doc}" try: fp = open(fil) fp.close() break - except IOError as e: + except OSError as e: if e.errno == errno.EACCES: continue else: diff --git a/src/saml2/tools/sync_attrmaps.py b/src/saml2/tools/sync_attrmaps.py index 718508c0..f20e2d6c 100644 --- a/src/saml2/tools/sync_attrmaps.py +++ b/src/saml2/tools/sync_attrmaps.py @@ -37,7 +37,7 @@ def intcmp(s1, s2): return 0 -class AMap(object): +class AMap: def __init__(self, head, tail, indent=4 * " "): self.mod = load(head, tail) self.variable = {} @@ -59,10 +59,10 @@ class AMap(object): try: assert self.mod.MAP["to"][val] == key except KeyError: # missing value - print("# Added %s=%s" % (self.mod.MAP["to"][val], key)) + print("# Added {}={}".format(self.mod.MAP["to"][val], key)) self.mod.MAP["to"][val] = key except AssertionError: - raise Exception("Mismatch key:%s '%s' != '%s'" % (key, val, self.mod.MAP["to"][val])) + raise Exception("Mismatch key:{} '{}' != '{}'".format(key, val, self.mod.MAP["to"][val])) for val in self.mod.MAP["to"].values(): if val not in self.mod.MAP["fro"]: @@ -77,7 +77,7 @@ class AMap(object): li = [k[len(_v) :] for k in _fro.keys() if k.startswith(_v)] li.sort(intcmp) for item in li: - txt.append("%s%s+'%s': '%s'," % (i2, var, item, _fro[_v + item])) + txt.append(f"{i2}{var}+'{item}': '{_fro[_v + item]}',") txt.append("%s}," % self.indent) return txt @@ -87,13 +87,13 @@ class AMap(object): _to = self.mod.MAP["to"] _keys = _to.keys() _keys.sort() - invmap = dict([(v, k) for k, v in self.variable.items()]) + invmap = {v: k for k, v in self.variable.items()} for key in _keys: val = _to[key] for _urn, _name in invmap.items(): if val.startswith(_urn): - txt.append("%s'%s': %s+'%s'," % (i2, key, _name, val[len(_urn) :])) + txt.append(f"{i2}'{key}': {_name}+'{val[len(_urn) :]}',") txt.append("%s}" % self.indent) return txt @@ -102,12 +102,12 @@ class AMap(object): self.sync() text = [] for key in self.vars: - text.append("%s = '%s'" % (key, self.variable[key])) + text.append(f"{key} = '{self.variable[key]}'") text.extend(["", ""]) text.append("MAP = {") - text.append("%s'identifier': '%s'," % (self.indent, self.mod.MAP["identifier"])) + text.append("{}'identifier': '{}',".format(self.indent, self.mod.MAP["identifier"])) text.extend(self.do_fro()) text.extend(self.do_to()) diff --git a/src/saml2/userinfo/__init__.py b/src/saml2/userinfo/__init__.py index 9e5bccd4..1415d300 100644 --- a/src/saml2/userinfo/__init__.py +++ b/src/saml2/userinfo/__init__.py @@ -3,7 +3,7 @@ import copy -class UserInfo(object): +class UserInfo: """Read only interface to a user info store""" def __init__(self): diff --git a/src/saml2/validate.py b/src/saml2/validate.py index 77163598..33083886 100644 --- a/src/saml2/validate.py +++ b/src/saml2/validate.py @@ -6,8 +6,7 @@ from ipaddress import IPv6Address import re import struct import time - -from six.moves.urllib.parse import urlparse +from urllib.parse import urlparse from saml2 import time_util @@ -334,9 +333,9 @@ def _valid_instance(instance, val): try: val.verify() except NotValid as exc: - raise NotValid("Class '%s' instance: %s" % (instance.__class__.__name__, exc.args[0])) + raise NotValid(f"Class '{instance.__class__.__name__}' instance: {exc.args[0]}") except OutsideCardinality as exc: - raise NotValid("Class '%s' instance cardinality error: %s" % (instance.__class__.__name__, exc.args[0])) + raise NotValid(f"Class '{instance.__class__.__name__}' instance cardinality error: {exc.args[0]}") ERROR_TEXT = "Wrong type of value '%s' on attribute '%s' expected it to be %s" @@ -355,13 +354,13 @@ def valid_instance(instance): try: validate_value_type(instance.text.strip(), instclass.c_value_type) except NotValid as exc: - raise NotValid("Class '%s' instance: %s" % (class_name, exc.args[0])) + raise NotValid(f"Class '{class_name}' instance: {exc.args[0]}") for (name, typ, required) in instclass.c_attributes.values(): value = getattr(instance, name, "") if required and not value: txt = "Required value on property '%s' missing" % name - raise MustValueError("Class '%s' instance: %s" % (class_name, txt)) + raise MustValueError(f"Class '{class_name}' instance: {txt}") if value: try: @@ -376,7 +375,7 @@ def valid_instance(instance): valid(typ, value) except (NotValid, ValueError) as exc: txt = ERROR_TEXT % (value, name, exc.args[0]) - raise NotValid("Class '%s' instance: %s" % (class_name, txt)) + raise NotValid(f"Class '{class_name}' instance: {txt}") for (name, _spec) in instclass.c_children.values(): value = getattr(instance, name, "") @@ -406,13 +405,11 @@ def valid_instance(instance): if _card: if _cmin is not None and _cmin > vlen: raise NotValid( - "Class '%s' instance cardinality error: %s" - % (class_name, "less then min (%s<%s)" % (vlen, _cmin)) + "Class '%s' instance cardinality error: %s" % (class_name, f"less then min ({vlen}<{_cmin})") ) if _cmax is not None and vlen > _cmax: raise NotValid( - "Class '%s' instance cardinality error: %s" - % (class_name, "more then max (%s>%s)" % (vlen, _cmax)) + "Class '%s' instance cardinality error: %s" % (class_name, f"more then max ({vlen}>{_cmax})") ) if _list: @@ -424,7 +421,7 @@ def valid_instance(instance): else: if _cmin: raise NotValid( - "Class '%s' instance cardinality error: %s" % (class_name, "too few values on %s" % name) + "Class '{}' instance cardinality error: {}".format(class_name, "too few values on %s" % name) ) return True diff --git a/src/saml2/virtual_org.py b/src/saml2/virtual_org.py index 375044b4..d5d8dcba 100644 --- a/src/saml2/virtual_org.py +++ b/src/saml2/virtual_org.py @@ -7,7 +7,7 @@ from saml2.saml import NAMEID_FORMAT_PERSISTENT logger = logging.getLogger(__name__) -class VirtualOrg(object): +class VirtualOrg: def __init__(self, sp, vorg, cnf): self.sp = sp # The parent SP client instance self._name = vorg diff --git a/src/saml2/xmldsig/__init__.py b/src/saml2/xmldsig/__init__.py index 18c9eed4..85f89263 100644 --- a/src/saml2/xmldsig/__init__.py +++ b/src/saml2/xmldsig/__init__.py @@ -75,8 +75,8 @@ ALLOWED_TRANSFORMS = { } -class DefaultSignature(object): - class _DefaultSignature(object): +class DefaultSignature: + class _DefaultSignature: def __init__(self, sign_alg=None, digest_alg=None): if sign_alg is None: self.sign_alg = sig_default diff --git a/src/saml2test/check.py b/src/saml2test/check.py index 7ef51279..726860ac 100644 --- a/src/saml2test/check.py +++ b/src/saml2test/check.py @@ -23,7 +23,7 @@ CONT_JSON = "application/json" CONT_JWT = "application/jwt" -class Check(object): +class Check: """General test""" cid = "check" @@ -88,7 +88,7 @@ class ResponseInfo(Information): self._status = self.status _msg = conv.last_content - if isinstance(_msg, six.string_types): + if isinstance(_msg, str): self._message = _msg else: self._message = _msg.to_dict() @@ -259,7 +259,7 @@ class Parse(CriticalError): if conv.exception: self._status = self.status err = conv.exception - self._message = "%s: %s" % (err.__class__.__name__, err) + self._message = f"{err.__class__.__name__}: {err}" else: _rmsg = conv.response_message cname = _rmsg.type() @@ -267,7 +267,7 @@ class Parse(CriticalError): self._status = self.status self._message = ( "Didn't get a response of the type I expected:", - " '%s' instead of '%s', content:'%s'" % (cname, conv.response_type, _rmsg), + f" '{cname}' instead of '{conv.response_type}', content:'{_rmsg}'", ) return {"response_type": conv.response_type, "url": conv.position} diff --git a/src/saml2test/interaction.py b/src/saml2test/interaction.py index 9c1dad6e..6fe6424a 100644 --- a/src/saml2test/interaction.py +++ b/src/saml2test/interaction.py @@ -92,7 +92,7 @@ class RResponse: return self.text -class Interaction(object): +class Interaction: def __init__(self, httpc, interactions=None): self.httpc = httpc self.interactions = interactions @@ -123,7 +123,7 @@ class Interaction(object): _match += 1 else: _c = _bs.title.contents - if isinstance(_c, list) and not isinstance(_c, six.string_types): + if isinstance(_c, list) and not isinstance(_c, str): for _line in _c: if val in _line: _match += 1 @@ -183,7 +183,7 @@ class Interaction(object): _default = _ava["value"] try: orig_val = form[prop] - if isinstance(orig_val, six.string_types): + if isinstance(orig_val, str): if orig_val == _default: _form = form elif _default in orig_val: @@ -309,7 +309,7 @@ class Interaction(object): _url = kwargs["location"] part = urlparse(_url) - url = "%s://%s%s" % (part[0], part[1], path) + url = f"{part[0]}://{part[1]}{path}" else: url = path @@ -361,7 +361,7 @@ class Interaction(object): # ======================================================================== -class Action(object): +class Action: def __init__(self, args): self.args = args or {} self.request = None diff --git a/src/saml2test/opfunc.py b/src/saml2test/opfunc.py index 09491739..c46f614d 100644 --- a/src/saml2test/opfunc.py +++ b/src/saml2test/opfunc.py @@ -164,7 +164,7 @@ def pick_form(response, content, url=None, **kwargs): _default = _ava["value"] try: orig_val = form[prop] - if isinstance(orig_val, six.string_types): + if isinstance(orig_val, str): if orig_val == _default: _form = form elif _default in orig_val: @@ -295,7 +295,7 @@ def chose(client, orig_response, content, path, **kwargs): _url = kwargs["location"] part = urlparse(_url) - url = "%s://%s%s" % (part[0], part[1], path) + url = f"{part[0]}://{part[1]}{path}" else: url = path @@ -339,7 +339,7 @@ def interaction(args): # ======================================================================== -class Operation(object): +class Operation: def __init__(self, conv, args=None, features=None): if args: self.function = interaction(args) diff --git a/src/saml2test/tool.py b/src/saml2test/tool.py index f87f0e2a..7a2f8f0b 100644 --- a/src/saml2test/tool.py +++ b/src/saml2test/tool.py @@ -24,7 +24,7 @@ __author__ = "rolandh" logger = logging.getLogger(__name__) -class Conversation(object): +class Conversation: """ :ivar response: The received HTTP messages :ivar protocol_response: List of the received protocol messages @@ -75,7 +75,7 @@ class Conversation(object): raise CheckError def do_check(self, test, **kwargs): - if isinstance(test, six.string_types): + if isinstance(test, str): chk = self.check_factory(test)(**kwargs) else: chk = test(**kwargs) |