Source code for glance.common.auth

# Copyright 2011 OpenStack Foundation
# All Rights Reserved.
#
#    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.

"""
This auth module is intended to allow OpenStack client-tools to select from a
variety of authentication strategies, including NoAuth (the default), and
Keystone (an identity management system).

::

   > auth_plugin = AuthPlugin(creds)

   > auth_plugin.authenticate()

   > auth_plugin.auth_token
    abcdefg

"""

import urllib.parse as urlparse

import httplib2
from oslo_serialization import jsonutils

from glance.common import exception
from glance.i18n import _


[docs] class BaseStrategy(object): def __init__(self): self.auth_token = None
[docs] def authenticate(self): raise NotImplementedError
@property def is_authenticated(self): raise NotImplementedError @property def strategy(self): raise NotImplementedError
[docs] class NoAuthStrategy(BaseStrategy):
[docs] def authenticate(self): pass
@property def is_authenticated(self): return True @property def strategy(self): return 'noauth'
[docs] class KeystoneStrategy(BaseStrategy): MAX_REDIRECTS = 10 def __init__(self, creds, insecure=False): self.creds = creds self.insecure = insecure super(KeystoneStrategy, self).__init__()
[docs] def check_auth_params(self): for required in ('username', 'password', 'auth_url', 'project', 'strategy', 'user_domain_id', 'project_domain_id'): if self.creds.get(required) is None: raise exception.MissingCredentialError(required=required) if self.creds['strategy'] != 'keystone': raise exception.BadAuthStrategy(expected='keystone', received=self.creds['strategy'])
[docs] def authenticate(self): """Authenticate with the Keystone service. """ def _authenticate(auth_url): # If OS_AUTH_URL is missing a trailing slash add one if not auth_url.endswith('/'): auth_url += '/' token_url = urlparse.urljoin(auth_url, "auth/tokens") self._auth(token_url) self.check_auth_params() auth_url = self.creds['auth_url'] for redirect_iter in range(self.MAX_REDIRECTS): try: _authenticate(auth_url) except exception.AuthorizationRedirect as e: # Keystone may redirect us auth_url = e.url else: # If we successfully auth'd, then memorize the correct auth_url # for future use. self.creds['auth_url'] = auth_url break else: # Guard against a redirection loop raise exception.MaxRedirectsExceeded(redirects=self.MAX_REDIRECTS)
def _auth(self, token_url): creds = { "auth": { "identity": { "methods": ["password"], "password": { "user": { "name": self.creds['username'], "domain": {"id": self.creds['user_domain_id']}, "password": self.creds['password'] } } }, "scope": { "project": { "name": self.creds['project'], "domain": { "id": self.creds['project_domain_id'] } } } } } headers = {'Content-Type': 'application/json'} req_body = jsonutils.dumps(creds) resp, _ = self._do_request( token_url, 'POST', headers=headers, body=req_body) if resp.status == 201: self.auth_token = resp['x-subject-token'] elif resp.status == 305: raise exception.RedirectException(resp['location']) elif resp.status == 400: raise exception.AuthBadRequest(url=token_url) elif resp.status == 401: raise exception.NotAuthenticated() else: raise Exception(_('Unknown response code: %d') % resp.status) @property def is_authenticated(self): return self.auth_token is not None @property def strategy(self): return 'keystone' def _do_request(self, url, method, headers=None, body=None): headers = headers or {} conn = httplib2.Http() conn.force_exception_to_status_code = True conn.disable_ssl_certificate_validation = self.insecure headers['User-Agent'] = 'glance-client' resp, resp_body = conn.request(url, method, headers=headers, body=body) return resp, resp_body
[docs] def get_plugin_from_strategy(strategy, creds=None, insecure=False): if strategy == 'noauth': return NoAuthStrategy() elif strategy == 'keystone': return KeystoneStrategy(creds, insecure) else: raise Exception(_("Unknown auth strategy '%s'") % strategy)