Skip to content
This repository has been archived by the owner on Apr 28, 2020. It is now read-only.

Moving to Py3 #378

Merged
merged 11 commits into from
Feb 25, 2020
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
language: python
python:
- "2.7"
- "3.7"
services:
- redis-server
- postgresql
install:
- pip install git+https://github.com/hasgeek/flask-babelhg/
- pip install -r requirements.txt
- pip install -r test_requirements.txt
before_script:
Expand All @@ -15,6 +16,3 @@ after_success:
- coveralls
notifications:
email: false
slack:
- hasgeek:HDCoMDj3T4ICB59qFFVorCG8
- friendsofhasgeek:3bLViYSzhfaThJovFYCVD3fX
4 changes: 2 additions & 2 deletions features/steps/failed_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def given_new_user(context):
# registering the test user
context.browser.visit('/register')
assert context.browser.find_element_by_name('csrf_token').is_enabled()
for k, v in context.test_user.iteritems():
for k, v in context.test_user.items():
context.browser.find_element_by_name(k).send_keys(v)

register_form = context.browser.find_element_by_id('form-register')
Expand All @@ -30,7 +30,7 @@ def when_form_submit(context):
# this will fail
context.browser.visit('/register')
assert context.browser.find_element_by_name('csrf_token').is_enabled()
for k, v in context.test_user.iteritems():
for k, v in context.test_user.items():
context.browser.find_element_by_name(k).send_keys(v)

register_form = context.browser.find_element_by_id('form-register')
Expand Down
4 changes: 2 additions & 2 deletions features/steps/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def given_existing_user(context):

context.browser.visit('/register')
assert context.browser.find_element_by_name('csrf_token').is_enabled()
for k, v in context.test_user.iteritems():
for k, v in context.test_user.items():
context.browser.find_element_by_name(k).send_keys(v)

register_form = context.browser.find_element_by_id('form-register')
Expand All @@ -34,7 +34,7 @@ def when_login_form_submit(context):
assert context.browser.find_element_by_name('csrf_token').is_enabled()

context.browser.find_element_by_id('showmore').click()
for k, v in context.login_data.iteritems():
for k, v in context.login_data.items():
context.browser.find_element_by_name(k).send_keys(v)

context.browser.find_element_by_name('username').submit()
Expand Down
2 changes: 1 addition & 1 deletion features/steps/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def when_form_submit(context):
context.browser.visit('/register')

assert context.browser.find_element_by_name('csrf_token').is_enabled()
for k, v in context.test_user.iteritems():
for k, v in context.test_user.items():
context.browser.find_element_by_name(k).send_keys(v)

register_form = context.browser.find_element_by_id('form-register')
Expand Down
8 changes: 5 additions & 3 deletions instance/settings-sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,16 @@
USERNAME_REASON = ''
EMAIL_REASON = 'Please provide an email address to complete your profile'
TIMEZONE_REASON = 'Dates and times will be shown in your preferred timezone'
ORG_NAME_REASON = u"Your company’s name as it will appear in the URL. Letters, numbers and dashes only"
ORG_TITLE_REASON = u"Your organization’s given name, preferably without legal suffixes"
ORG_NAME_REASON = (
"Your company’s name as it will appear in the URL. Letters, numbers and dashes only"
)
ORG_TITLE_REASON = "Your organization’s given name, preferably without legal suffixes"
LOGIN_MESSAGE_1 = ""
LOGIN_MESSAGE_2 = ""
SMS_VERIFICATION_TEMPLATE = (
'Your verification code is {code}. If you did not request this, please ignore.'
)
CREATE_ACCOUNT_MESSAGE = (
u"This account is for you as an individual. We’ll make one for your company later"
"This account is for you as an individual. We’ll make one for your company later"
)
LOGOUT_UNAUTHORIZED_MESSAGE = "We detected a possibly unauthorized attempt to log you out. If you really did intend to logout, please click on the logout link again"
8 changes: 5 additions & 3 deletions instance/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,12 @@
EMAIL_REASON = 'Please provide an email address to complete your profile'
BIO_REASON = ''
TIMEZONE_REASON = 'Dates and times will be shown in your preferred timezone'
ORG_NAME_REASON = u"Your company’s name as it will appear in the URL. Letters, numbers and dashes only"
ORG_TITLE_REASON = u"Your organization’s given name, preferably without legal suffixes"
ORG_NAME_REASON = (
"Your company’s name as it will appear in the URL. Letters, numbers and dashes only"
)
ORG_TITLE_REASON = "Your organization’s given name, preferably without legal suffixes"
ORG_DESCRIPTION_REASON = (
u"A few words about your organization (optional). Plain text only"
"A few words about your organization (optional). Plain text only"
)
LOGIN_MESSAGE_1 = ""
LOGIN_MESSAGE_2 = ""
63 changes: 33 additions & 30 deletions lastuser_core/models/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from datetime import timedelta
from hashlib import sha256
import urlparse
import urllib.parse

