blob: d591f03b42d12a286a5dca300268bb3cada7f76d [file] [log] [blame]
Matthew Treinish9e26ca82016-02-23 11:43:20 -05001# Copyright 2015 NEC Corporation. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15from oslo_log import log as logging
16from oslo_serialization import jsonutils as json
17
18from tempest.lib.common import rest_client
19from tempest.lib import exceptions
20
21
22class V3TokenClient(rest_client.RestClient):
23
24 def __init__(self, auth_url, disable_ssl_certificate_validation=None,
zhufl071e94c2016-07-12 10:26:34 +080025 ca_certs=None, trace_requests=None, **kwargs):
Andrea Frittoli8b8db532016-12-22 11:21:47 +000026 """Initialises the Token client
27
28 :param auth_url: URL to which the token request is sent
29 :param disable_ssl_certificate_validation: pass-through to rest client
30 :param ca_certs: pass-through to rest client
31 :param trace_requests: pass-through to rest client
32 :param kwargs: any extra parameter to pass through the rest client.
33 Three kwargs are forbidden: region, service and auth_provider
34 as they are not meaningful for token client
35 """
Matthew Treinish9e26ca82016-02-23 11:43:20 -050036 dscv = disable_ssl_certificate_validation
Andrea Frittoli8b8db532016-12-22 11:21:47 +000037 for unwanted_kwargs in ['region', 'service', 'auth_provider']:
38 kwargs.pop(unwanted_kwargs, None)
Matthew Treinish9e26ca82016-02-23 11:43:20 -050039 super(V3TokenClient, self).__init__(
40 None, None, None, disable_ssl_certificate_validation=dscv,
zhufl071e94c2016-07-12 10:26:34 +080041 ca_certs=ca_certs, trace_requests=trace_requests, **kwargs)
Matthew Treinish9e26ca82016-02-23 11:43:20 -050042
43 if auth_url is None:
44 raise exceptions.IdentityError("Couldn't determine auth_url")
45
46 if 'auth/tokens' not in auth_url:
47 auth_url = auth_url.rstrip('/') + '/auth/tokens'
48
49 self.auth_url = auth_url
50
51 def auth(self, user_id=None, username=None, password=None, project_id=None,
52 project_name=None, user_domain_id=None, user_domain_name=None,
53 project_domain_id=None, project_domain_name=None, domain_id=None,
Colleen Murphy0e52d4e2018-02-17 21:29:40 +010054 domain_name=None, token=None, app_cred_id=None,
55 app_cred_secret=None):
Matthew Treinish9e26ca82016-02-23 11:43:20 -050056 """Obtains a token from the authentication service
57
58 :param user_id: user id
59 :param username: user name
60 :param user_domain_id: the user domain id
61 :param user_domain_name: the user domain name
62 :param project_domain_id: the project domain id
63 :param project_domain_name: the project domain name
64 :param domain_id: a domain id to scope to
65 :param domain_name: a domain name to scope to
66 :param project_id: a project id to scope to
67 :param project_name: a project name to scope to
68 :param token: a token to re-scope.
69
70 Accepts different combinations of credentials.
71 Sample sample valid combinations:
72 - token
73 - token, project_name, project_domain_id
74 - user_id, password
75 - username, password, user_domain_id
76 - username, password, project_name, user_domain_id, project_domain_id
77 Validation is left to the server side.
78 """
79 creds = {
80 'auth': {
81 'identity': {
82 'methods': [],
83 }
84 }
85 }
86 id_obj = creds['auth']['identity']
87 if token:
88 id_obj['methods'].append('token')
89 id_obj['token'] = {
90 'id': token
91 }
92
93 if (user_id or username) and password:
94 id_obj['methods'].append('password')
95 id_obj['password'] = {
96 'user': {
97 'password': password,
98 }
99 }
100 if user_id:
101 id_obj['password']['user']['id'] = user_id
102 else:
103 id_obj['password']['user']['name'] = username
104
105 _domain = None
106 if user_domain_id is not None:
107 _domain = dict(id=user_domain_id)
108 elif user_domain_name is not None:
109 _domain = dict(name=user_domain_name)
110 if _domain:
111 id_obj['password']['user']['domain'] = _domain
112
Colleen Murphy0e52d4e2018-02-17 21:29:40 +0100113 if app_cred_id and app_cred_secret:
114 id_obj['methods'].append('application_credential')
115 id_obj['application_credential'] = {
116 'id': app_cred_id,
117 'secret': app_cred_secret,
118 }
119
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500120 if (project_id or project_name):
121 _project = dict()
122
123 if project_id:
124 _project['id'] = project_id
125 elif project_name:
126 _project['name'] = project_name
127
128 if project_domain_id is not None:
129 _project['domain'] = {'id': project_domain_id}
130 elif project_domain_name is not None:
131 _project['domain'] = {'name': project_domain_name}
132
133 creds['auth']['scope'] = dict(project=_project)
134 elif domain_id:
135 creds['auth']['scope'] = dict(domain={'id': domain_id})
136 elif domain_name:
137 creds['auth']['scope'] = dict(domain={'name': domain_name})
138
139 body = json.dumps(creds, sort_keys=True)
140 resp, body = self.post(self.auth_url, body=body)
141 self.expected_success(201, resp.status)
142 return rest_client.ResponseBody(resp, body)
143
144 def request(self, method, url, extra_headers=False, headers=None,
Jordan Pittier4408c4a2016-04-29 15:05:09 +0200145 body=None, chunked=False):
146 """A simple HTTP request interface.
147
148 Note: this overloads the `request` method from the parent class and
149 thus must implement the same method signature.
150 """
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500151 if headers is None:
152 # Always accept 'json', for xml token client too.
153 # Because XML response is not easily
154 # converted to the corresponding JSON one
155 headers = self.get_headers(accept_type="json")
156 elif extra_headers:
157 try:
158 headers.update(self.get_headers(accept_type="json"))
159 except (ValueError, TypeError):
160 headers = self.get_headers(accept_type="json")
161
162 resp, resp_body = self.raw_request(url, method,
163 headers=headers, body=body)
164 self._log_request(method, url, resp, req_headers=headers,
165 req_body='<omitted>', resp_body=resp_body)
166
167 if resp.status in [401, 403]:
168 resp_body = json.loads(resp_body)
169 raise exceptions.Unauthorized(resp_body['error']['message'])
170 elif resp.status not in [200, 201, 204]:
171 raise exceptions.IdentityError(
172 'Unexpected status code {0}'.format(resp.status))
173
174 return resp, json.loads(resp_body)
175
176 def get_token(self, **kwargs):
177 """Returns (token id, token data) for supplied credentials"""
178
179 auth_data = kwargs.pop('auth_data', False)
180
181 if not (kwargs.get('user_domain_id') or
182 kwargs.get('user_domain_name')):
183 kwargs['user_domain_name'] = 'Default'
184
185 if not (kwargs.get('project_domain_id') or
186 kwargs.get('project_domain_name')):
187 kwargs['project_domain_name'] = 'Default'
188
189 body = self.auth(**kwargs)
190
191 token = body.response.get('x-subject-token')
192 if auth_data:
193 return token, body['token']
194 else:
195 return token
196
197
198class V3TokenClientJSON(V3TokenClient):
199 LOG = logging.getLogger(__name__)
200
201 def _warn(self):
Jordan Pittier525ec712016-12-07 17:51:26 +0100202 self.LOG.warning("%s class was deprecated and renamed to %s",
203 self.__class__.__name__, 'V3TokenClient')
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500204
205 def __init__(self, *args, **kwargs):
206 self._warn()
207 super(V3TokenClientJSON, self).__init__(*args, **kwargs)