From e7387bdde996a4bf376e83703d9fe8eb2544e915 Mon Sep 17 00:00:00 2001 From: Paul Lettich Date: Fri, 23 Nov 2018 00:13:56 +0100 Subject: [PATCH 1/2] Fix several issues with Python 3 - The type `basestring` does not exist in Python 3 so we use `six.string_types` - Iterator don't contain a `.next()` function anymore, instead the builtin function `next()` is used - `raw_input()` does not exist in Python 3 instead we use `input()` from `six.moves` which does not evaluate the input - Changes how a MetaClass is defined - `long` type does not exist in Python 3. Instead a little hack is used to redefine `long` as `int` in case of Python 3 - Encoding strings with non-text encodings does not work anymore in Python 3. Instead the functionality from the `binascii` module is used. - The behaviour of the division operator changed in Python 3. To achieve the same behaviour in Python 2 we import `division` from `__future__` - The `range()` builtin function returns an iterator type in Python 3 thus we have to convert it to a list in some cases. - Improving conversion of a list of integers to bytes in `aeshsm.py` - The `string` class does not provide the method `replace()` anymore in Python 3, instead use the instance method. - If a class defines the `__eq__()` function it also has to define the `__hash__()` function in Python 3. Also the `__nonzero__` function is called `__bool__` in Python 3 - Some more string encodings, checks and exception handling --- privacyidea/lib/auditmodules/sqlaudit.py | 47 ++++++++++--------- privacyidea/lib/caconnectors/localca.py | 32 ++++++------- privacyidea/lib/config.py | 10 ++-- privacyidea/lib/crypto.py | 32 +++++++++---- privacyidea/lib/machines/base.py | 7 +-- privacyidea/lib/policy.py | 20 ++++---- privacyidea/lib/resolvers/LDAPIdResolver.py | 6 +-- privacyidea/lib/security/aeshsm.py | 21 +++++---- .../lib/smsprovider/SmtpSMSProvider.py | 11 ++--- privacyidea/lib/subscriptions.py | 3 ++ privacyidea/lib/token.py | 16 ++++--- privacyidea/lib/tokens/mOTP.py | 6 +-- privacyidea/lib/tokens/totptoken.py | 2 +- privacyidea/lib/tokens/vasco.py | 2 +- privacyidea/lib/user.py | 12 +++-- privacyidea/lib/utils.py | 12 ++--- 16 files changed, 135 insertions(+), 104 deletions(-) diff --git a/privacyidea/lib/auditmodules/sqlaudit.py b/privacyidea/lib/auditmodules/sqlaudit.py index 57c87e48c7..1d3ed478fd 100755 --- a/privacyidea/lib/auditmodules/sqlaudit.py +++ b/privacyidea/lib/auditmodules/sqlaudit.py @@ -49,6 +49,7 @@ from sqlalchemy import asc, desc, and_, or_ import datetime import traceback +from six import string_types log = logging.getLogger(__name__) @@ -148,7 +149,7 @@ def _truncate_data(self): for column, l in column_length.items(): if column in self.audit_data: data = self.audit_data[column] - if isinstance(data, basestring): + if isinstance(data, string_types): if column == "policies": # The policies column is shortend per comma entry data = truncate_comma_list(data, l) @@ -324,25 +325,27 @@ def _log_to_string(le): Note: Not all elements of the LogEntry are used to generate the string (the Signature is not!), otherwise we could have used pickle + + :param le: LogEntry object containing the data + :type le: LogEntry + :rtype str """ - s = "id=%s,date=%s,action=%s,succ=%s,serial=%s,t=%s,u=%s,r=%s,adm=%s,"\ - "ad=%s,i=%s,ps=%s,c=%s,l=%s,cl=%s" % (le.id, - le.date, - le.action, - le.success, - le.serial, - le.token_type, - le.user, - le.realm, - le.administrator, - le.action_detail, - le.info, - le.privacyidea_server, - le.client, - le.loglevel, - le.clearance_level) - if type(s) == unicode: - s = s.encode("utf-8") + s = u"id=%s,date=%s,action=%s,succ=%s,serial=%s,t=%s,u=%s,r=%s,adm=%s," \ + u"ad=%s,i=%s,ps=%s,c=%s,l=%s,cl=%s" % (le.id, + le.date, + le.action, + le.success, + le.serial, + le.token_type, + le.user, + le.realm, + le.administrator, + le.action_detail, + le.info, + le.privacyidea_server, + le.client, + le.loglevel, + le.clearance_level) return s @staticmethod @@ -427,12 +430,12 @@ def search(self, search_dict, page_size=15, page=1, sortorder="asc", page=page, sortorder=sortorder, timelimit=timelimit) try: - le = auditIter.next() + le = next(auditIter) while le: # Fill the list paging_object.auditdata.append(self.audit_entry_to_dict(le)) - le = auditIter.next() - except StopIteration: + le = next(auditIter) + except StopIteration as _e: log.debug("Interation stopped.") return paging_object diff --git a/privacyidea/lib/caconnectors/localca.py b/privacyidea/lib/caconnectors/localca.py index 646a135a73..fbd8cc903e 100644 --- a/privacyidea/lib/caconnectors/localca.py +++ b/privacyidea/lib/caconnectors/localca.py @@ -38,6 +38,9 @@ import re import logging import os +from six import string_types +from six.moves import input + log = logging.getLogger(__name__) CA_SIGN = "openssl ca -keyfile {cakey} -cert {cacert} -config {config} " \ @@ -495,7 +498,7 @@ def revoke_cert(self, certificate, reason=CRL_REASONS[0]): :return: Returns the serial number of the revoked certificate. Otherwise an error is raised. """ - if type(certificate) in [basestring, unicode, str]: + if isinstance(certificate, string_types): cert_obj = crypto.load_certificate(crypto.FILETYPE_PEM, certificate) elif type(certificate) == crypto.X509: cert_obj = certificate @@ -595,23 +598,21 @@ def create_ca(cls, name): config = CONFIG(name) while 1: - directory = raw_input("In which directory do you want to create " - "the CA [{0!s}]: ".format(config.directory)) + directory = input("In which directory do you want to create " + "the CA [{0!s}]: ".format(config.directory)) config.directory = directory or config.directory if not config.directory.startswith("/"): config.directory = os.path.abspath(config.directory) - keysize = raw_input( - "What should be the keysize of the CA (2048/4096/8192) [{" - "0!s}]: ".format(config.keysize)) + keysize = input("What should be the keysize of the CA (2048/4096/8192)" + "[{0!s}]: ".format(config.keysize)) config.keysize = keysize or config.keysize - validity_ca = raw_input("How many days should the CA be valid [" - "{0!s}]: ".format(config.validity_ca)) + validity_ca = input("How many days should the CA be valid [" + "{0!s}]: ".format(config.validity_ca)) config.validity_ca = validity_ca or config.validity_ca - dn = raw_input("What is the DN of the CA [{0!s}]: ".format( - config.dn)) + dn = input("What is the DN of the CA [{0!s}]: ".format(config.dn)) config.dn = dn or config.dn # At the moment we do not use this. This would be written to the # templates file. @@ -619,17 +620,16 @@ def create_ca(cls, name): # "What should be the validity period of enrolled certificates in days [{0!s}]: ".format( # config.validity_cert)) #config.validity_cert = validity_cert or config.validity_cert - crl_days = raw_input("How many days should the CRL be valid [{" - "0!s}]: ".format(config.crl_days)) + crl_days = input("How many days should the CRL be valid " + "[{0!s}]: ".format(config.crl_days)) config.crl_days = crl_days or config.crl_days - crl_overlap = raw_input( - "What should be the overlap period of the CRL in days [{" - "0!s}]: ".format(config.crl_overlap)) + crl_overlap = input("What should be the overlap period of the CRL in days " + "[{0!s}]: ".format(config.crl_overlap)) config.crl_overlap = crl_overlap or config.crl_overlap print("="*60) print("{0!s}".format(config)) - answer = raw_input("Is this configuration correct? [y/n] ") + answer = input("Is this configuration correct? [y/n] ") if answer.lower() == "y": break diff --git a/privacyidea/lib/config.py b/privacyidea/lib/config.py index 64d354b0e8..e5bf5f9059 100644 --- a/privacyidea/lib/config.py +++ b/privacyidea/lib/config.py @@ -50,6 +50,7 @@ from .utils import reload_db, is_true import importlib import datetime +from six import with_metaclass, string_types log = logging.getLogger(__name__) @@ -82,14 +83,13 @@ def __call__(cls, *args, **kwargs): return cls._instances[cls] -class ConfigClass(object): +class ConfigClass(with_metaclass(Singleton, object)): """ The Config_Object will contain all database configuration of system config, resolvers and realm. It will be created at the beginning of the request and is supposed to stay alive unchanged during the request. """ - __metaclass__ = Singleton def __init__(self): """ @@ -198,7 +198,7 @@ def get_config(self, key=None, default=None, role="admin", pass if isinstance(r_config, int): r_config = r_config > 0 - if isinstance(r_config, basestring): + if isinstance(r_config, string_types): r_config = is_true(r_config.lower()) return r_config @@ -347,8 +347,8 @@ def get_token_class(tokentype): def get_token_types(): """ Return a simple list of the type names of the tokens. - :return: array of tokentypes like 'hotp', 'totp'... - :rtype: array + :return: list of tokentypes like 'hotp', 'totp'... + :rtype: list """ if "pi_token_types" not in this.config: (t_classes, t_types) = get_token_class_dict() diff --git a/privacyidea/lib/crypto.py b/privacyidea/lib/crypto.py index 18638276e7..e6d9b69483 100644 --- a/privacyidea/lib/crypto.py +++ b/privacyidea/lib/crypto.py @@ -37,6 +37,7 @@ This lib.cryto is tested in tests/test_lib_crypto.py """ +from __future__ import division import hmac import logging from hashlib import sha256 @@ -62,7 +63,10 @@ import passlib.hash import sys import traceback +from six import PY2, string_types +if not PY2: + long = int FAILED_TO_DECRYPT_PASSWORD = "FAILED TO DECRYPT PASSWORD!" @@ -490,7 +494,9 @@ def geturandom(length=20, hex=False): get random - from the security module :param length: length of the returned bytes - default is 20 bytes - :rtype length: int + :type length: int + :param hex: convert result to hexstring + :type hex: bool :return: buffer of bytes @@ -517,10 +523,10 @@ def random(): :return: float value """ # get a binary random string - randbin = geturandom(urandom.precision) + randhex = geturandom(urandom.precision, hex=True) # convert this to an integer - randi = int(randbin.encode('hex'), 16) * 1.0 + randi = int(randhex, 16) * 1.0 # get the max integer intmax = 2 ** (8 * urandom.precision) * 1.0 @@ -628,8 +634,8 @@ def get_rand_digit_str(length=16): if length == 1: raise ValueError("get_rand_digit_str only works for values > 1") clen = int(length / 2.4 + 0.5) - randd = geturandom(clen) - s = "{0:d}".format((int(randd.encode('hex'), 16))) + randd = geturandom(clen, hex=True) + s = "{0:d}".format((int(randd, 16))) if len(s) < length: s = "0" * (length - len(s)) + s elif len(s) > length: @@ -646,8 +652,7 @@ def get_alphanum_str(length=16): """ ret = "" for i in range(length): - ret += random.choice(string.lowercase + string.uppercase + - string.digits) + ret += random.choice(string.ascii_letters + string.digits) return ret @@ -704,9 +709,13 @@ def sign(self, s): """ Create a signature of the string s + :param s: String to sign + :type s: str :return: The signature of the string :rtype: long """ + if isinstance(s, string_types): + s = s.encode('utf8') RSAkey = RSA.importKey(self.private) if SIGN_WITH_RSA: hashvalue = HashFunc.new(s).digest() @@ -720,7 +729,14 @@ def sign(self, s): def verify(self, s, signature): """ Check the signature of the string s + + :param s: String to check + :type s: str + :param signature: the signature to compare + :type signature: str """ + if isinstance(s, string_types): + s = s.encode('utf8') r = False try: RSAkey = RSA.importKey(self.public) @@ -731,7 +747,7 @@ def verify(self, s, signature): else: hashvalue = HashFunc.new(s) pkcs1_15.new(RSAkey).verify(hashvalue, signature) - except Exception: # pragma: no cover + except Exception as e: # pragma: no cover log.error("Failed to verify signature: {0!r}".format(s)) log.debug("{0!s}".format(traceback.format_exc())) return r diff --git a/privacyidea/lib/machines/base.py b/privacyidea/lib/machines/base.py index 9e35cfa342..d9dfd8aa33 100644 --- a/privacyidea/lib/machines/base.py +++ b/privacyidea/lib/machines/base.py @@ -25,6 +25,7 @@ This file is tested in tests/test_lib_machines.py """ import netaddr +from six import string_types class Machine(object): @@ -39,7 +40,7 @@ def __init__(self, resolver_name, machine_id, hostname=None, ip=None): self.id = machine_id self.resolver_name = resolver_name self.hostname = hostname - if type(ip) in [basestring, str, unicode]: + if isinstance(ip, string_types): self.ip = netaddr.IPAddress(ip) else: self.ip = ip @@ -55,7 +56,7 @@ def has_hostname(self, hostname): """ if type(self.hostname) == list: return hostname in self.hostname - elif type(self.hostname) in [basestring, str, unicode]: + elif isinstance(self.hostname, string_types): return hostname.lower() == self.hostname.lower() def has_ip(self, ip): @@ -68,7 +69,7 @@ def has_ip(self, ip): :return: True or false """ # convert to IPAddress - if type(ip) in [basestring, str, unicode]: + if isinstance(ip, string_types): ip = netaddr.IPAddress(ip) if type(self.ip) == list: diff --git a/privacyidea/lib/policy.py b/privacyidea/lib/policy.py index a083290fc8..29594ec880 100644 --- a/privacyidea/lib/policy.py +++ b/privacyidea/lib/policy.py @@ -175,6 +175,7 @@ import datetime import re import ast +from six import with_metaclass, PY2, string_types log = logging.getLogger(__name__) @@ -376,7 +377,7 @@ class TIMEOUT_ACTION(object): LOCKSCREEN = 'lockscreen' -class PolicyClass(object): +class PolicyClass(with_metaclass(Singleton, object)): """ The Policy_Object will contain all database policy entries for easy @@ -384,7 +385,6 @@ class PolicyClass(object): It will be created at the beginning of the request and is supposed to stay alive unchanged during the request. """ - __metaclass__ = Singleton def __init__(self): """ @@ -774,7 +774,7 @@ def ui_get_rights(self, scope, realm, username, client=None): if action_value: rights.add(action) # if the action has an actual non-boolean value, return it - if isinstance(action_value, basestring): + if isinstance(action_value, string_types): rights.add(u"{}={}".format(action, action_value)) # check if we have policies at all: pols = self.get_policies(scope=scope, active=True) @@ -1280,12 +1280,12 @@ def get_static_policy_definitions(scope=None): 'group': GROUP.SYSTEM, 'mainmenu': [MAIN_MENU.CONFIG]}, ACTION.OTPPINMAXLEN: {'type': 'int', - 'value': range(0, 32), + 'value': list(range(0, 32)), "desc": _("Set the maximum allowed length " "of the OTP PIN."), 'group': GROUP.PIN}, ACTION.OTPPINMINLEN: {'type': 'int', - 'value': range(0, 32), + 'value': list(range(0, 32)), "desc": _("Set the minimum required length " "of the OTP PIN."), 'group': GROUP.PIN}, @@ -1452,12 +1452,12 @@ def get_static_policy_definitions(scope=None): "PIN during enrollment."), 'group': GROUP.PIN}, ACTION.OTPPINMAXLEN: {'type': 'int', - 'value': range(0, 32), + 'value': list(range(0, 32)), "desc": _("Set the maximum allowed length " "of the OTP PIN."), 'group': GROUP.PIN}, ACTION.OTPPINMINLEN: {'type': 'int', - 'value': range(0, 32), + 'value': list(range(0, 32)), "desc": _("Set the minimum required length " "of the OTP PIN."), 'group': GROUP.PIN}, @@ -1504,7 +1504,7 @@ def get_static_policy_definitions(scope=None): 'group': GROUP.TOKEN}, ACTION.OTPPINRANDOM: { 'type': 'int', - 'value': range(0, 32), + 'value': list(range(0, 32)), "desc": _("Set a random OTP PIN with this length for a " "token."), 'group': GROUP.PIN}, @@ -1558,7 +1558,7 @@ def get_static_policy_definitions(scope=None): 'group': GROUP.TOKEN}, ACTION.LOSTTOKENPWLEN: { 'type': 'int', - 'value': range(1, 32), + 'value': list(range(1, 32)), 'desc': _('The length of the password in case of ' 'temporary token (lost token).')}, ACTION.LOSTTOKENPWCONTENTS: { @@ -1567,7 +1567,7 @@ def get_static_policy_definitions(scope=None): 'described by the characters C, c, n, s.')}, ACTION.LOSTTOKENVALID: { 'type': 'int', - 'value': range(1, 61), + 'value': list(range(1, 61)), 'desc': _('The length of the validity for the temporary ' 'token (in days).')}, }, diff --git a/privacyidea/lib/resolvers/LDAPIdResolver.py b/privacyidea/lib/resolvers/LDAPIdResolver.py index 2de690aedf..36c9bad7ad 100644 --- a/privacyidea/lib/resolvers/LDAPIdResolver.py +++ b/privacyidea/lib/resolvers/LDAPIdResolver.py @@ -83,6 +83,7 @@ import uuid from ldap3.utils.conv import escape_bytes from operator import itemgetter +from six import string_types CACHE = {} @@ -511,7 +512,7 @@ def _ldap_attributes_to_user_object(self, attributes): if ldap_k == map_v: if ldap_k == "objectGUID": # An objectGUID should be no list, since it is unique - if isinstance(ldap_v, basestring): + if isinstance(ldap_v, string_types): ret[map_k] = ldap_v.strip("{").strip("}") else: raise Exception("The LDAP returns an objectGUID, that is no string: {0!s}".format(type(ldap_v))) @@ -1066,8 +1067,7 @@ def _create_ssha(password): sha_hash.update(salt) # Create a base64 encoded string - digest_b64 = '{0}{1}'.format(sha_hash.digest(), - salt).encode('base64').strip() + digest_b64 = binascii.b2a_base64('{0}{1}'.format(sha_hash.digest(), salt)).strip() # Tag it with SSHA tagged_digest = '{{SSHA}}{}'.format(digest_b64) diff --git a/privacyidea/lib/security/aeshsm.py b/privacyidea/lib/security/aeshsm.py index 4d6488679f..3a77581ce3 100644 --- a/privacyidea/lib/security/aeshsm.py +++ b/privacyidea/lib/security/aeshsm.py @@ -22,6 +22,7 @@ import binascii import logging +from builtins import bytes from privacyidea.lib.security.default import SecurityModule from privacyidea.lib.error import HSMException from privacyidea.lib.crypto import get_alphanum_str @@ -52,10 +53,6 @@ "So we can not use the PKCS11 security module.") -def int_list_to_bytestring(int_list): # pragma: no cover - return "".join([chr(i) for i in int_list]) - - class AESHardwareSecurityModule(SecurityModule): # pragma: no cover def __init__(self, config=None): @@ -172,7 +169,7 @@ def random(self, length): """ Return a random bytestring :param length: length of the random bytestring - :return: + :rtype bytes """ retries = 0 while True: @@ -189,9 +186,13 @@ def random(self, length): raise HSMException("Failed to generate random number after multiple retries.") # convert the array of the random integers to a string - return int_list_to_bytestring(r_integers) + return bytes(r_integers) def encrypt(self, data, iv, key_id=TOKEN_KEY): + """ + + :rtype: bytes + """ if len(data) == 0: return bytes("") log.debug("Encrypting {} bytes with key {}".format(len(data), key_id)) @@ -211,9 +212,13 @@ def encrypt(self, data, iv, key_id=TOKEN_KEY): if retries > self.max_retries: raise HSMException("Failed to encrypt after multiple retries.") - return int_list_to_bytestring(r) + return bytes(r) def decrypt(self, data, iv, key_id=TOKEN_KEY): + """ + + :rtype bytes + """ if len(data) == 0: return bytes("") log.debug("Decrypting {} bytes with key {}".format(len(data), key_id)) @@ -233,7 +238,7 @@ def decrypt(self, data, iv, key_id=TOKEN_KEY): if retries > self.max_retries: raise HSMException("Failed to decrypt after multiple retries.") - return int_list_to_bytestring(r) + return bytes(r) def decrypt_password(self, crypt_pass): """ diff --git a/privacyidea/lib/smsprovider/SmtpSMSProvider.py b/privacyidea/lib/smsprovider/SmtpSMSProvider.py index e8ec6f49cb..d30f5d8250 100644 --- a/privacyidea/lib/smsprovider/SmtpSMSProvider.py +++ b/privacyidea/lib/smsprovider/SmtpSMSProvider.py @@ -36,7 +36,6 @@ """ from privacyidea.lib.smsprovider.SMSProvider import ISMSProvider, SMSError from privacyidea.lib.smtpserver import send_email_identifier, send_email_data -import string import logging log = logging.getLogger(__name__) @@ -78,11 +77,11 @@ def submit_message(self, phone, message): raise SMSError(-1, "Incomplete SMS config.") log.debug("submitting message {0!r} to {1!s}".format(body, phone)) - recipient = string.replace(recipient, PHONE_TAG, phone) - subject = string.replace(subject, PHONE_TAG, phone) - subject = string.replace(subject, MSG_TAG, message) - body = string.replace(body, PHONE_TAG, phone) - body = string.replace(body, MSG_TAG, message) + recipient = recipient.replace(PHONE_TAG, phone) + subject = subject.replace(PHONE_TAG, phone) + subject = subject.replace(MSG_TAG, message) + body = body.replace(PHONE_TAG, phone) + body = body.replace(MSG_TAG, message) if identifier: r = send_email_identifier(identifier, recipient, subject, body) diff --git a/privacyidea/lib/subscriptions.py b/privacyidea/lib/subscriptions.py index 3eb5378b0c..42d9ecd960 100644 --- a/privacyidea/lib/subscriptions.py +++ b/privacyidea/lib/subscriptions.py @@ -39,7 +39,10 @@ from Crypto.PublicKey import RSA import traceback from sqlalchemy import func +from six import PY2 +if not PY2: + long = int SUBSCRIPTION_DATE_FORMAT = "%Y-%m-%d" SIGN_FORMAT = """{application} diff --git a/privacyidea/lib/token.py b/privacyidea/lib/token.py index af219ded0a..6baa53e64d 100644 --- a/privacyidea/lib/token.py +++ b/privacyidea/lib/token.py @@ -64,6 +64,7 @@ import binascii import os import logging +from six import string_types from sqlalchemy import (and_, func) from privacyidea.lib.error import (TokenAdminError, @@ -428,7 +429,7 @@ def get_tokens_paginate(tokentype=None, realm=None, assigned=None, user=None, rollout_state=rollout_state, description=description, userid=userid) - if type(sortby) in [str, unicode]: + if isinstance(sortby, string_types): # convert the string to a Token column cols = Token.__table__.columns sortby = cols.get(sortby) @@ -1265,13 +1266,13 @@ def set_pin(serial, pin, user=None, encrypt_pin=False): :type pin: basestring :param user: If the user is specified, the pins for all tokens of this user will be set - :type used: User object + :type user: User object :param serial: If the serial is specified, the PIN for this very token will be set. (exact) :return: The number of PINs set (usually 1) :rtype: int """ - if isinstance(user, basestring): + if isinstance(user, string_types): # check if by accident the wrong parameter (like PIN) # is put into the user attribute log.warning("Parameter user must not be a string: {0!r}".format(user)) @@ -2041,7 +2042,8 @@ def check_token_list(tokenobject_list, passw, user=None, options=None): # Token is locked pin_match = False otp_count = -1 - repl = {'message': tae.message} + _num, message = tae.args + repl = {'message': message} repl = repl or {} reply_dict.update(repl) if otp_count >= 0: @@ -2101,7 +2103,7 @@ def check_token_list(tokenobject_list, passw, user=None, options=None): # reset the failcounter of valid token try: token_obj.reset() - except Exception: + except Exception as e: # In some cases (Registration Token) the token does not # exist anymore. So this would bail an exception! log.debug("registration token does not exist anymore and " @@ -2315,14 +2317,14 @@ def get_dynamic_policy_definitions(scope=None): for pin_scope in pin_scopes: pol[pin_scope]['{0!s}_otp_pin_maxlength'.format(ttype.lower())] = { 'type': 'int', - 'value': range(0, 32), + 'value': list(range(0, 32)), "desc": _("Set the maximum allowed PIN length of the {0!s}" " token.").format(ttype.upper()), 'group': GROUP.PIN } pol[pin_scope]['{0!s}_otp_pin_minlength'.format(ttype.lower())] = { 'type': 'int', - 'value': range(0, 32), + 'value': list(range(0, 32)), "desc": _("Set the minimum required PIN length of the {0!s}" " token.").format(ttype.upper()), 'group': GROUP.PIN diff --git a/privacyidea/lib/tokens/mOTP.py b/privacyidea/lib/tokens/mOTP.py index bc72caeece..1a347c715a 100644 --- a/privacyidea/lib/tokens/mOTP.py +++ b/privacyidea/lib/tokens/mOTP.py @@ -92,10 +92,10 @@ def checkOtp(self, anOtpVal, window=10, options=None): if options is not None and type(options) == dict: initTime = int(options.get('initTime', 0)) - if (initTime == 0): - otime = int(time.time() / 10) + if initTime == 0: + otime = int(time.time() // 10) else: - otime = int(initTime) + otime = initTime if self.secretObject is None: diff --git a/privacyidea/lib/tokens/totptoken.py b/privacyidea/lib/tokens/totptoken.py index d161609a58..3282ab48d8 100644 --- a/privacyidea/lib/tokens/totptoken.py +++ b/privacyidea/lib/tokens/totptoken.py @@ -34,7 +34,7 @@ It depends on the DB model, and the lib.tokenclass. TOTP is defined in https://tools.ietf.org/html/rfc6238 """ - +from __future__ import division import logging import time import math diff --git a/privacyidea/lib/tokens/vasco.py b/privacyidea/lib/tokens/vasco.py index ade9213dd4..47cccefc5f 100644 --- a/privacyidea/lib/tokens/vasco.py +++ b/privacyidea/lib/tokens/vasco.py @@ -129,7 +129,7 @@ def vasco_serialize(datablob): :param datablob: Digipass blob :return: bytestring """ - tokendata = buffer(datablob)[:] + tokendata = memoryview(datablob).tobytes() assert len(tokendata) == 248 return tokendata diff --git a/privacyidea/lib/user.py b/privacyidea/lib/user.py index 3c89c5a0a7..440e50b573 100644 --- a/privacyidea/lib/user.py +++ b/privacyidea/lib/user.py @@ -131,9 +131,11 @@ def __eq__(self, other): :rtype: bool """ # TODO: Should we add a check for `uid` here? - return (self.login == other.login) and (self.resolver == - other.resolver) and ( - self.realm == other.realm) + return isinstance(other, type(self)) and (self.login == other.login) and ( + self.resolver == other.resolver) and (self.realm == other.realm) + + def __hash__(self): + return hash((type(self), self.login, self.resolver, self.realm)) def __unicode__(self): ret = u"" @@ -156,8 +158,10 @@ def __repr__(self): self.login, self.realm, self.resolver)) return ret - def __nonzero__(self): + def __bool__(self): return not self.is_empty() + + __nonzero__ = __bool__ @log_with(log) def get_ordererd_resolvers(self): diff --git a/privacyidea/lib/utils.py b/privacyidea/lib/utils.py index e07a9157c3..8631b725a4 100644 --- a/privacyidea/lib/utils.py +++ b/privacyidea/lib/utils.py @@ -212,7 +212,7 @@ def create_img(data, width=0, alt=None, raw=False): o_data = create_png(data, alt=alt) else: o_data = data - data_uri = o_data.encode("base64").replace("\n", "") + data_uri = binascii.b2a_base64(o_data).replace(b"\n", b"") if width != 0: width_str = " width={0:d} ".format((int(width))) @@ -250,21 +250,19 @@ def generate_password(size=6, characters=string.ascii_lowercase + def modhex_encode(s): return ''.join( - [hex2ModDict[c] for c in s.encode('hex')] + [hex2ModDict[c] for c in binascii.hexlify(s)] ) # end def modhex_encode def modhex_decode(m): - return ''.join( - [mod2HexDict[c] for c in m] - ).decode('hex') + return binascii.unhexlify(''.join([mod2HexDict[c] for c in m])) # end def modhex_decode def checksum(msg): crc = 0xffff - for i in range(0, len(msg) / 2): + for i in range(0, len(msg) // 2): b = int(msg[i * 2] + msg[(i * 2) + 1], 16) crc = crc ^ (b & 0xff) for _j in range(0, 8): @@ -993,7 +991,7 @@ def gensalt_blowfish(self, inp): # pragma: no cover itoa64 = \ './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' outp = '$2a$' - outp += chr(ord('0') + self.iteration_count_log2 / 10) + outp += chr(ord('0') + self.iteration_count_log2 // 10) outp += chr(ord('0') + self.iteration_count_log2 % 10) outp += '$' cur = 0 From 5f12bca040569646859841b5a6adeaeaf31e64e7 Mon Sep 17 00:00:00 2001 From: Paul Lettich Date: Mon, 26 Nov 2018 11:03:11 +0100 Subject: [PATCH 2/2] Fixes remarks from review - In some cases, the `six.string_types` type is not suitable since it also contains bytestrings in Python 2. In this case, the `six.text_type` type is better (which contains only unicode strings) - Formatting a bytestring does not work as expected in Python 3 since it includes the `b` prefix in the string - The `bytes` type from Python 3 does not work as expected. The bytestring generation is reverted. - Missed the added message attribute in `privacyIDEAError` --- privacyidea/lib/crypto.py | 8 ++++---- privacyidea/lib/policy.py | 2 +- privacyidea/lib/resolvers/LDAPIdResolver.py | 2 +- privacyidea/lib/security/aeshsm.py | 12 ++++++++---- privacyidea/lib/token.py | 5 ++--- privacyidea/lib/utils.py | 2 +- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/privacyidea/lib/crypto.py b/privacyidea/lib/crypto.py index e6d9b69483..706903f0d9 100644 --- a/privacyidea/lib/crypto.py +++ b/privacyidea/lib/crypto.py @@ -63,7 +63,7 @@ import passlib.hash import sys import traceback -from six import PY2, string_types +from six import PY2, text_type if not PY2: long = int @@ -714,7 +714,7 @@ def sign(self, s): :return: The signature of the string :rtype: long """ - if isinstance(s, string_types): + if isinstance(s, text_type): s = s.encode('utf8') RSAkey = RSA.importKey(self.private) if SIGN_WITH_RSA: @@ -735,7 +735,7 @@ def verify(self, s, signature): :param signature: the signature to compare :type signature: str """ - if isinstance(s, string_types): + if isinstance(s, text_type): s = s.encode('utf8') r = False try: @@ -747,7 +747,7 @@ def verify(self, s, signature): else: hashvalue = HashFunc.new(s) pkcs1_15.new(RSAkey).verify(hashvalue, signature) - except Exception as e: # pragma: no cover + except Exception as _e: # pragma: no cover log.error("Failed to verify signature: {0!r}".format(s)) log.debug("{0!s}".format(traceback.format_exc())) return r diff --git a/privacyidea/lib/policy.py b/privacyidea/lib/policy.py index 29594ec880..da69c493a0 100644 --- a/privacyidea/lib/policy.py +++ b/privacyidea/lib/policy.py @@ -175,7 +175,7 @@ import datetime import re import ast -from six import with_metaclass, PY2, string_types +from six import with_metaclass, string_types log = logging.getLogger(__name__) diff --git a/privacyidea/lib/resolvers/LDAPIdResolver.py b/privacyidea/lib/resolvers/LDAPIdResolver.py index 36c9bad7ad..141b4f7aab 100644 --- a/privacyidea/lib/resolvers/LDAPIdResolver.py +++ b/privacyidea/lib/resolvers/LDAPIdResolver.py @@ -1067,7 +1067,7 @@ def _create_ssha(password): sha_hash.update(salt) # Create a base64 encoded string - digest_b64 = binascii.b2a_base64('{0}{1}'.format(sha_hash.digest(), salt)).strip() + digest_b64 = binascii.b2a_base64(sha_hash.digest() + salt).strip() # Tag it with SSHA tagged_digest = '{{SSHA}}{}'.format(digest_b64) diff --git a/privacyidea/lib/security/aeshsm.py b/privacyidea/lib/security/aeshsm.py index 3a77581ce3..41c757d8df 100644 --- a/privacyidea/lib/security/aeshsm.py +++ b/privacyidea/lib/security/aeshsm.py @@ -22,10 +22,10 @@ import binascii import logging -from builtins import bytes from privacyidea.lib.security.default import SecurityModule from privacyidea.lib.error import HSMException from privacyidea.lib.crypto import get_alphanum_str +from six import int2byte __doc__ = """ This is a PKCS11 Security module that encrypts and decrypts the data on a @@ -53,6 +53,10 @@ "So we can not use the PKCS11 security module.") +def int_list_to_bytestring(int_list): # pragma: no cover + return b"".join([int2byte(i) for i in int_list]) + + class AESHardwareSecurityModule(SecurityModule): # pragma: no cover def __init__(self, config=None): @@ -186,7 +190,7 @@ def random(self, length): raise HSMException("Failed to generate random number after multiple retries.") # convert the array of the random integers to a string - return bytes(r_integers) + return int_list_to_bytestring(r_integers) def encrypt(self, data, iv, key_id=TOKEN_KEY): """ @@ -212,7 +216,7 @@ def encrypt(self, data, iv, key_id=TOKEN_KEY): if retries > self.max_retries: raise HSMException("Failed to encrypt after multiple retries.") - return bytes(r) + return int_list_to_bytestring(r) def decrypt(self, data, iv, key_id=TOKEN_KEY): """ @@ -238,7 +242,7 @@ def decrypt(self, data, iv, key_id=TOKEN_KEY): if retries > self.max_retries: raise HSMException("Failed to decrypt after multiple retries.") - return bytes(r) + return int_list_to_bytestring(r) def decrypt_password(self, crypt_pass): """ diff --git a/privacyidea/lib/token.py b/privacyidea/lib/token.py index 6baa53e64d..9331117227 100644 --- a/privacyidea/lib/token.py +++ b/privacyidea/lib/token.py @@ -2042,8 +2042,7 @@ def check_token_list(tokenobject_list, passw, user=None, options=None): # Token is locked pin_match = False otp_count = -1 - _num, message = tae.args - repl = {'message': message} + repl = {'message': tae.message} repl = repl or {} reply_dict.update(repl) if otp_count >= 0: @@ -2103,7 +2102,7 @@ def check_token_list(tokenobject_list, passw, user=None, options=None): # reset the failcounter of valid token try: token_obj.reset() - except Exception as e: + except Exception as _e: # In some cases (Registration Token) the token does not # exist anymore. So this would bail an exception! log.debug("registration token does not exist anymore and " diff --git a/privacyidea/lib/utils.py b/privacyidea/lib/utils.py index 8631b725a4..dc67c72249 100644 --- a/privacyidea/lib/utils.py +++ b/privacyidea/lib/utils.py @@ -250,7 +250,7 @@ def generate_password(size=6, characters=string.ascii_lowercase + def modhex_encode(s): return ''.join( - [hex2ModDict[c] for c in binascii.hexlify(s)] + [hex2ModDict[c] for c in binascii.hexlify(s).decode('utf8')] ) # end def modhex_encode