Remove jwt validation for jschorr to fix later

Refactor oauth validate method to take config over entire appconfig
This commit is contained in:
Sam Chow 2018-06-01 11:31:19 -04:00
parent 7df8ed4a60
commit 301cc6992a
27 changed files with 136 additions and 76 deletions

View file

@ -1,14 +1,8 @@
from flask import url_for
from urlparse import urljoin
def get_app_url(config):
""" Returns the application's URL, based on the given config. """
return '%s://%s' % (config['PREFERRED_URL_SCHEME'], config['SERVER_HOSTNAME'])
def get_app_url_from_scheme_hostname(url_scheme_and_hostname):
""" Returns the application's URL, based on the given url scheme and hostname. """
return '%s://%s' % (url_scheme_and_hostname.url_scheme, url_scheme_and_hostname.hostname)
def slash_join(*args):
"""
Joins together strings and guarantees there is only one '/' in between the
@ -23,12 +17,3 @@ def slash_join(*args):
args = [rmslash(path) for path in args]
return '/'.join(args)
def create_uri_func_from_context(context, url_scheme_and_hostname):
def create_uri(repository_and_namespace, checksum):
with context:
relative_layer_url = url_for('v2.download_blob', repository=repository_and_namespace,
digest=checksum)
return urljoin(get_app_url_from_scheme_hostname(url_scheme_and_hostname), relative_layer_url)
return create_uri

View file

@ -1,3 +1,29 @@
from collections import namedtuple
class URLSchemeAndHostname:
"""
Immutable configuration for a given preferred url scheme (e.g. http or https), and a hostname (e.g. localhost:5000)
"""
def __init__(self, url_scheme, hostname):
self._url_scheme = url_scheme
self._hostname = hostname
@classmethod
def from_app_config(cls, app_config):
"""
Helper method to instantiate class from app config, a frequent pattern
:param app_config:
:return:
"""
return cls(app_config['PREFERRED_URL_SCHEME'], app_config['SERVER_HOSTNAME'])
@property
def url_scheme(self):
return self._url_scheme
@property
def hostname(self):
return self._hostname
def get_url(self):
""" Returns the application's URL, based on the given url scheme and hostname. """
return '%s://%s' % (self._url_scheme, self._hostname)
URLSchemeAndHostname = namedtuple('URLSchemeAndHostname', ['url_scheme', 'hostname'])

View file

@ -13,7 +13,6 @@ class TestConfigProvider(BaseProvider):
def get_config_root(self):
raise Exception('Test Config does not have a config root')
# return ''
def __init__(self):
self.clear()

View file

@ -2,7 +2,7 @@ import logging
from auth.auth_context import get_authenticated_user
from data.users import LDAP_CERT_FILENAME
from util import create_uri_func_from_context
from util.secscan.secscan_util import get_blob_download_uri_getter
from util.config import URLSchemeAndHostname
from util.config.validators.validate_database import DatabaseValidator
@ -118,20 +118,30 @@ class ValidatorContext(object):
self.config_provider = config_provider
@classmethod
def from_app(cls, config, user_password, app, ip_resolver, client=None, config_provider=None):
url_scheme = URLSchemeAndHostname(app.config['PREFERRED_URL_SCHEME'], app.config['SERVER_HOSTNAME'])
def from_app(cls, app, config, user_password, ip_resolver, client=None, config_provider=None):
"""
Creates a ValidatorContext from an app config, with a given config to validate
:param app: the Flask app to pull configuration information from
:param config: the config to validate
:param user_password: request password
:param ip_resolver: an App
:param client:
:param config_provider:
:return:
"""
url_scheme_and_hostname = URLSchemeAndHostname.from_app_config(app.config)
cls(config,
user_password,
client or app.config['HTTPCLIENT'],
app.app_context,
url_scheme,
url_scheme_and_hostname,
app.config.get('JWT_AUTH_MAX_FRESH_S', 300),
app.config['REGISTRY_TITLE'],
ip_resolver,
app.config.get('FEATURE_SECURITY_SCANNER', False),
app.config.get('TESTING', False),
create_uri_func_from_context(app.test_request_context('/'), url_scheme),
get_blob_download_uri_getter(app.test_request_context('/'), url_scheme_and_hostname),
config_provider)

View file

@ -35,14 +35,13 @@ def test_validate_bitbucket_trigger(app):
with HTTMock(handler):
validator = BitbucketTriggerValidator()
url_scheme_and_hostname = URLSchemeAndHostname('http', 'localhost:5000')
unvalidated_config = ValidatorContext({
'BITBUCKET_TRIGGER_CONFIG': {
'CONSUMER_KEY': 'foo',
'CONSUMER_SECRET': 'bar',
},
})
unvalidated_config.url_scheme_and_hostname = URLSchemeAndHostname('http', 'localhost:5000')
}, url_scheme_and_hostname=url_scheme_and_hostname)
validator.validate(unvalidated_config)