from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import load_only
Expand Down Expand Up @@ -47,16 +47,16 @@ def _scope_get(self):
return tuple(sorted(self._scope.split()))

def _scope_set(self, value):
if isinstance(value, basestring):
if isinstance(value, str):
value = [value]
self._scope = u' '.join(sorted(t.strip() for t in value if t))
self._scope = ' '.join(sorted(t.strip() for t in value if t))

@declared_attr
def scope(cls):
return db.synonym('_scope', descriptor=property(cls._scope_get, cls._scope_set))

def add_scope(self, additional):
if isinstance(additional, basestring):
if isinstance(additional, str):
additional = [additional]
self.scope = list(set(self.scope).union(set(additional)))

Expand All @@ -83,7 +83,7 @@ class Client(ScopeMixin, BaseMixin, db.Model):
#: Human-readable title
title = db.Column(db.Unicode(250), nullable=False)
#: Long description
description = db.Column(db.UnicodeText, nullable=False, default=u'')
description = db.Column(db.UnicodeText, nullable=False, default='')
#: Confidential or public client? Public has no secret key
confidential = db.Column(db.Boolean, nullable=False)
#: Website
Expand All @@ -92,12 +92,12 @@ class Client(ScopeMixin, BaseMixin, db.Model):
namespace = db.Column(db.UnicodeText, nullable=True, unique=True)
#: Redirect URIs (one or more)
_redirect_uris = db.Column(
'redirect_uri', db.UnicodeText, nullable=True, default=u''
'redirect_uri', db.UnicodeText, nullable=True, default=''
)
#: Back-end notification URI
notification_uri = db.Column(db.UnicodeText, nullable=True, default=u'')
notification_uri = db.Column(db.UnicodeText, nullable=True, default='')
#: Front-end notification URI
iframe_uri = db.Column(db.UnicodeText, nullable=True, default=u'')
iframe_uri = db.Column(db.UnicodeText, nullable=True, default='')
#: Active flag
active = db.Column(db.Boolean, nullable=False, default=True)
#: Allow anyone to login to this app?
Expand Down Expand Up @@ -131,7 +131,7 @@ class Client(ScopeMixin, BaseMixin, db.Model):
)

def __repr__(self):
return u'<Client "{title}" {key}>'.format(title=self.title, key=self.key)
return '<Client "{title}" {key}>'.format(title=self.title, key=self.key)

