models.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. from django.db import models
  2. from django.utils.text import slugify
  3. from django.utils.translation import ugettext_lazy as _
  4. from organizations.base import (
  5. OrganizationBase,
  6. OrganizationUserBase,
  7. OrganizationOwnerBase,
  8. )
  9. from organizations.abstract import SharedBaseModel
  10. from organizations.fields import SlugField
  11. from organizations.signals import user_added
  12. # Defines which scopes belong to which role
  13. # Credit to sentry/conf/server.py
  14. ROLES = (
  15. {
  16. "id": "member",
  17. "name": "Member",
  18. "desc": "Members can view and act on events, as well as view most other data within the organization.",
  19. "scopes": set(
  20. [
  21. "event:read",
  22. "event:write",
  23. "event:admin",
  24. "project:releases",
  25. "project:read",
  26. "org:read",
  27. "member:read",
  28. "team:read",
  29. ]
  30. ),
  31. },
  32. {
  33. "id": "admin",
  34. "name": "Admin",
  35. "desc": "Admin privileges on any teams of which they're a member. They can create new teams and projects, as well as remove teams and projects which they already hold membership on (or all teams, if open membership is on). Additionally, they can manage memberships of teams that they are members of.",
  36. "scopes": set(
  37. [
  38. "event:read",
  39. "event:write",
  40. "event:admin",
  41. "org:read",
  42. "member:read",
  43. "project:read",
  44. "project:write",
  45. "project:admin",
  46. "project:releases",
  47. "team:read",
  48. "team:write",
  49. "team:admin",
  50. "org:integrations",
  51. ]
  52. ),
  53. },
  54. {
  55. "id": "manager",
  56. "name": "Manager",
  57. "desc": "Gains admin access on all teams as well as the ability to add and remove members.",
  58. "is_global": True,
  59. "scopes": set(
  60. [
  61. "event:read",
  62. "event:write",
  63. "event:admin",
  64. "member:read",
  65. "member:write",
  66. "member:admin",
  67. "project:read",
  68. "project:write",
  69. "project:admin",
  70. "project:releases",
  71. "team:read",
  72. "team:write",
  73. "team:admin",
  74. "org:read",
  75. "org:write",
  76. "org:integrations",
  77. ]
  78. ),
  79. },
  80. {
  81. "id": "owner",
  82. "name": "Organization Owner",
  83. "desc": "Unrestricted access to the organization, its data, and its settings. Can add, modify, and delete projects and members, as well as make billing and plan changes.",
  84. "is_global": True,
  85. "scopes": set(
  86. [
  87. "org:read",
  88. "org:write",
  89. "org:admin",
  90. "org:integrations",
  91. "member:read",
  92. "member:write",
  93. "member:admin",
  94. "team:read",
  95. "team:write",
  96. "team:admin",
  97. "project:read",
  98. "project:write",
  99. "project:admin",
  100. "project:releases",
  101. "event:read",
  102. "event:write",
  103. "event:admin",
  104. ]
  105. ),
  106. },
  107. )
  108. class OrganizationUserRole(models.IntegerChoices):
  109. MEMBER = 0, "Member"
  110. ADMIN = 1, "Admin"
  111. MANAGER = 2, "Manager"
  112. OWNER = 3, "Owner" # Many users can be owner but only one primary owner
  113. @classmethod
  114. def from_string(cls, string: str):
  115. for status in cls:
  116. if status.label.lower() == string.lower():
  117. return status
  118. @classmethod
  119. def get_role(cls, role: int):
  120. return ROLES[role]
  121. class Organization(SharedBaseModel, OrganizationBase):
  122. slug = SlugField(
  123. max_length=200,
  124. blank=False,
  125. editable=True,
  126. populate_from="name",
  127. unique=True,
  128. help_text=_("The name in all lowercase, suitable for URL identification"),
  129. )
  130. is_accepting_events = models.BooleanField(
  131. default=True, help_text="Used for throttling at org level"
  132. )
  133. open_membership = models.BooleanField(
  134. default=True, help_text="Allow any organization member to join any team"
  135. )
  136. scrub_ip_addresses = models.BooleanField(
  137. default=True,
  138. help_text="Default for whether projects should script IP Addresses",
  139. )
  140. def slugify_function(self, content):
  141. reserved_words = [
  142. "login",
  143. "register",
  144. "app",
  145. "profile",
  146. "organizations",
  147. "settings",
  148. "issues",
  149. "performance",
  150. "_health",
  151. "rest-auth",
  152. "api",
  153. "accept",
  154. "stripe",
  155. "admin",
  156. "__debug__",
  157. ]
  158. slug = slugify(content)
  159. if slug in reserved_words:
  160. return slug + "-1"
  161. return slug
  162. def add_user(self, user, role=OrganizationUserRole.MEMBER):
  163. """
  164. Adds a new user and if the first user makes the user an admin and
  165. the owner.
  166. """
  167. users_count = self.users.all().count()
  168. if users_count == 0:
  169. role = OrganizationUserRole.OWNER
  170. org_user = self._org_user_model.objects.create(
  171. user=user, organization=self, role=role
  172. )
  173. if users_count == 0:
  174. self._org_owner_model.objects.create(
  175. organization=self, organization_user=org_user
  176. )
  177. # User added signal
  178. user_added.send(sender=self, user=user)
  179. return org_user
  180. @property
  181. def owners(self):
  182. return self.users.filter(
  183. organizations_ext_organizationuser__role=OrganizationUserRole.OWNER
  184. )
  185. @property
  186. def email(self):
  187. """ Used to identify billing contact for stripe. """
  188. billing_contact = self.owner.organization_user.user
  189. return billing_contact.email
  190. def get_user_scopes(self, user):
  191. org_user = self.organization_users.get(user=user)
  192. return org_user.get_scopes()
  193. class OrganizationUser(SharedBaseModel, OrganizationUserBase):
  194. user = models.ForeignKey(
  195. "users.User",
  196. blank=True,
  197. null=True,
  198. on_delete=models.CASCADE,
  199. related_name="organizations_ext_organizationuser",
  200. )
  201. role = models.PositiveSmallIntegerField(choices=OrganizationUserRole.choices)
  202. email = models.EmailField(
  203. blank=True, null=True, help_text="Email for pending invite"
  204. )
  205. class Meta(OrganizationOwnerBase.Meta):
  206. unique_together = (("user", "organization"), ("email", "organization"))
  207. def __str__(self, *args, **kwargs):
  208. if self.user:
  209. return super().__str__(*args, **kwargs)
  210. return self.email
  211. def get_email(self):
  212. if self.user:
  213. return self.user.email
  214. return self.email
  215. def get_role(self):
  216. return self.get_role_display().lower()
  217. def get_scopes(self):
  218. role = OrganizationUserRole.get_role(self.role)
  219. return role["scopes"]
  220. def accept_invite(self, user):
  221. self.user = user
  222. self.email = None
  223. self.save()
  224. @property
  225. def pending(self):
  226. return self.user_id is None
  227. @property
  228. def is_active(self):
  229. """ Non pending means active """
  230. return not self.pending
  231. class OrganizationOwner(OrganizationOwnerBase):
  232. """ Only usage is for billing contact currently """