View file

@ -33,16 +33,17 @@ def test_validate_gitlab_enterprise_trigger(app):
with HTTMock(handler):
validator = GitLabTriggerValidator()
url_scheme_and_hostname = URLSchemeAndHostname('http', 'localhost:5000')
unvalidated_config = ValidatorContext({
'GITLAB_TRIGGER_CONFIG': {
'GITLAB_ENDPOINT': 'http://somegitlab',
'CLIENT_ID': 'foo',
'CLIENT_SECRET': 'bar',
},
})
unvalidated_config.http_client = build_requests_session()
}, http_client=build_requests_session(), url_scheme_and_hostname=url_scheme_and_hostname)
unvalidated_config.url_scheme_and_hostname = URLSchemeAndHostname('http', 'localhost:5000')
validator.validate(unvalidated_config)
assert url_hit[0]

View file

@ -34,6 +34,8 @@ def test_invalid_config(unvalidated_config, app):
JWTAuthValidator.validate(config)
# TODO(jschorr): fix these when re-adding jwt auth mechanism to jwt validators
@pytest.mark.skip(reason='No way of currently testing this')
@pytest.mark.parametrize('username, password, expected_exception', [
('invaliduser', 'invalidpass', ConfigValidationException),
('cool.user', 'invalidpass', ConfigValidationException),

View file

@ -15,8 +15,7 @@ from app import config_provider
({'AUTHENTICATION_TYPE': 'Database'}),
])
def test_validate_noop(unvalidated_config, app):
config = ValidatorContext(unvalidated_config)
config.config_provider = config_provider
config = ValidatorContext(unvalidated_config, config_provider=config_provider)
LDAPValidator.validate(config)
@pytest.mark.parametrize('unvalidated_config', [
@ -25,8 +24,7 @@ def test_validate_noop(unvalidated_config, app):
])
def test_invalid_config(unvalidated_config, app):
with pytest.raises(ConfigValidationException):
config = ValidatorContext(unvalidated_config)
config.config_provider = config_provider
config = ValidatorContext(unvalidated_config, config_provider=config_provider)
LDAPValidator.validate(config)
@ -45,8 +43,7 @@ def test_invalid_uri(uri, app):
config['LDAP_URI'] = uri
with pytest.raises(ConfigValidationException):
config = ValidatorContext(config)
config.config_provider = config_provider
config = ValidatorContext(config, config_provider=config_provider)
LDAPValidator.validate(config)
@ -64,10 +61,8 @@ def test_validated_ldap(username, password, expected_exception, app):
config['LDAP_ADMIN_PASSWD'] = 'password'
config['LDAP_USER_RDN'] = ['ou=employees']
unvalidated_config = ValidatorContext(config)
unvalidated_config = ValidatorContext(config, user_password=password, config_provider=config_provider)
unvalidated_config.user = AttrDict(dict(username=username))
unvalidated_config.user_password = password
unvalidated_config.config_provider = config_provider
if expected_exception is not None:
with pytest.raises(ConfigValidationException):

View file

@ -12,9 +12,10 @@ from test.fixtures import *
({'DISTRIBUTED_STORAGE_PREFERENCE': []}),
])
def test_validate_noop(unvalidated_config, app):
unvalidated_config = ValidatorContext(unvalidated_config, feature_sec_scanner=False, is_testing=True)
unvalidated_config.http_client = build_requests_session()
unvalidated_config.url_scheme_and_hostname = URLSchemeAndHostname('http', 'localhost:5000')
unvalidated_config = ValidatorContext(unvalidated_config, feature_sec_scanner=False, is_testing=True,
http_client=build_requests_session(),
url_scheme_and_hostname=URLSchemeAndHostname('http', 'localhost:5000'))
SecurityScannerValidator.validate(unvalidated_config)
@ -35,9 +36,9 @@ def test_validate_noop(unvalidated_config, app):
}, None),
])
def test_validate(unvalidated_config, expected_error, app):
unvalidated_config = ValidatorContext(unvalidated_config, feature_sec_scanner=True, is_testing=True)
unvalidated_config.http_client = build_requests_session()
unvalidated_config.url_scheme_and_hostname = URLSchemeAndHostname('http', 'localhost:5000')
unvalidated_config = ValidatorContext(unvalidated_config, feature_sec_scanner=True, is_testing=True,
http_client=build_requests_session(),
url_scheme_and_hostname=URLSchemeAndHostname('http', 'localhost:5000'))
with fake_security_scanner(hostname='fakesecurityscanner'):
if expected_error is not None:

View file