def secret_is(self, candidate, name):
"""
Expand All @@ -146,7 +146,7 @@ def redirect_uris(self):

@redirect_uris.setter
def redirect_uris(self, value):
self._redirect_uris = u'\r\n'.join(value)
self._redirect_uris = '\r\n'.join(value)

@property
def redirect_uri(self):
Expand All @@ -155,12 +155,12 @@ def redirect_uri(self):
return uris[0]

def host_matches(self, url):
netloc = urlparse.urlsplit(url or '').netloc
netloc = urllib.parse.urlsplit(url or '').netloc
if netloc:
return netloc in [
urlparse.urlsplit(r).netloc
for r in list(self.redirect_uris) + [self.website]
]
return netloc in (
urllib.parse.urlsplit(r).netloc
for r in (self.redirect_uris + (self.website,))
)
return False

@property
Expand Down Expand Up @@ -249,14 +249,17 @@ class ClientCredential(BaseMixin, db.Model):
#: OAuth client key
name = db.Column(db.String(22), nullable=False, unique=True, default=buid)
#: User description for this credential
title = db.Column(db.Unicode(250), nullable=False, default=u'')
title = db.Column(db.Unicode(250), nullable=False, default='')
#: OAuth client secret, hashed (64 chars hash plus 7 chars id prefix = 71 chars)
secret_hash = db.Column(db.String(71), nullable=False)
#: When was this credential last used for an API call?
accessed_at = db.Column(db.TIMESTAMP(timezone=True), nullable=True)

def secret_is(self, candidate):
return self.secret_hash == 'sha256$' + sha256(candidate).hexdigest()
return (
self.secret_hash
== 'sha256$' + sha256(candidate.encode('utf-8')).hexdigest()
)

@classmethod
def get(cls, name):
Expand All @@ -273,7 +276,7 @@ def new(cls, client):
cred = cls(client=client, name=buid())
db.session.add(cred)
secret = newsecret()
cred.secret_hash = 'sha256$' + sha256(secret).hexdigest()
cred.secret_hash = 'sha256$' + sha256(secret.encode('utf-8')).hexdigest()
return cred, secret


Expand Down Expand Up @@ -312,7 +315,7 @@ class Resource(BaseScopedNameMixin, db.Model):
)
parent = db.synonym('client')
title = db.Column(db.Unicode(250), nullable=False)
description = db.Column(db.UnicodeText, default=u'', nullable=False)
description = db.Column(db.UnicodeText, default='', nullable=False)
siteresource = db.Column(db.Boolean, default=False, nullable=False)
restricted = db.Column(db.Boolean, default=False, nullable=False)
__table_args__ = (
Expand Down Expand Up @@ -369,7 +372,7 @@ class ResourceAction(BaseMixin, db.Model):
backref=db.backref('actions', cascade='all, delete-orphan'),
)
title = db.Column(db.Unicode(250), nullable=False)
description = db.Column(db.UnicodeText, default=u'', nullable=False)
description = db.Column(db.UnicodeText, default='', nullable=False)

# Action names are unique per client app
__table_args__ = (db.UniqueConstraint('resource_id', 'name'),)
Expand Down Expand Up @@ -445,7 +448,7 @@ class AuthToken(ScopeMixin, BaseMixin, db.Model):
token = db.Column(db.String(22), default=buid, nullable=False, unique=True)
#: The token's type
token_type = db.Column(
db.String(250), default=u'bearer', nullable=False
db.String(250), default='bearer', nullable=False
) # 'bearer', 'mac' or a URL
#: Token secret for 'mac' type
secret = db.Column(db.String(44), nullable=True)
Expand Down Expand Up @@ -485,7 +488,7 @@ def __init__(self, **kwargs):
self.secret = newsecret()

def __repr__(self):
return u'<AuthToken {token} of {client} {user}>'.format(
return '<AuthToken {token} of {client} {user}>'.format(
token=self.token, client=repr(self.client)[1:-1], user=repr(self.user)[1:-1]
)

Expand Down Expand Up @@ -513,7 +516,7 @@ def algorithm(self, value):
elif value in ['hmac-sha-1', 'hmac-sha-256']:
self._algorithm = value
else:
raise ValueError(_(u"Unrecognized algorithm ‘{value}’").format(value=value))
raise ValueError(_("Unrecognized algorithm ‘{value}’").format(value=value))

algorithm = db.synonym('_algorithm', descriptor=algorithm)

Expand Down Expand Up @@ -605,7 +608,7 @@ class Permission(BaseMixin, db.Model):
#: Human-friendly title
title = db.Column(db.Unicode(250), nullable=False)
#: Description of what this permission is about
description = db.Column(db.UnicodeText, default=u'', nullable=False)
description = db.Column(db.UnicodeText, default='', nullable=False)
#: Is this permission available to all users and client apps?
allusers = db.Column(db.Boolean, default=False, nullable=False)

Expand Down Expand Up @@ -677,7 +680,7 @@ class UserClientPermissions(BaseMixin, db.Model):
)
#: The permissions as a string of tokens
access_permissions = db.Column(
'permissions', db.UnicodeText, default=u'', nullable=False
'permissions', db.UnicodeText, default='', nullable=False
)

# Only one assignment per user and client
Expand All @@ -702,9 +705,9 @@ def migrate_user(cls, olduser, newuser):
# Merge permission strings
tokens = set(operm.access_permissions.split(' '))
tokens.update(set(nperm.access_permissions.split(' ')))
if u' ' in tokens:
tokens.remove(u' ')
nperm.access_permissions = u' '.join(sorted(tokens))
if ' ' in tokens:
tokens.remove(' ')
nperm.access_permissions = ' '.join(sorted(tokens))
db.session.delete(operm)
merge_performed = True
if not merge_performed:
Expand Down Expand Up @@ -732,7 +735,7 @@ class TeamClientPermissions(BaseMixin, db.Model):
)
#: The permissions as a string of tokens
access_permissions = db.Column(
'permissions', db.UnicodeText, default=u'', nullable=False
'permissions', db.UnicodeText, default='', nullable=False
)

# Only one assignment per team and client
Expand Down Expand Up @@ -790,6 +793,6 @@ class NoticeType(BaseMixin, db.Model):
#: Human-friendly title
title = db.Column(db.Unicode(250), nullable=False)
#: Description of what this notice type is about
description = db.Column(db.UnicodeText, default=u'', nullable=False)
description = db.Column(db.UnicodeText, default='', nullable=False)
#: Is this notice type available to all users and client apps?
allusers = db.Column(db.Boolean, default=False, nullable=False)
Loading