Make Keystone token validation works in devstack

Change-Id: Ie8a748bd06b2b76df577297ce27aa10a79c47232
This commit is contained in:
Frédéric Guillot 2016-12-09 12:15:43 -05:00
parent b31477b716
commit 3cc039afba
5 changed files with 82 additions and 26 deletions

View File

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from keystoneauth1 import exceptions as keystoneauth1_exceptions
from keystoneauth1.identity import v3 from keystoneauth1.identity import v3
from keystoneauth1 import session from keystoneauth1 import session
from keystoneclient.v3 import client as keystone_client from keystoneclient.v3 import client as keystone_client
@ -23,9 +24,14 @@ from almanach.core import exception
class KeystoneAuthentication(base_auth.BaseAuth): class KeystoneAuthentication(base_auth.BaseAuth):
def __init__(self, config): def __init__(self, config):
auth = v3.Password(username=config.auth.keystone_username, auth = v3.Password(username=config.keystone_authtoken.username,
password=config.auth.keystone_password, password=config.keystone_authtoken.password,
auth_url=config.auth.keystone_url) user_domain_id=config.keystone_authtoken.user_domain_id,
user_domain_name=config.keystone_authtoken.user_domain_name,
project_domain_name=config.keystone_authtoken.project_domain_name,
project_name=config.keystone_authtoken.project_name,
auth_url=config.keystone_authtoken.auth_url)
sess = session.Session(auth=auth) sess = session.Session(auth=auth)
self._client = keystone_client.Client(session=sess) self._client = keystone_client.Client(session=sess)
@ -33,9 +39,10 @@ class KeystoneAuthentication(base_auth.BaseAuth):
if token is None: if token is None:
raise exception.AuthenticationFailureException('No token provided') raise exception.AuthenticationFailureException('No token provided')
result = self._client.tokens.validate(token) try:
if not self._client.tokens.validate(token):
if not result:
raise exception.AuthenticationFailureException('Invalid token') raise exception.AuthenticationFailureException('Invalid token')
except keystoneauth1_exceptions.HttpError as e:
raise exception.AuthenticationFailureException(e.message)
return True return True

View File

@ -54,22 +54,37 @@ collector_opts = [
help='Delay in seconds between retries'), help='Delay in seconds between retries'),
] ]
keystone_opts = [
cfg.StrOpt('username',
help='Keystone service username'),
cfg.StrOpt('password',
secret=True,
help='Keystone service password'),
cfg.StrOpt('user_domain_id',
default='default',
help='Keystone service user domain ID'),
cfg.StrOpt('user_domain_name',
default='Default',
help='Keystone service user domain name'),
cfg.StrOpt('project_domain_name',
default='Default',
help='Keystone service project domain name'),
cfg.StrOpt('project_name',
default='service',
help='Keystone service project name'),
cfg.StrOpt('auth_url',
default='http://127.0.0.1:35357/v3',
help='Keystone API V3 admin endpoint'),
]
auth_opts = [ auth_opts = [
cfg.StrOpt('strategy', cfg.StrOpt('strategy',
default='private_key', default='private_key',
help='Authentication driver for the API'), help='Authentication driver for the API: private_key or keystone'),
cfg.StrOpt('private_key', cfg.StrOpt('private_key',
secret=True, secret=True,
default='secret', default='secret',
help='Private key for private key authentication'), help='Private key for private key authentication'),
cfg.StrOpt('keystone_username',
help='Keystone service username'),
cfg.StrOpt('keystone_password',
secret=True,
help='Keystone service password'),
cfg.StrOpt('keystone_url',
default='http://keystone_url:5000/v3',
help='Keystone URL'),
] ]
resource_opts = [ resource_opts = [
@ -86,6 +101,7 @@ CONF.register_opts(database_opts, group='database')
CONF.register_opts(api_opts, group='api') CONF.register_opts(api_opts, group='api')
CONF.register_opts(collector_opts, group='collector') CONF.register_opts(collector_opts, group='collector')
CONF.register_opts(auth_opts, group='auth') CONF.register_opts(auth_opts, group='auth')
CONF.register_opts(keystone_opts, group='keystone_authtoken')
CONF.register_opts(resource_opts, group='resources') CONF.register_opts(resource_opts, group='resources')
logging.register_options(CONF) logging.register_options(CONF)
@ -97,6 +113,7 @@ def list_opts():
('database', database_opts), ('database', database_opts),
('api', api_opts), ('api', api_opts),
('collector', collector_opts), ('collector', collector_opts),
('api.auth', auth_opts), ('auth', auth_opts),
('keystone_authtoken', keystone_opts),
('resources', resource_opts), ('resources', resource_opts),
] ]

