Merge pull request #131 from coreos-inc/moveapp
Refactor JWT auth to not import app locally
This commit is contained in:
commit
b21a033ef3
4 changed files with 27 additions and 24 deletions
2
app.py
2
app.py
|
@ -113,7 +113,7 @@ analytics = Analytics(app)
|
||||||
billing = Billing(app)
|
billing = Billing(app)
|
||||||
sentry = Sentry(app)
|
sentry = Sentry(app)
|
||||||
build_logs = BuildLogs(app)
|
build_logs = BuildLogs(app)
|
||||||
authentication = UserAuthentication(app)
|
authentication = UserAuthentication(app, OVERRIDE_CONFIG_DIRECTORY)
|
||||||
userevents = UserEventsBuilderModule(app)
|
userevents = UserEventsBuilderModule(app)
|
||||||
superusers = SuperUserManager(app)
|
superusers = SuperUserManager(app)
|
||||||
signer = Signer(app, OVERRIDE_CONFIG_DIRECTORY)
|
signer = Signer(app, OVERRIDE_CONFIG_DIRECTORY)
|
||||||
|
|
|
@ -54,18 +54,18 @@ class JWTAuthUsers(object):
|
||||||
""" Delegates authentication to a REST endpoint that returns JWTs. """
|
""" Delegates authentication to a REST endpoint that returns JWTs. """
|
||||||
PUBLIC_KEY_FILENAME = 'jwt-authn.cert'
|
PUBLIC_KEY_FILENAME = 'jwt-authn.cert'
|
||||||
|
|
||||||
def __init__(self, exists_url, verify_url, issuer, public_key_path=None):
|
def __init__(self, exists_url, verify_url, issuer, override_config_dir, http_client,
|
||||||
from app import OVERRIDE_CONFIG_DIRECTORY
|
public_key_path=None):
|
||||||
|
|
||||||
self.verify_url = verify_url
|
self.verify_url = verify_url
|
||||||
self.exists_url = exists_url
|
self.exists_url = exists_url
|
||||||
self.issuer = issuer
|
self.issuer = issuer
|
||||||
|
self.client = http_client
|
||||||
|
|
||||||
default_key_path = os.path.join(OVERRIDE_CONFIG_DIRECTORY, JWTAuthUsers.PUBLIC_KEY_FILENAME)
|
default_key_path = os.path.join(override_config_dir, JWTAuthUsers.PUBLIC_KEY_FILENAME)
|
||||||
public_key_path = public_key_path or default_key_path
|
public_key_path = public_key_path or default_key_path
|
||||||
if not os.path.exists(public_key_path):
|
if not os.path.exists(public_key_path):
|
||||||
error_message = ('JWT Authentication public key file "%s" not found in directory %s' %
|
error_message = ('JWT Authentication public key file "%s" not found in directory %s' %
|
||||||
(JWTAuthUsers.PUBLIC_KEY_FILENAME, OVERRIDE_CONFIG_DIRECTORY))
|
(JWTAuthUsers.PUBLIC_KEY_FILENAME, override_config_dir))
|
||||||
|
|
||||||
raise Exception(error_message)
|
raise Exception(error_message)
|
||||||
|
|
||||||
|
@ -73,9 +73,7 @@ class JWTAuthUsers(object):
|
||||||
self.public_key = public_key_file.read()
|
self.public_key = public_key_file.read()
|
||||||
|
|
||||||
def verify_user(self, username_or_email, password, create_new_user=True):
|
def verify_user(self, username_or_email, password, create_new_user=True):
|
||||||
from app import app
|
result = self.client.get(self.verify_url, timeout=2, auth=(username_or_email, password))
|
||||||
client = app.config['HTTPCLIENT']
|
|
||||||
result = client.get(self.verify_url, timeout=2, auth=(username_or_email, password))
|
|
||||||
|
|
||||||
if result.status_code != 200:
|
if result.status_code != 200:
|
||||||
return (None, result.text or 'Invalid username or password')
|
return (None, result.text or 'Invalid username or password')
|
||||||
|
@ -112,9 +110,7 @@ class JWTAuthUsers(object):
|
||||||
return _get_federated_user(payload['sub'], payload['email'], 'jwtauthn', create_new_user)
|
return _get_federated_user(payload['sub'], payload['email'], 'jwtauthn', create_new_user)
|
||||||
|
|
||||||
def user_exists(self, username):
|
def user_exists(self, username):
|
||||||
from app import app
|
result = self.client.get(self.exists_url, auth=(username, ''), timeout=2)
|
||||||
client = app.config['HTTPCLIENT']
|
|
||||||
result = client.get(self.exists_url, auth=(username, ''), timeout=2)
|
|
||||||
if result.status_code / 500 >= 1:
|
if result.status_code / 500 >= 1:
|
||||||
raise Exception('Internal Error when trying to check if user exists: %s' % result.text)
|
raise Exception('Internal Error when trying to check if user exists: %s' % result.text)
|
||||||
|
|
||||||
|
@ -310,14 +306,17 @@ class LDAPUsers(object):
|
||||||
|
|
||||||
|
|
||||||
class UserAuthentication(object):
|
class UserAuthentication(object):
|
||||||
def __init__(self, app=None):
|
def __init__(self, app=None, override_config_dir=None):
|
||||||
|
self.app_secret_key = None
|
||||||
self.app = app
|
self.app = app
|
||||||
if app is not None:
|
if app is not None:
|
||||||
self.state = self.init_app(app)
|
self.state = self.init_app(app, override_config_dir)
|
||||||
else:
|
else:
|
||||||
self.state = None
|
self.state = None
|
||||||
|
|
||||||
def init_app(self, app):
|
def init_app(self, app, override_config_dir):
|
||||||
|
self.app_secret_key = app.config['SECRET_KEY']
|
||||||
|
|
||||||
authentication_type = app.config.get('AUTHENTICATION_TYPE', 'Database')
|
authentication_type = app.config.get('AUTHENTICATION_TYPE', 'Database')
|
||||||
|
|
||||||
if authentication_type == 'Database':
|
if authentication_type == 'Database':
|
||||||
|
@ -336,24 +335,24 @@ class UserAuthentication(object):
|
||||||
verify_url = app.config.get('JWT_VERIFY_ENDPOINT')
|
verify_url = app.config.get('JWT_VERIFY_ENDPOINT')
|
||||||
exists_url = app.config.get('JWT_EXISTS_ENDPOINT')
|
exists_url = app.config.get('JWT_EXISTS_ENDPOINT')
|
||||||
issuer = app.config.get('JWT_AUTH_ISSUER')
|
issuer = app.config.get('JWT_AUTH_ISSUER')
|
||||||
users = JWTAuthUsers(exists_url, verify_url, issuer)
|
users = JWTAuthUsers(exists_url, verify_url, issuer, override_config_dir,
|
||||||
|
app.config['HTTPCLIENT'])
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Unknown authentication type: %s' % authentication_type)
|
raise RuntimeError('Unknown authentication type: %s' % authentication_type)
|
||||||
|
|
||||||
# register extension with app
|
# register extension with app
|
||||||
app.extensions = getattr(app, 'extensions', {})
|
app.extensions = getattr(app, 'extensions', {})
|
||||||
app.extensions['authentication'] = users
|
app.extensions['authentication'] = users
|
||||||
|
|
||||||
return users
|
return users
|
||||||
|
|
||||||
def _get_secret_key(self):
|
def _get_secret_key(self):
|
||||||
""" Returns the secret key to use for encrypting and decrypting. """
|
""" Returns the secret key to use for encrypting and decrypting. """
|
||||||
from app import app
|
|
||||||
app_secret_key = app.config['SECRET_KEY']
|
|
||||||
secret_key = None
|
secret_key = None
|
||||||
|
|
||||||
# First try parsing the key as an int.
|
# First try parsing the key as an int.
|
||||||
try:
|
try:
|
||||||
big_int = int(app_secret_key)
|
big_int = int(self.app_secret_key)
|
||||||
secret_key = str(bytearray.fromhex('{:02x}'.format(big_int)))
|
secret_key = str(bytearray.fromhex('{:02x}'.format(big_int)))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
@ -361,12 +360,12 @@ class UserAuthentication(object):
|
||||||
# Next try parsing it as an UUID.
|
# Next try parsing it as an UUID.
|
||||||
if secret_key is None:
|
if secret_key is None:
|
||||||
try:
|
try:
|
||||||
secret_key = uuid.UUID(app_secret_key).bytes
|
secret_key = uuid.UUID(self.app_secret_key).bytes
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if secret_key is None:
|
if secret_key is None:
|
||||||
secret_key = str(bytearray(map(ord, app_secret_key)))
|
secret_key = str(bytearray(map(ord, self.app_secret_key)))
|
||||||
|
|
||||||
# Otherwise, use the bytes directly.
|
# Otherwise, use the bytes directly.
|
||||||
return ''.join(itertools.islice(itertools.cycle(secret_key), 32))
|
return ''.join(itertools.islice(itertools.cycle(secret_key), 32))
|
||||||
|
|
|
@ -94,7 +94,8 @@ class JWTAuthTestCase(LiveServerTestCase):
|
||||||
self.jwt_auth = JWTAuthUsers(
|
self.jwt_auth = JWTAuthUsers(
|
||||||
self.get_server_url() + '/user/exists',
|
self.get_server_url() + '/user/exists',
|
||||||
self.get_server_url() + '/user/verify',
|
self.get_server_url() + '/user/verify',
|
||||||
'authy', JWTAuthTestCase.public_key.name)
|
'authy', '', app.config['HTTPCLIENT'],
|
||||||
|
JWTAuthTestCase.public_key.name)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
finished_database_for_testing(self)
|
finished_database_for_testing(self)
|
||||||
|
|
|
@ -12,11 +12,12 @@ from flask import Flask
|
||||||
from flask.ext.mail import Mail, Message
|
from flask.ext.mail import Mail, Message
|
||||||
from data.database import validate_database_url, User
|
from data.database import validate_database_url, User
|
||||||
from storage import get_storage_driver
|
from storage import get_storage_driver
|
||||||
from app import app, CONFIG_PROVIDER, get_app_url
|
|
||||||
from auth.auth_context import get_authenticated_user
|
from auth.auth_context import get_authenticated_user
|
||||||
from util.oauth import GoogleOAuthConfig, GithubOAuthConfig, GitLabOAuthConfig
|
from util.oauth import GoogleOAuthConfig, GithubOAuthConfig, GitLabOAuthConfig
|
||||||
from bitbucket import BitBucket
|
from bitbucket import BitBucket
|
||||||
|
|
||||||
|
from app import app, CONFIG_PROVIDER, get_app_url, OVERRIDE_CONFIG_DIRECTORY
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
SSL_FILENAMES = ['ssl.cert', 'ssl.key']
|
SSL_FILENAMES = ['ssl.cert', 'ssl.key']
|
||||||
|
@ -325,7 +326,9 @@ def _validate_jwt(config):
|
||||||
|
|
||||||
# Try to instatiate the JWT authentication mechanism. This will raise an exception if
|
# Try to instatiate the JWT authentication mechanism. This will raise an exception if
|
||||||
# the key cannot be found.
|
# the key cannot be found.
|
||||||
users = JWTAuthUsers(exists_endpoint, verify_endpoint, issuer)
|
users = JWTAuthUsers(exists_endpoint, verify_endpoint, issuer,
|
||||||
|
OVERRIDE_CONFIG_DIRECTORY,
|
||||||
|
app.config['HTTPCLIENT'])
|
||||||
|
|
||||||
# Verify that the superuser exists. If not, raise an exception.
|
# Verify that the superuser exists. If not, raise an exception.
|
||||||
username = get_authenticated_user().username
|
username = get_authenticated_user().username
|
||||||
|
|
Reference in a new issue