diff --git a/etc/storyboard.conf.sample b/etc/storyboard.conf.sample index 855caf52..ef49a1c4 100644 --- a/etc/storyboard.conf.sample +++ b/etc/storyboard.conf.sample @@ -37,6 +37,17 @@ lock_path = $state_path/lock # Port the bind the API server to # bind_port = 8080 +# List paging configuration options. +# page_size_maximum = 500 +# page_size_default = 20 + +# Enable notifications. This feature drives deferred processing, reporting, +# and subscriptions. +# enable_notifications = True + +[oauth] +# StoryBoard's oauth configuration. + # OpenId Authentication endpoint # openid_url = https://login.launchpad.net/+openid @@ -46,14 +57,6 @@ lock_path = $state_path/lock # Time in seconds before an refresh_token expires # refresh_token_ttl = 604800 -# List paging configuration options. -# page_size_maximum = 500 -# page_size_default = 20 - -# Enable notifications. This feature drives deferred processing, reporting, -# and subscriptions. -# enable_notifications = True - [cron] # Storyboard's cron management configuration @@ -137,3 +140,8 @@ lock_path = $state_path/lock # If set, use this value for pool_timeout with sqlalchemy # pool_timeout = 10 + +[plugin_token_cleaner] +# Enable/Disable the token cleaning cron plugin. This requires cron +# management to be enabled. +# enable = True \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 9c79842c..61fbda39 100644 --- a/setup.cfg +++ b/setup.cfg @@ -42,7 +42,7 @@ storyboard.worker.task = storyboard.plugin.user_preferences = storyboard.plugin.cron = cron-management = storyboard.plugin.cron.manager:CronManager - token-cleaner = storyboard.plugin.oauth.cleaner:TokenCleaner + token-cleaner = storyboard.plugin.token_cleaner.cleaner:TokenCleaner [build_sphinx] source-dir = doc/source diff --git a/storyboard/api/auth/__init__.py b/storyboard/api/auth/__init__.py index e69de29b..55cd14ce 100644 --- a/storyboard/api/auth/__init__.py +++ b/storyboard/api/auth/__init__.py @@ -0,0 +1,33 @@ +# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing permissions and +# limitations under the License. + +from oslo.config import cfg + +CONF = cfg.CONF + +OAUTH_OPTS = [ + cfg.StrOpt('openid_url', + default='https://login.launchpad.net/+openid', + help='OpenId Authentication endpoint'), + + cfg.IntOpt("access_token_ttl", + default=60 * 60, # One hour + help="Time in seconds before an access_token expires"), + + cfg.IntOpt("refresh_token_ttl", + default=60 * 60 * 24 * 7, # One week + help="Time in seconds before an refresh_token expires") +] + +CONF.register_opts(OAUTH_OPTS, "oauth") diff --git a/storyboard/api/auth/oauth_validator.py b/storyboard/api/auth/oauth_validator.py index c02710e7..de993efb 100644 --- a/storyboard/api/auth/oauth_validator.py +++ b/storyboard/api/auth/oauth_validator.py @@ -26,18 +26,6 @@ from storyboard.openstack.common import log CONF = cfg.CONF LOG = log.getLogger(__name__) -TOKEN_OPTS = [ - cfg.IntOpt("access_token_ttl", - default=60 * 60, # One hour - help="Time in seconds before an access_token expires"), - - cfg.IntOpt("refresh_token_ttl", - default=60 * 60 * 24 * 7, # One week - help="Time in seconds before an refresh_token expires") -] - -CONF.register_opts(TOKEN_OPTS) - class SkeletonValidator(RequestValidator): """This is oauth skeleton for handling all kind of validations and storage @@ -259,7 +247,7 @@ class SkeletonValidator(RequestValidator): class OpenIdConnectServer(WebApplicationServer): def __init__(self, request_validator): - access_token_ttl = CONF.access_token_ttl + access_token_ttl = CONF.oauth.access_token_ttl super(OpenIdConnectServer, self).__init__( request_validator, token_expires_in=access_token_ttl) diff --git a/storyboard/api/auth/openid_client.py b/storyboard/api/auth/openid_client.py index b37875b0..5bba35ca 100644 --- a/storyboard/api/auth/openid_client.py +++ b/storyboard/api/auth/openid_client.py @@ -24,19 +24,10 @@ LOG = log.getLogger(__name__) CONF = cfg.CONF -OPENID_OPTS = [ - cfg.StrOpt('openid_url', - default='https://login.launchpad.net/+openid', - help='OpenId Authentication endpoint') -] - -CONF.register_opts(OPENID_OPTS) - - class OpenIdClient(object): def send_openid_redirect(self, request, response): - redirect_location = CONF.openid_url + redirect_location = CONF.oauth.openid_url response.status_code = 303 return_params = { @@ -91,7 +82,8 @@ class OpenIdClient(object): verify_params = dict(request.params.copy()) verify_params["openid.mode"] = "check_authentication" - verify_response = requests.post(CONF.openid_url, data=verify_params) + verify_response = requests.post(CONF.oauth.openid_url, + data=verify_params) verify_data_tokens = verify_response.content.split() verify_dict = dict((token.split(":")[0], token.split(":")[1]) for token in verify_data_tokens) diff --git a/storyboard/api/auth/token_storage/db_storage.py b/storyboard/api/auth/token_storage/db_storage.py index be498e1f..5ba57289 100644 --- a/storyboard/api/auth/token_storage/db_storage.py +++ b/storyboard/api/auth/token_storage/db_storage.py @@ -55,7 +55,7 @@ class DBTokenStorage(storage.StorageBase): # Oauthlib does not provide a separate expiration time for a # refresh_token so taking it from config directly. - refresh_expires_in = CONF.refresh_token_ttl + refresh_expires_in = CONF.oauth.refresh_token_ttl refresh_token_values = { "refresh_token": refresh_token, diff --git a/storyboard/plugin/token_cleaner/__init__.py b/storyboard/plugin/token_cleaner/__init__.py new file mode 100644 index 00000000..d806005e --- /dev/null +++ b/storyboard/plugin/token_cleaner/__init__.py @@ -0,0 +1,25 @@ +# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing permissions and +# limitations under the License. + +from oslo.config import cfg + +CONF = cfg.CONF + +PLUGIN_OPTS = [ + cfg.BoolOpt("enable", + default=False, # By default, we don't scrub old oauth tokens. + help="Enable, or disable, the oauth token cleaner") +] + +CONF.register_opts(PLUGIN_OPTS, "plugin_token_cleaner") diff --git a/storyboard/plugin/oauth/cleaner.py b/storyboard/plugin/token_cleaner/cleaner.py similarity index 93% rename from storyboard/plugin/oauth/cleaner.py rename to storyboard/plugin/token_cleaner/cleaner.py index 6561fdd7..a91a20d9 100644 --- a/storyboard/plugin/oauth/cleaner.py +++ b/storyboard/plugin/token_cleaner/cleaner.py @@ -31,7 +31,9 @@ class TokenCleaner(CronPluginBase): """Indicate whether this plugin is enabled. This indicates whether this plugin alone is runnable, as opposed to the entire cron system. """ - return True + if 'plugin_token_cleaner' in self.config: + return self.config.plugin_token_cleaner.enable or False + return False def interval(self): """This plugin executes on startup, and once every hour after that. diff --git a/storyboard/tests/plugin/cron/test_manager.py b/storyboard/tests/plugin/cron/test_manager.py index 6c0ba9c8..e5db90aa 100644 --- a/storyboard/tests/plugin/cron/test_manager.py +++ b/storyboard/tests/plugin/cron/test_manager.py @@ -245,8 +245,8 @@ class TestCronManager(base.TestCase): manager = cronmanager.CronManager(CONF, tabfile=self.tabfile) manager.execute() - # We're expecting 2 in-branch plugins. - self.assertCronLength(2, command='storyboard-cron') + # We're expecting 1 enabled in-branch plugins. + self.assertCronLength(1, command='storyboard-cron') def test_execute_update(self): """Test that execute() method updates plugins.""" @@ -312,7 +312,7 @@ class TestCronManager(base.TestCase): # Check a new crontab to see what we find. self.assertCronLength(0, command=plugin_command) - self.assertCronLength(2, command='storyboard-cron') + self.assertCronLength(1, command='storyboard-cron') # Cleanup after ourselves. manager.remove() @@ -343,7 +343,7 @@ class TestCronManager(base.TestCase): manager.execute() # Check a new crontab to see what we find. - self.assertCronLength(2, command='storyboard-cron') + self.assertCronLength(1, command='storyboard-cron') # Cleanup after ourselves. manager.remove() diff --git a/storyboard/tests/plugin/oauth/__init__.py b/storyboard/tests/plugin/oauth/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/storyboard/plugin/oauth/__init__.py b/storyboard/tests/plugin/token_cleaner/__init__.py similarity index 100% rename from storyboard/plugin/oauth/__init__.py rename to storyboard/tests/plugin/token_cleaner/__init__.py diff --git a/storyboard/tests/plugin/oauth/test_cleaner.py b/storyboard/tests/plugin/token_cleaner/test_cleaner.py similarity index 86% rename from storyboard/tests/plugin/oauth/test_cleaner.py rename to storyboard/tests/plugin/token_cleaner/test_cleaner.py index d757b1a8..d91bf117 100644 --- a/storyboard/tests/plugin/oauth/test_cleaner.py +++ b/storyboard/tests/plugin/token_cleaner/test_cleaner.py @@ -18,7 +18,7 @@ from datetime import timedelta from oslo.config import cfg import storyboard.db.api.base as db_api from storyboard.db.models import AccessToken -from storyboard.plugin.oauth.cleaner import TokenCleaner +from storyboard.plugin.token_cleaner.cleaner import TokenCleaner import storyboard.tests.base as base from storyboard.tests.mock_data import load_data @@ -36,12 +36,19 @@ class TestTokenCleaner(base.FunctionalTest): super(TestTokenCleaner, self).tearDown() def test_enabled(self): - """This plugin must always be enabled. The only time it's not enabled - is when cron has been disabled. + """Assert that this plugin responds to the flag set in our + oauth configuration block. """ + CONF.set_override('enable', False, 'plugin_token_cleaner') + plugin = TokenCleaner(CONF) + self.assertFalse(plugin.enabled()) + + CONF.set_override('enable', True, 'plugin_token_cleaner') plugin = TokenCleaner(CONF) self.assertTrue(plugin.enabled()) + CONF.clear_override('enable', 'plugin_token_cleaner') + def test_interval(self): """Assert that the cron manager runs every 5 minutes.""" plugin = TokenCleaner(CONF)