View File

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from keystoneauth1 import exceptions as keystoneauth1_exceptions
import mock import mock
from almanach.api.auth import keystone_auth from almanach.api.auth import keystone_auth
@ -43,6 +44,12 @@ class KeystoneAuthenticationTest(base.BaseTestCase):
self.assertRaises(exception.AuthenticationFailureException, self.driver.validate, token) self.assertRaises(exception.AuthenticationFailureException, self.driver.validate, token)
self.validation_mock.assert_called_once_with(token) self.validation_mock.assert_called_once_with(token)
def test_with_http_error(self):
token = 'some keystone token'
self.validation_mock.side_effect = keystoneauth1_exceptions.HttpError(message='Some Error')
self.assertRaises(exception.AuthenticationFailureException, self.driver.validate, token)
self.validation_mock.assert_called_once_with(token)
def test_with_empty_token(self): def test_with_empty_token(self):
token = None token = None
self.assertRaises(exception.AuthenticationFailureException, self.driver.validate, token) self.assertRaises(exception.AuthenticationFailureException, self.driver.validate, token)

View File

@ -35,9 +35,13 @@ function almanach_configure {
iniset $ALMANACH_CONF auth strategy keystone iniset $ALMANACH_CONF auth strategy keystone
iniset $ALMANACH_CONF auth keystone_username almanach iniset $ALMANACH_CONF keystone_authtoken username almanach
iniset $ALMANACH_CONF auth keystone_password $SERVICE_PASSWORD iniset $ALMANACH_CONF keystone_authtoken password $SERVICE_PASSWORD
iniset $ALMANACH_CONF auth keystone_url $KEYSTONE_SERVICE_URI/v2.0 iniset $ALMANACH_CONF keystone_authtoken user_domain_id default
iniset $ALMANACH_CONF keystone_authtoken user_domain_name $SERVICE_DOMAIN_NAME
iniset $ALMANACH_CONF keystone_authtoken project_domain_name $SERVICE_DOMAIN_NAME
iniset $ALMANACH_CONF keystone_authtoken project_name $SERVICE_PROJECT_NAME
iniset $ALMANACH_CONF keystone_authtoken auth_url $KEYSTONE_SERVICE_URI_V3
iniset $ALMANACH_CONF collector transport_url rabbit://stackrabbit:secret@localhost:5672 iniset $ALMANACH_CONF collector transport_url rabbit://stackrabbit:secret@localhost:5672

View File

@ -76,7 +76,7 @@ Protocol
The authentication mechanism use the HTTP header :code:`X-Auth-Token` to send a token. The authentication mechanism use the HTTP header :code:`X-Auth-Token` to send a token.
This token is validated through Keystone or with the config file (private secret key). This token is validated through Keystone or with the config file (private secret key).
:: code:: raw .. code:: raw
GET /volume_types HTTP/1.1 GET /volume_types HTTP/1.1
X-Auth-Token: secret X-Auth-Token: secret
@ -93,7 +93,7 @@ Private Key Authentication
The private secret key authentication is the default method. The private secret key authentication is the default method.
In your config file, you have to define your private key in the field :code:`auth_token`: In your config file, you have to define your private key in the field :code:`auth_token`:
:: code:: raw .. code:: raw
[auth] [auth]
strategy = private_key strategy = private_key
@ -106,13 +106,34 @@ Keystone Authentication
The token will be validated with Keystone. The token will be validated with Keystone.
To use this authentication backend you have to define the authentication strategy to :code:`keystone`. To use this authentication backend you have to define the authentication strategy to :code:`keystone`.
:: code:: raw .. code:: raw
[auth] [auth]
strategy = keystone strategy = keystone
keystone_username = my_service_username
keystone_password = my_service_password [keystone_authtoken]
keystone_url = http://keystone_url:5000/v3
# Keystone service username (string value)
username = almanach
# Keystone service password (string value)
password = secret
# Keystone service user domain ID (string value)
user_domain_id = default
# Keystone service user domain name (string value)
user_domain_name = Default
# Keystone service project domain name (string value)
project_domain_name = Default
# Keystone service project name (string value)
project_name = service
# Keystone API V3 admin endpoint (string value)
auth_url = http://127.0.0.1:35357/v3
RabbitMQ configuration RabbitMQ configuration