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

Commit

Permalink
Merge 84f7463 into 6f6e668
Browse files Browse the repository at this point in the history
  • Loading branch information
iambibhas committed Feb 20, 2020
2 parents 6f6e668 + 84f7463 commit d24a72a
Show file tree
Hide file tree
Showing 67 changed files with 1,135 additions and 714 deletions.
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)

0 comments on commit d24a72a

Please sign in to comment.