diff --git a/uffd/invite/models.py b/uffd/invite/models.py index 196e333cd378f2a52e60104c1df87db47e29005f..1c54ba881b5401b6f022c2e96892ce026a144d7f 100644 --- a/uffd/invite/models.py +++ b/uffd/invite/models.py @@ -1,4 +1,3 @@ -import secrets import datetime from flask import current_app @@ -9,6 +8,7 @@ from uffd.ldapalchemy.dbutils import DBRelationship from uffd.database import db from uffd.user.models import User from uffd.signup.models import Signup +from uffd.utils import token_urlfriendly invite_roles = db.Table('invite_roles', Column('invite_id', Integer(), ForeignKey('invite.id'), primary_key=True), @@ -18,7 +18,7 @@ invite_roles = db.Table('invite_roles', class Invite(db.Model): __tablename__ = 'invite' id = Column(Integer(), primary_key=True, autoincrement=True) - token = Column(String(128), unique=True, nullable=False, default=lambda: secrets.token_urlsafe(48)) + token = Column(String(128), unique=True, nullable=False, default=token_urlfriendly) created = Column(DateTime, default=datetime.datetime.now, nullable=False) creator_dn = Column(String(128), nullable=True) creator = DBRelationship('creator_dn', User) diff --git a/uffd/selfservice/models.py b/uffd/selfservice/models.py index 15d7050f2bc60006506509ed91a76dd08faaaccd..b87148cd8dca5292b98f80d2059852ecbebf4ca4 100644 --- a/uffd/selfservice/models.py +++ b/uffd/selfservice/models.py @@ -1,15 +1,12 @@ import datetime -import secrets from sqlalchemy import Column, String, DateTime from uffd.database import db - -def random_token(): - return secrets.token_hex(128) +from uffd.utils import token_urlfriendly class Token(): - token = Column(String(128), primary_key=True, default=random_token) + token = Column(String(128), primary_key=True, default=token_urlfriendly) created = Column(DateTime, default=datetime.datetime.now) class PasswordToken(Token, db.Model): diff --git a/uffd/session/models.py b/uffd/session/models.py index c91619e6d830a27a238e975d6b014e010a705ef1..ab243e0fcdbac2bc6dd55e85371a93c06fdc857e 100644 --- a/uffd/session/models.py +++ b/uffd/session/models.py @@ -1,6 +1,5 @@ import datetime import secrets -import math import enum from sqlalchemy import Column, String, Integer, DateTime, ForeignKey, Enum @@ -10,15 +9,7 @@ from sqlalchemy.ext.hybrid import hybrid_property from uffd.ldapalchemy.dbutils import DBRelationship from uffd.database import db from uffd.user.models import User - -def token_typeable(nbytes=None): - '''Return random text token that is easy to type (on mobile)''' - alphabet = '123456789abcdefghkmnopqrstuvwx' # No '0ijlyz' - if nbytes is None: - nbytes = secrets.DEFAULT_ENTROPY - nbytes_per_char = math.log(len(alphabet), 256) - nchars = math.ceil(nbytes / nbytes_per_char) - return ''.join([secrets.choice(alphabet) for _ in range(nchars)]) +from uffd.utils import token_typeable # Device login provides a convenient and secure way to log into SSO-enabled # services on a secondary device without entering the user password or diff --git a/uffd/signup/models.py b/uffd/signup/models.py index 7c7f4cc9b476b5632100b4038bf26e341117264b..e994acb00ccf3701f73d144730c743b91910e8ee 100644 --- a/uffd/signup/models.py +++ b/uffd/signup/models.py @@ -1,4 +1,3 @@ -import secrets import datetime from crypt import crypt @@ -8,6 +7,7 @@ from uffd.ldapalchemy.dbutils import DBRelationship from uffd.database import db from uffd.ldap import ldap from uffd.user.models import User +from uffd.utils import token_urlfriendly class Signup(db.Model): '''Model that represents a self-signup request @@ -26,7 +26,7 @@ class Signup(db.Model): As long as they are not completed, signup requests have no effect each other or different parts of the application.''' __tablename__ = 'signup' - token = Column(String(128), primary_key=True, default=lambda: secrets.token_urlsafe(48)) + token = Column(String(128), primary_key=True, default=token_urlfriendly) created = Column(DateTime, default=datetime.datetime.now, nullable=False) loginname = Column(Text) displayname = Column(Text) diff --git a/uffd/utils.py b/uffd/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..ff40711c3e114736781931aa83ee3c2cc10a20a9 --- /dev/null +++ b/uffd/utils.py @@ -0,0 +1,20 @@ +import secrets +import math + +def token_with_alphabet(alphabet, nbytes=None): + '''Return random text token that consists of characters from `alphabet`''' + if nbytes is None: + nbytes = max(secrets.DEFAULT_ENTROPY, 32) + nbytes_per_char = math.log(len(alphabet), 256) + nchars = math.ceil(nbytes / nbytes_per_char) + return ''.join([secrets.choice(alphabet) for _ in range(nchars)]) + +def token_typeable(nbytes=None): + '''Return random text token that is easy to type (on mobile)''' + alphabet = '123456789abcdefghkmnopqrstuvwx' # No '0ijlyz' + return token_with_alphabet(alphabet, nbytes=nbytes) + +def token_urlfriendly(nbytes=None): + '''Return random text token that is urlsafe and works around common parsing bugs''' + alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + return token_with_alphabet(alphabet, nbytes=nbytes)