usersdb.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. #!/usr/bin/python
  2. class UsersDB(object):
  3. def __init__(self, module):
  4. self.module = module
  5. self.users_db = self.module.params["usersdb"]
  6. self.source_user_db = self.module.params["source_userdb"]
  7. # If we have to userdb and source db lets merge them if not
  8. if self.users_db and self.source_user_db:
  9. self.users_db.update(self.source_user_db)
  10. if self.source_user_db and not self.users_db:
  11. self.users_db = self.source_user_db
  12. if not self.users_db and not self.source_user_db:
  13. self.module.fail_json(msg="Missing argument. You must defined either 'usersdb' or 'source_userdb'.")
  14. self.teams_db = self.module.params["teamsdb"]
  15. self.servers_db = self.module.params["serversdb"]
  16. # Databases
  17. self.lookup_key_db = {} # used for quick lookup
  18. self.expanded_users_db = [] # Used in simple mode
  19. self.expanded_users_key_db = [] # Used in simple mode
  20. self.expanded_server_db = [] # Used in advanced mode for merged User + server
  21. self.expanded_server_key_db = [] # Used in advanced mode
  22. def _concat_keys(self, user_name, user_keys=None, server_keys=None, user_status=False):
  23. # Concat keys (if possible) and update username to keys
  24. new_user_keys = []
  25. if user_keys and server_keys:
  26. for user_key in user_keys:
  27. new_user_key = dict(user_key, **server_keys)
  28. new_user_key.pop("name", None)
  29. new_user_key.pop("user", None)
  30. if user_status:
  31. new_user_key.update({"state": "absent"})
  32. new_user_keys.append(new_user_key)
  33. elif server_keys:
  34. # server key is dic
  35. server_keys.pop("name", None)
  36. new_user_keys = [server_keys]
  37. elif user_keys:
  38. for user_key in user_keys:
  39. new_user_key = user_key
  40. new_user_key.pop("user", None)
  41. new_user_key.pop("name", None)
  42. if user_status:
  43. new_user_key.update({"state": "absent"})
  44. new_user_keys.append(new_user_key)
  45. else:
  46. # TODO: Should work without keys
  47. # self.module.fail_json(msg="user '{}' list has no keys defined.".format(user_name))
  48. pass
  49. return new_user_keys
  50. def _merge_key(self, user_keys, sever_keys, user_name, user_status=False):
  51. # Rules ( no real merge happens )
  52. # 1- Default use the user key
  53. # 2- if server has defined keys then use those instead no merge here
  54. if sever_keys:
  55. merged_keys = []
  56. for server_key in sever_keys:
  57. if "user" in server_key:
  58. user = server_key.pop("user", False)
  59. account_key = self.lookup_key_db.get(user)
  60. user_definition = self.users_db.get(user, {})
  61. if user_definition.get("state", "present") in ("absent", "delete", "deleted", "remove", "removed"):
  62. user_status = "absent"
  63. merged_keys += self._concat_keys(user_name, account_key, server_key, user_status=user_status)
  64. elif "team" in server_key:
  65. self.module.fail_json(msg="Team key is not yet implemented")
  66. elif "key" in server_key:
  67. merged_keys += self._concat_keys(user_name, server_keys=server_key, user_status=user_status)
  68. else:
  69. # TODO: Should work without keys
  70. # self.module.fail_json(msg="user '{}' list has no keys defined.".format(user_name))
  71. pass
  72. return merged_keys
  73. else:
  74. return self._concat_keys(user_name, user_keys=user_keys, user_status=user_status)
  75. def _merge_user(self, user_name, user_server):
  76. user_definition = self.users_db.get(user_name, False)
  77. if not user_definition:
  78. self.module.fail_json(msg="'%s' user has no definition" % user_name)
  79. if user_definition.get("state", "present") in ("absent", "delete", "deleted", "remove", "removed"):
  80. user_status = "absent"
  81. else:
  82. user_status = False
  83. # Merge User and Server ( Server has precedence in this case )
  84. merged_user = dict(user_definition.items() + user_server.items())
  85. if user_status:
  86. merged_user.update({"state": "absent"})
  87. user_server = merged_user
  88. user_db_key = self.lookup_key_db.get(user_name, None)
  89. user_server_keys = self._merge_key(user_db_key, user_server.get("keys", None), user_name, user_status)
  90. # In case of team user dict will not be defined so lets just define anyway
  91. user_server.update({"user": user_name})
  92. # Populate DBs
  93. user_server.pop("keys", None) # Get rid of keys
  94. user_server.pop("team", None) # Get rid of team if exists
  95. self.expanded_server_db.append(user_server)
  96. if len(user_server_keys) > 0:
  97. self.expanded_server_key_db.append({"user": user_name, "keys": user_server_keys})
  98. def expand_servers(self):
  99. # Advanced mode Merges users and servers data
  100. # Expand server will overwrite same attributes defined in user db except for state = "absent"
  101. for user_server in self.servers_db:
  102. team_name = user_server.get("team", False)
  103. user_name = user_server.get("user", False) or user_server.get("name", False)
  104. if user_name:
  105. self._merge_user(user_name, user_server)
  106. elif team_name:
  107. team_definition = self.teams_db.get(team_name, False)
  108. if not team_definition:
  109. self.module.fail_json(msg="'%s' team has no definition" % team_name)
  110. for user_in_team in team_definition:
  111. self._merge_user(user_in_team, user_server)
  112. else:
  113. self.module.fail_json(msg="Your server definition has no user or team. Please check your data type. "
  114. "for '{}'".format(user_server))
  115. def expand_keys(self, keys, user):
  116. # if len(keys) == 0:
  117. # # TODO: Should work without keys
  118. # self.module.fail_json(msg="user '{}' has no keys defined.".format(user))
  119. user_keys = []
  120. # If key is not a list than its a raw key string
  121. if not isinstance(keys, list):
  122. user_keys.append({"key": keys})
  123. else:
  124. for key in keys:
  125. # Basic syntax check
  126. if isinstance(key, basestring) and "ssh-" in key:
  127. user_keys.append({"key": key})
  128. elif "key" not in key and "name" not in key:
  129. # TODO: Should work without keys
  130. # self.module.fail_json(msg="user '{}' list has no keys defined.".format(key.keys()))
  131. pass
  132. else:
  133. # All is okay just add the dict
  134. user_keys.append(key)
  135. return user_keys
  136. def expand_users(self):
  137. # Get User database which is a dic and create expendaded_user_db and key_db
  138. # Put keys in right dictionary format
  139. for username, user_options in self.users_db.iteritems():
  140. # 1- Convert dic to list (servers_db style)
  141. user = {"name": username} # create the account name
  142. user.update(user_options) # update all other option
  143. # 2- Compile key
  144. unformatted_keys = user_options.get("keys", [])
  145. keys = self.expand_keys(unformatted_keys, user)
  146. # 3- remove keys from userdb if exists
  147. user.pop("keys", None)
  148. # 4- Populate DBs
  149. self.expanded_users_db.append(user) # Populate new list user db
  150. self.expanded_users_key_db.append({"user": username, "keys": keys})
  151. if len(keys) > 0:
  152. self.lookup_key_db.update({username: keys}) # Populate dict key db
  153. def main(self):
  154. self.expand_users()
  155. if self.servers_db and len(self.servers_db) > 0:
  156. # Advanced mode we have to do merges and stuff :D
  157. self.expand_servers()
  158. result = {"changed": False, "msg": "",
  159. "users_db": self.expanded_server_db,
  160. "key_db": self.expanded_server_key_db}
  161. else:
  162. # Simple mode no servers db
  163. result = {"changed": False, "msg": "",
  164. "users_db": self.expanded_users_db,
  165. "key_db": self.expanded_users_key_db}
  166. self.module.exit_json(**result)
  167. def main():
  168. module = AnsibleModule(
  169. argument_spec=dict(
  170. usersdb=dict(default=None, required=False, type="dict"),
  171. source_userdb=dict(default=None, required=False, type="dict"),
  172. teamsdb=dict(default=None, required=False), # Should be dict but would break if value is false/none
  173. serversdb=dict(default=None, required=False),
  174. ),
  175. supports_check_mode=False
  176. )
  177. UsersDB(module).main()
  178. # import module snippets
  179. from ansible.module_utils.basic import *
  180. main()