Added ability to add a user to a role.
[snaps.git] / snaps / openstack / utils / keystone_utils.py
1 # Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import logging
16
17 from keystoneclient.client import Client
18 from keystoneauth1.identity import v3, v2
19 from keystoneauth1 import session
20 import requests
21
22 from snaps.domain.user import User
23
24 logger = logging.getLogger('keystone_utils')
25
26 V2_VERSION = 'v2.0'
27
28
29 def get_session_auth(os_creds):
30     """
31     Return the session auth for keystone session
32     :param os_creds: the OpenStack credentials (OSCreds) object
33     :return: the auth
34     """
35     if os_creds.identity_api_version == 3:
36         auth = v3.Password(auth_url=os_creds.auth_url,
37                            username=os_creds.username,
38                            password=os_creds.password,
39                            project_name=os_creds.project_name,
40                            user_domain_id=os_creds.user_domain_id,
41                            project_domain_id=os_creds.project_domain_id)
42     else:
43         auth = v2.Password(auth_url=os_creds.auth_url,
44                            username=os_creds.username,
45                            password=os_creds.password,
46                            tenant_name=os_creds.project_name)
47     return auth
48
49
50 def keystone_session(os_creds):
51     """
52     Creates a keystone session used for authenticating OpenStack clients
53     :param os_creds: The connection credentials to the OpenStack API
54     :return: the client object
55     """
56     logger.debug('Retrieving Keystone Session')
57
58     auth = get_session_auth(os_creds)
59
60     req_session = None
61     if os_creds.proxy_settings:
62         req_session = requests.Session()
63         req_session.proxies = {
64             'http':
65                 os_creds.proxy_settings.host + ':' +
66                 os_creds.proxy_settings.port}
67     return session.Session(auth=auth, session=req_session,
68                            verify=os_creds.cacert)
69
70
71 def keystone_client(os_creds):
72     """
73     Returns the keystone client
74     :param os_creds: the OpenStack credentials (OSCreds) object
75     :return: the client
76     """
77     return Client(
78         version=os_creds.identity_api_version,
79         session=keystone_session(os_creds), interface=os_creds.interface)
80
81
82 def get_endpoint(os_creds, service_type, interface='public'):
83     """
84     Returns the endpoint of specific service
85     :param os_creds: the OpenStack credentials (OSCreds) object
86     :param service_type: the type of specific service
87     :param interface: the type of interface
88     :return: the endpoint url
89     """
90     auth = get_session_auth(os_creds)
91     key_session = keystone_session(os_creds)
92     return key_session.get_endpoint(
93         auth=auth, service_type=service_type, interface=interface)
94
95
96 def get_project(keystone=None, os_creds=None, project_name=None):
97     """
98     Returns the first project object or None if not found
99     :param keystone: the Keystone client
100     :param os_creds: the OpenStack credentials used to obtain the Keystone
101                      client if the keystone parameter is None
102     :param project_name: the name to query
103     :return: the ID or None
104     """
105     if not project_name:
106         return None
107
108     if not keystone:
109         if os_creds:
110             keystone = keystone_client(os_creds)
111         else:
112             raise Exception('Cannot lookup project without the proper '
113                             'credentials')
114
115     if keystone.version == V2_VERSION:
116         projects = keystone.tenants.list()
117     else:
118         projects = keystone.projects.list(**{'name': project_name})
119
120     for project in projects:
121         if project.name == project_name:
122             return project
123
124     return None
125
126
127 def create_project(keystone, project_settings):
128     """
129     Creates a project
130     :param keystone: the Keystone client
131     :param project_settings: the project configuration
132     :return:
133     """
134     if keystone.version == V2_VERSION:
135         return keystone.tenants.create(
136             project_settings.name, project_settings.description,
137             project_settings.enabled)
138
139     return keystone.projects.create(
140         project_settings.name, project_settings.domain,
141         description=project_settings.description,
142         enabled=project_settings.enabled)
143
144
145 def delete_project(keystone, project):
146     """
147     Deletes a project
148     :param keystone: the Keystone clien
149     :param project: the OpenStack project object
150     """
151     if keystone.version == V2_VERSION:
152         keystone.tenants.delete(project)
153     else:
154         keystone.projects.delete(project)
155
156
157 def get_os_user(keystone, user):
158     """
159     Returns the OpenStack user object
160     :param keystone: the Keystone client object
161     :param user: the SNAPS-OO User domain object
162     :return:
163     """
164     return keystone.users.get(user.id)
165
166
167 def get_user(keystone, username, project_name=None):
168     """
169     Returns a user for a given name and optionally project
170     :param keystone: the keystone client
171     :param username: the username to lookup
172     :param project_name: the associated project (optional)
173     :return: a SNAPS-OO User domain object or None
174     """
175     project = get_project(keystone=keystone, project_name=project_name)
176
177     if project:
178         users = keystone.users.list(tenant_id=project.id)
179     else:
180         users = keystone.users.list()
181
182     for user in users:
183         if user.name == username:
184             return User(name=user.name, user_id=user.id)
185
186     return None
187
188
189 def create_user(keystone, user_settings):
190     """
191     Creates a user
192     :param keystone: the Keystone client
193     :param user_settings: the user configuration
194     :return: a SNAPS-OO User domain object
195     """
196     project = None
197     if user_settings.project_name:
198         project = get_project(keystone=keystone,
199                               project_name=user_settings.project_name)
200
201     if keystone.version == V2_VERSION:
202         project_id = None
203         if project:
204             project_id = project.id
205         os_user = keystone.users.create(
206             name=user_settings.name, password=user_settings.password,
207             email=user_settings.email, tenant_id=project_id,
208             enabled=user_settings.enabled)
209     else:
210         os_user = keystone.users.create(
211             name=user_settings.name, password=user_settings.password,
212             email=user_settings.email, project=project,
213             domain=user_settings.domain_name, enabled=user_settings.enabled)
214
215     for role_name, role_project in user_settings.roles.items():
216         os_role = get_os_role_by_name(keystone, role_name)
217         os_project = get_project(keystone=keystone, project_name=role_project)
218
219         if os_role and os_project:
220             existing_roles = get_os_roles_by_user(keystone, os_user,
221                                                   os_project)
222             found = False
223             for role in existing_roles:
224                 if role.id == os_role.id:
225                     found = True
226
227             if not found:
228                 grant_user_role_to_project(
229                     keystone=keystone, user=os_user, role=os_role,
230                     project=os_project)
231
232     if os_user:
233         return User(name=os_user.name, user_id=os_user.id)
234
235
236 def delete_user(keystone, user):
237     """
238     Deletes a user
239     :param keystone: the Keystone client
240     :param user: the SNAPS-OO User domain object
241     """
242     keystone.users.delete(user.id)
243
244
245 def get_os_role_by_name(keystone, name):
246     """
247     Returns an OpenStack role object of a given name or None if not exists
248     :param keystone: the keystone client
249     :param name: the role name
250     :return: the OpenStack role object
251     """
252     roles = keystone.roles.list()
253     for role in roles:
254         if role.name == name:
255             return role
256
257
258 def get_os_roles_by_user(keystone, user, project):
259     """
260     Returns a list of OpenStack role object associated with a user
261     :param keystone: the keystone client
262     :param user: the OpenStack user object
263     :param project: the OpenStack project object (only required for v2)
264     :return: a list of OpenStack role objects
265     """
266     if keystone.version == V2_VERSION:
267         os_user = get_os_user(keystone, user)
268         roles = keystone.roles.roles_for_user(os_user, project)
269         return roles
270     else:
271         return keystone.roles.list(user=user, project=project)
272
273
274 def get_os_role_by_id(keystone, role_id):
275     """
276     Returns an OpenStack role object of a given name or None if not exists
277     :param keystone: the keystone client
278     :param role_id: the role ID
279     :return: the OpenStack role object
280     """
281     return keystone.roles.get(role_id)
282
283
284 def create_role(keystone, name):
285     """
286     Creates an OpenStack role
287     :param keystone: the keystone client
288     :param name: the role name
289     :return:
290     """
291     return keystone.roles.create(name)
292
293
294 def delete_role(keystone, role):
295     """
296     Deletes an OpenStack role
297     :param keystone: the keystone client
298     :param role: the role to delete
299     :return:
300     """
301     keystone.roles.delete(role)
302
303
304 def grant_user_role_to_project(keystone, role, user, project):
305     """
306     Grants user and role to a project
307     :param keystone: the Keystone client
308     :param role: the role used to join a project/user
309     :param user: the user to add to the project (SNAPS-OO User Domain object
310     :param project: the project to which to add a user
311     :return:
312     """
313     if keystone.version == V2_VERSION:
314         keystone.roles.add_user_role(user, role, tenant=project)
315     else:
316         keystone.roles.grant(role, user=user, project=project)