importer.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import requests
  2. import tablib
  3. from django.db.models import Q
  4. from django.urls import reverse
  5. from organizations_ext.admin import OrganizationResource, OrganizationUserResource
  6. from organizations_ext.models import OrganizationUser, OrganizationUserRole
  7. from projects.admin import ProjectKeyResource, ProjectResource
  8. from projects.models import Project
  9. from teams.admin import TeamResource
  10. from users.admin import UserResource
  11. from users.models import User
  12. from .exceptions import ImporterException
  13. class GlitchTipImporter:
  14. """
  15. Generic importer tool to use with cli or web
  16. If used by a non server admin, it's important to assume all incoming
  17. JSON is hostile and not from a real GT server. Foreign Key ids could be
  18. faked and used to elevate privileges. Always confirm new data is associated with
  19. appropriate organization. Also assume user is at least an org admin, no need to
  20. double check permissions when creating assets within the organization.
  21. create_users should be False unless running as superuser/management command
  22. """
  23. def __init__(
  24. self, url: str, auth_token: str, organization_slug: str, create_users=False
  25. ):
  26. self.api_root_url = reverse("api-root-view")
  27. self.url = url
  28. self.headers = {"Authorization": f"Bearer {auth_token}"}
  29. self.create_users = create_users
  30. self.organization_slug = organization_slug
  31. self.organization_id = None
  32. self.organization_url = reverse(
  33. "organization-detail", kwargs={"slug": self.organization_slug}
  34. )
  35. self.organization_users_url = reverse(
  36. "organization-users-list",
  37. kwargs={"organization_slug": self.organization_slug},
  38. )
  39. self.projects_url = reverse(
  40. "organization-projects-list",
  41. kwargs={"organization_slug": self.organization_slug},
  42. )
  43. self.teams_url = reverse(
  44. "organization-teams-list",
  45. kwargs={"organization_slug": self.organization_slug},
  46. )
  47. def run(self, organization_id=None):
  48. """Set organization_id to None to import (superuser only)"""
  49. if organization_id is None:
  50. self.import_organization()
  51. else:
  52. self.organization_id = organization_id
  53. self.import_organization_users()
  54. self.import_projects()
  55. self.import_teams()
  56. def get(self, url):
  57. return requests.get(url, headers=self.headers)
  58. def import_organization(self):
  59. resource = OrganizationResource()
  60. res = self.get(self.url + self.organization_url)
  61. data = res.json()
  62. self.organization_id = data["id"] # TODO unsafe for web usage
  63. dataset = tablib.Dataset()
  64. dataset.dict = [data]
  65. resource.import_data(dataset, raise_errors=True)
  66. def import_organization_users(self):
  67. resource = OrganizationUserResource()
  68. res = self.get(self.url + self.organization_users_url)
  69. org_users = res.json()
  70. if self.create_users:
  71. user_resource = UserResource()
  72. users_list = [
  73. org_user["user"] for org_user in org_users if org_user is not None
  74. ]
  75. users = [
  76. {k: v for k, v in user.items() if k in ["id", "email", "name"]}
  77. for user in users_list
  78. ]
  79. dataset = tablib.Dataset()
  80. dataset.dict = users
  81. user_resource.import_data(dataset, raise_errors=True)
  82. for org_user in org_users:
  83. org_user["organization"] = self.organization_id
  84. org_user["role"] = OrganizationUserRole.from_string(org_user["role"])
  85. if self.create_users:
  86. org_user["user"] = (
  87. User.objects.filter(email=org_user["user"]["email"])
  88. .values_list("pk", flat=True)
  89. .first()
  90. )
  91. else:
  92. org_user["user"] = None
  93. dataset = tablib.Dataset()
  94. dataset.dict = org_users
  95. resource.import_data(dataset, raise_errors=True)
  96. def import_projects(self):
  97. project_resource = ProjectResource()
  98. project_key_resource = ProjectKeyResource()
  99. res = self.get(self.url + self.projects_url)
  100. projects = res.json()
  101. project_keys = []
  102. for project in projects:
  103. project["organization"] = self.organization_id
  104. keys = self.get(
  105. self.url
  106. + reverse(
  107. "project-keys-list",
  108. kwargs={
  109. "project_pk": f"{self.organization_slug}/{project['slug']}",
  110. },
  111. )
  112. ).json()
  113. for key in keys:
  114. key["project"] = project["id"]
  115. key["public_key"] = key["public"]
  116. project_keys += keys
  117. dataset = tablib.Dataset()
  118. dataset.dict = projects
  119. project_resource.import_data(dataset, raise_errors=True)
  120. owned_project_ids = Project.objects.filter(
  121. organization_id=self.organization_id,
  122. pk__in=[d["projectId"] for d in project_keys],
  123. ).values_list("pk", flat=True)
  124. project_keys = list(
  125. filter(lambda key: key["projectId"] in owned_project_ids, project_keys)
  126. )
  127. dataset.dict = project_keys
  128. project_key_resource.import_data(dataset, raise_errors=True)
  129. def import_teams(self):
  130. resource = TeamResource()
  131. res = self.get(self.url + self.teams_url)
  132. teams = res.json()
  133. for team in teams:
  134. team["organization"] = self.organization_id
  135. team["projects"] = ",".join(
  136. map(
  137. str,
  138. Project.objects.filter(
  139. organization_id=self.organization_id,
  140. pk__in=[int(d["id"]) for d in team["projects"]],
  141. ).values_list("id", flat=True),
  142. )
  143. )
  144. team_members = self.get(
  145. self.url
  146. + reverse(
  147. "team-members-list",
  148. kwargs={"team_pk": f"{self.organization_slug}/{team['slug']}"},
  149. )
  150. ).json()
  151. team_member_emails = [d["email"] for d in team_members]
  152. team["members"] = ",".join(
  153. [
  154. str(i)
  155. for i in OrganizationUser.objects.filter(
  156. organization_id=self.organization_id
  157. )
  158. .filter(
  159. Q(email__in=team_member_emails)
  160. | Q(user__email__in=team_member_emails)
  161. )
  162. .values_list("pk", flat=True)
  163. ]
  164. )
  165. dataset = tablib.Dataset()
  166. dataset.dict = teams
  167. resource.import_data(dataset, raise_errors=True)
  168. def check_auth(self):
  169. res = requests.get(self.url + self.api_root_url, headers=self.headers)
  170. data = res.json()
  171. if res.status_code != 200 or not data["user"]:
  172. raise ImporterException("Bad auth token")