@ -1,6 +1,5 @@
from bitbucket import BitBucket
from util import get_app_url_from_scheme_hostname
from util.config.validators import BaseValidator, ConfigValidationException
class BitbucketTriggerValidator(BaseValidator):
@ -23,7 +22,7 @@ class BitbucketTriggerValidator(BaseValidator):
key = trigger_config['CONSUMER_KEY']
secret = trigger_config['CONSUMER_SECRET']
callback_url = '%s/oauth1/bitbucket/callback/trigger/' % (get_app_url_from_scheme_hostname(validator_context.url_scheme_and_hostname))
callback_url = '%s/oauth1/bitbucket/callback/trigger/' % (validator_context.url_scheme_and_hostname.get_url())
bitbucket_client = BitBucket(key, secret, callback_url)
(result, _, _) = bitbucket_client.get_authorization_url()

View file

@ -10,6 +10,7 @@ class BaseGitHubValidator(BaseValidator):
""" Validates the OAuth credentials and API endpoint for a Github service. """
config = validator_context.config
client = validator_context.http_client
url_scheme_and_hostname = validator_context.url_scheme_and_hostname
github_config = config.get(cls.config_key)
if not github_config:
@ -33,7 +34,7 @@ class BaseGitHubValidator(BaseValidator):
'organization')
oauth = GithubOAuthService(config, cls.config_key)
result = oauth.validate_client_id_and_secret(client)
result = oauth.validate_client_id_and_secret(client, url_scheme_and_hostname)
if not result:
raise ConfigValidationException('Invalid client id or client secret')

View file

@ -9,6 +9,7 @@ class GoogleLoginValidator(BaseValidator):
""" Validates the Google Login client ID and secret. """
config = validator_context.config
client = validator_context.http_client
url_scheme_and_hostname = validator_context.url_scheme_and_hostname
google_login_config = config.get('GOOGLE_LOGIN_CONFIG')
if not google_login_config:
@ -21,6 +22,6 @@ class GoogleLoginValidator(BaseValidator):
raise ConfigValidationException('Missing Client Secret')
oauth = GoogleOAuthService(config, 'GOOGLE_LOGIN_CONFIG')
result = oauth.validate_client_id_and_secret(client)
result = oauth.validate_client_id_and_secret(client, url_scheme_and_hostname)
if not result:
raise ConfigValidationException('Invalid client id or client secret')

View file

@ -31,7 +31,10 @@ class JWTAuthValidator(BaseValidator):
raise ConfigValidationException('Missing JWT Issuer ID')
override_config_directory = os.path.join(config_provider.get_config_root(), 'stack/')
# TODO(jschorr): fix this
return
override_config_directory = os.path.join(config_provider.get_config_root(), '../stack/')
# Try to instatiate the JWT authentication mechanism. This will raise an exception if
# the key cannot be found.

View file

@ -13,7 +13,6 @@ class StorageValidator(BaseValidator):
ip_resolver = validator_context.ip_resolver
config_provider = validator_context.config_provider
# replication_enabled = app.config.get('FEATURE_STORAGE_REPLICATION', False)
replication_enabled = config.get('FEATURE_STORAGE_REPLICATION', False)
providers = _get_storage_providers(config, ip_resolver, config_provider).items()

View file

@ -0,0 +1,22 @@
from urlparse import urljoin
from flask import url_for
def get_blob_download_uri_getter(context, url_scheme_and_hostname):
"""
Returns a function with context to later generate the uri for a download blob
:param context: Flask RequestContext
:param url_scheme_and_hostname: URLSchemeAndHostname class instance
:return: function (repository_and_namespace, checksum) -> uri
"""
def create_uri(repository_and_namespace, checksum):
"""
Creates a uri for a download blob from a repository, namespace, and checksum from earlier context
"""
with context:
relative_layer_url = url_for('v2.download_blob', repository=repository_and_namespace,
digest=checksum)
return urljoin(url_scheme_and_hostname.get_url(), relative_layer_url)
return create_uri

View file

@ -0,0 +1,19 @@
import pytest
from app import app
from util.config import URLSchemeAndHostname
from util.secscan.secscan_util import get_blob_download_uri_getter
from test.fixtures import *
@pytest.mark.parametrize('url_scheme_and_hostname, repo_namespace, checksum, expected_value,', [
(URLSchemeAndHostname('http', 'localhost:5000'),
'devtable/simple', 'tarsum+sha256:123',
'http://localhost:5000/v2/devtable/simple/blobs/tarsum+sha256:123'),
])
def test_blob_download_uri_getter(app, url_scheme_and_hostname,
repo_namespace, checksum,
expected_value):
blob_uri_getter = get_blob_download_uri_getter(app.test_request_context('/'), url_scheme_and_hostname)
assert blob_uri_getter(repo_namespace, checksum) == expected_value