3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
16 import access_management.db.amdb as amdb
17 from am_api_base import *
18 from keystoneauth1 import exceptions
19 from cmframework.apis import cmclient
22 class Users(AMApiBase):
25 User create operations
27 .. :quickref: Users;User create operations
29 .. http:post:: /am/v1/users
37 POST am/v1/users HTTP/1.1
38 Host: haproxyvip:61200
39 Accept: application/json
42 "password": "Passwd_1",
43 "email": "test@mail.com",
44 "project": "10f8fa2c6efe409d8207517128f03265",
48 :> json string username: The created user name.
49 :> json string password: The user's password.
50 :> json string email: The user's e-mail.
51 :> json string project: ID of the project to be set as primary project for the user.
52 :> json string description: The user's description.
68 :> json int code: the status code
69 :> json string description: the error description, present if code is non zero
70 :> json object data: a dictionary with the created user's id
71 :> json string id: The created user's id.
75 .. :quickref: Users;Users list operations
77 .. http:get:: /am/v1/users
85 GET am/v1/users HTTP/1.1
86 Host: haproxyvip:61200
87 Accept: application/json
99 "0edf341a27544c349b7c37bb76ab25d1":
102 "id": "0edf341a27544c349b7c37bb76ab25d1",
104 "password_expires_at": null
106 "32e8859519f94b1ea80f61d53d17e74e":
109 "id": "32e8859519f94b1ea80f61d53d17e74e",
111 "password_expires_at": null
116 :> json int code: the status code
117 :> json string description: the error description, present if code is non zero
118 :> json object data: The existing users.
119 :> json string enabled: The user's state.
120 :> json string id: The user's id.
121 :> json string name: The user's name.
122 :> json string password_expires_at: The user's password expiration date.
124 User delete operations
126 .. :quickref: Users;User delete operations
128 .. http:delete:: /am/v1/users
130 **Start User delete**
136 DELETE am/v1/users HTTP/1.1
137 Host: haproxyvip:61200
138 Accept: application/json
140 "user": <uuid> or <username>
143 :> json string user: The removed user's id or user name.
145 **Example response**:
152 "description": "User deleted!"
155 :> json int code: the status code
156 :> json string description: the error description, present if code is non zero
159 endpoints = ['users']
160 parser_arguments = ['username',
168 self.logger.info("Received a user create request!")
169 args = self.parse_args()
171 if args["email"] is not None:
172 if re.match("^[\.a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-z]+$", args["email"]) is None:
173 self.logger.error("E-mail validation failed!")
174 return AMApiBase.embed_data({}, 1, "E-mail validation failed!")
176 if self.id_validator(args["username"]):
177 self.logger.error("{0} username is invalid, because cannot assign a valid uuid to it.".format(args["username"]))
178 return AMApiBase.embed_data({}, 1, "{0} username is invalid, because cannot assign a valid uuid to it.".format(args["username"]))
181 projectidstate = self.id_validator(args["project"])
182 if projectidstate == False:
183 self.logger.error("Project id validation failed")
184 return AMApiBase.embed_data({}, 1, "Project id validation failed")
186 if re.match("^[a-zA-Z0-9_-]+$", args["username"]) is None:
187 self.logger.error("Username validation failed!")
188 return AMApiBase.embed_data({}, 1, "Username validation failed!")
190 passstate = self.passwd_validator(args["password"])
191 if passstate is not None:
192 self.logger.error(passstate)
193 return AMApiBase.embed_data({}, 1, passstate)
195 state, result = self._create_user(args)
197 self.logger.info("User created!")
198 return AMApiBase.embed_data({"id": result}, 0, "")
200 return AMApiBase.embed_data({}, 1, result)
203 self.logger.info("Received a user list request!")
206 self.keystone = self.auth_keystone()
207 u_list = self.keystone.users.list()
208 except Exception as ex:
209 self.logger.error("{0}".format(ex))
210 return False, "{0}".format(ex)
212 for element in u_list:
213 user_list.update({element.id : element._info})
215 self.logger.info("The user list response done!")
216 return AMApiBase.embed_data(user_list, 0, "User list.")
219 self.logger.info("Received a user delete request!")
220 args = self.parse_args()
222 state, user_info = self.get_uuid_and_name(args["user"])
224 token_owner = self.get_uuid_from_token()
225 if user_info["id"] == token_owner:
226 self.logger.error("The {0} user tried to delete own account!".format(user_info["id"]))
227 return AMApiBase.embed_data({}, 1, "You cannot delete your own account!")
229 state, message = self._delete_user(user_info)
232 self.logger.info("User deleted!")
233 return AMApiBase.embed_data({}, 0, "User deleted!")
235 self.logger.error(message)
236 return AMApiBase.embed_data({}, 1, message)
238 self.logger.error(user_info)
239 return AMApiBase.embed_data({}, 1, user_info)
241 def _delete_user(self, user_info):
242 state, name = self._delete_user_from_db(user_info)
244 self.logger.info("User removed from the db!")
246 self.keystone.users.delete(user_info["id"])
247 except exceptions.http.NotFound as ex:
248 self.logger.info("{0} user does not exist in the keystone!".format(user_info["name"]))
249 return True, "Done, but this user didn't exist in the keystone!"
250 except Exception as ex:
251 self.logger.error("{0}".format(ex))
252 return False, "{0}".format(ex)
257 def _delete_user_from_db(self, user_info):
258 state_open, message_open = self._open_db()
261 roles = self.db.get_user_roles(user_info["id"])
264 if self.db.is_chroot_role(role):
265 self.logger.debug("This user has a chroot role.")
267 self.remove_chroot_linux_role_handling(user_info["id"], "Chroot", "cloud.chroot")
269 if self.check_chroot_linux_state(user_info["name"], "cloud.chroot", "absent"):
270 self.db.delete_user(user_info["id"])
271 return True, user_info["name"]
273 if role == "linux_user":
274 self.logger.debug("This user has a linux_user role!")
276 self.remove_chroot_linux_role_handling(user_info["id"], "Linux", "cloud.linuxuser")
278 if self.check_chroot_linux_state(user_info["name"], "cloud.linuxuser", "absent"):
279 self.db.delete_user(user_info["id"])
280 return True, user_info["name"]
282 self.db.delete_user(user_info["id"])
283 except amdb.NotAllowedOperation:
284 self.logger.error("Deleting service user is not allowed: {0}".format(user_info["name"]))
285 return False, "Deleting service user is not allowed: {0}".format(user_info["name"])
286 except amdb.NotExist:
287 self.logger.info("The {0} user does not exist!".format(user_info["name"]))
289 except Exception as ex:
290 self.logger.error("Internal error: {0}".format(ex))
291 return False, "Internal error: {0}".format(ex)
293 state_close, message_close = self._close_db()
296 return True, user_info["name"]
298 return False, message_open
300 def _create_user(self, args):
302 ks_member_roleid = self.get_role_id(defaults.KS_MEMBER_NAME)
303 if ks_member_roleid is None:
304 self.logger.error("Member user role not found!")
305 return False, "Member user role not found!"
307 roles.append(ks_member_roleid)
308 basic_member_roleid = self.get_role_id(defaults.AM_MEMBER_NAME)
309 if basic_member_roleid is None:
310 self.logger.error("basic_member user role not found!")
311 return False, "basic_member user role not found!"
313 roles.append(basic_member_roleid)
315 um_proj_id = self.get_project_id(defaults.PROJECT_NAME)
316 if um_proj_id is None:
317 self.logger.error("The user management project is not found!")
318 return False, "The user management project is not found!"
320 if args["email"] is None:
321 args["email"] = 'None'
322 if args["project"] is None:
323 args["project"] = um_proj_id
326 c_user_out = self.keystone.users.create(name=args["username"], password=args["password"], email=args["email"], default_project=args["project"], description=args["description"])
327 except exceptions.http.Conflict as ex:
328 self.logger.error("{0}".format(ex))
329 return False, "This user exists in the keystone!"
330 except Exception as ex:
331 self.logger.error("{0}".format(ex))
332 return False, "{0}".format(ex)
335 state, message = self._add_basic_roles(um_proj_id, ID, roles)
337 return False, message
338 if args["project"] != um_proj_id:
339 state, message = self._add_basic_roles(args["project"], ID, [ks_member_roleid])
341 return False, message
342 return self._create_user_in_db(ID, args)
344 def _add_basic_roles(self, project, ID, roles):
347 self.keystone.roles.grant(role, user=ID, project=project)
350 self.keystone.roles.grant(role, user=ID, project=project)
351 except Exception as ex:
352 self.logger.error("{0}".format(ex))
353 self.keystone.users.delete(ID)
354 return False, "{0}".format(ex)
357 def _create_user_in_db(self, ID, args):
358 state_open, message_open = self._open_db()
361 self.db.create_user(ID, args["username"])
362 self.db.add_user_role(ID, defaults.AM_MEMBER_NAME)
363 except amdb.AlreadyExist as ex1:
364 self.logger.error("User already exists in table!")
366 self.keystone.users.delete(ID)
367 self.db.delete_user(ID)
368 except amdb.NotAllowedOperation as ex2:
369 self.logger.error("Internal error: Except1: {0}, Except2: {1}".format(ex1, ex2))
370 return False, "Except1: {0}, Except2: {1}".format(ex1, ex2)
371 except Exception as ex3:
372 self.logger.error("Internal error: Except1: {0}, Except2: {1}".format(ex1, ex3))
373 return False, "Except1: {0}, Except2: {1}".format(ex1, ex3)
374 return False, "User already exists!"
375 except Exception as ex:
376 self.logger.error("Internal error: {0}".format(ex))
378 self.keystone.users.delete(ID)
379 except exceptions.http.NotFound as ex:
380 self.logger.error("{0}".format(ex))
381 return False, "This user does not exist in the keystone!"
382 except Exception as ex:
383 self.logger.error("{0}".format(ex))
384 return False, "{0}".format(ex)
385 return False, "Internal error: {0}".format(ex)
387 state_close, message_close = self._close_db()
392 return False, message_open
394 def remove_chroot_linux_role_handling(self, user_id, user_type, list_name):
395 cmc = cmclient.CMClient()
396 user_list = cmc.get_property(list_name)
397 user_list = json.loads(user_list)
398 self.logger.debug("{0} user list before the change: {1}".format(user_type, json.dumps(user_list)))
399 if user_list is not None:
400 self.logger.debug("The {0} user list exists!".format(user_type))
401 username, def_project = self.get_user_from_uuid(user_id)
402 self.logger.debug("User name: {0}".format(username))
403 for val in user_list:
404 if val["name"] == username:
405 val["public_key"] = ""
406 val["state"] = "absent"
407 val["remove"] = "yes"
410 self.logger.debug("{0} user list after the change: {1}".format(user_type, json.dumps(user_list)))
411 cmc.set_property(list_name, json.dumps(user_list))