You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

317 lines
9.2 KiB

10 years ago
10 years ago
10 years ago
  1. from datetime import timedelta as td
  2. import json
  3. import re
  4. from urllib.parse import quote, urlencode
  5. from django import forms
  6. from django.forms import URLField
  7. from django.conf import settings
  8. from django.core.exceptions import ValidationError
  9. from hc.front.validators import (
  10. CronExpressionValidator,
  11. TimezoneValidator,
  12. WebhookValidator,
  13. )
  14. import requests
  15. def _is_latin1(s):
  16. try:
  17. s.encode("latin-1")
  18. return True
  19. except UnicodeError:
  20. return False
  21. class HeadersField(forms.Field):
  22. message = """Use "Header-Name: value" pairs, one per line."""
  23. def to_python(self, value):
  24. if not value:
  25. return {}
  26. headers = {}
  27. for line in value.split("\n"):
  28. if not line.strip():
  29. continue
  30. if ":" not in line:
  31. raise ValidationError(self.message)
  32. n, v = line.split(":", maxsplit=1)
  33. n, v = n.strip(), v.strip()
  34. if not n or not v:
  35. raise ValidationError(message=self.message)
  36. if not _is_latin1(n):
  37. raise ValidationError(
  38. message="Header names must not contain special characters"
  39. )
  40. headers[n] = v
  41. return headers
  42. def validate(self, value):
  43. super().validate(value)
  44. for k, v in value.items():
  45. if len(k) > 1000 or len(v) > 1000:
  46. raise ValidationError("Value too long")
  47. class NameTagsForm(forms.Form):
  48. name = forms.CharField(max_length=100, required=False)
  49. tags = forms.CharField(max_length=500, required=False)
  50. desc = forms.CharField(required=False)
  51. def clean_tags(self):
  52. result = []
  53. for part in self.cleaned_data["tags"].split(" "):
  54. part = part.strip()
  55. if part != "":
  56. result.append(part)
  57. return " ".join(result)
  58. class FilteringRulesForm(forms.Form):
  59. filter_by_subject = forms.ChoiceField(choices=(("no", "no"), ("yes", "yes")))
  60. subject = forms.CharField(required=False, max_length=200)
  61. subject_fail = forms.CharField(required=False, max_length=200)
  62. methods = forms.ChoiceField(required=False, choices=(("", "Any"), ("POST", "POST")))
  63. manual_resume = forms.BooleanField(required=False)
  64. def clean_subject(self):
  65. if self.cleaned_data["filter_by_subject"] == "yes":
  66. return self.cleaned_data["subject"]
  67. return ""
  68. def clean_subject_fail(self):
  69. if self.cleaned_data["filter_by_subject"] == "yes":
  70. return self.cleaned_data["subject_fail"]
  71. return ""
  72. class TimeoutForm(forms.Form):
  73. timeout = forms.IntegerField(min_value=60, max_value=2592000)
  74. grace = forms.IntegerField(min_value=60, max_value=2592000)
  75. def clean_timeout(self):
  76. return td(seconds=self.cleaned_data["timeout"])
  77. def clean_grace(self):
  78. return td(seconds=self.cleaned_data["grace"])
  79. class CronForm(forms.Form):
  80. schedule = forms.CharField(max_length=100, validators=[CronExpressionValidator()])
  81. tz = forms.CharField(max_length=36, validators=[TimezoneValidator()])
  82. grace = forms.IntegerField(min_value=1, max_value=43200)
  83. class AddOpsgenieForm(forms.Form):
  84. error_css_class = "has-error"
  85. region = forms.ChoiceField(initial="us", choices=(("us", "US"), ("eu", "EU")))
  86. key = forms.CharField(max_length=40)
  87. PRIO_CHOICES = [
  88. ("-2", "Lowest Priority"),
  89. ("-1", "Low Priority"),
  90. ("0", "Normal Priority"),
  91. ("1", "High Priority"),
  92. ("2", "Emergency Priority"),
  93. ]
  94. class AddPushoverForm(forms.Form):
  95. error_css_class = "has-error"
  96. pushover_user_key = forms.CharField()
  97. prio = forms.ChoiceField(initial="0", choices=PRIO_CHOICES)
  98. prio_up = forms.ChoiceField(initial="0", choices=PRIO_CHOICES)
  99. def get_value(self):
  100. key = self.cleaned_data["pushover_user_key"]
  101. prio = self.cleaned_data["prio"]
  102. prio_up = self.cleaned_data["prio_up"]
  103. return "%s|%s|%s" % (key, prio, prio_up)
  104. class EmailForm(forms.Form):
  105. error_css_class = "has-error"
  106. value = forms.EmailField(max_length=100)
  107. down = forms.BooleanField(required=False, initial=True)
  108. up = forms.BooleanField(required=False, initial=True)
  109. def clean(self):
  110. super().clean()
  111. down = self.cleaned_data.get("down")
  112. up = self.cleaned_data.get("up")
  113. if not down and not up:
  114. self.add_error("down", "Please select at least one.")
  115. def get_value(self):
  116. return json.dumps(dict(self.cleaned_data), sort_keys=True)
  117. class AddUrlForm(forms.Form):
  118. error_css_class = "has-error"
  119. value = forms.URLField(max_length=1000, validators=[WebhookValidator()])
  120. METHODS = ("GET", "POST", "PUT")
  121. class WebhookForm(forms.Form):
  122. error_css_class = "has-error"
  123. name = forms.CharField(max_length=100, required=False)
  124. method_down = forms.ChoiceField(initial="GET", choices=zip(METHODS, METHODS))
  125. body_down = forms.CharField(max_length=1000, required=False)
  126. headers_down = HeadersField(required=False)
  127. url_down = URLField(
  128. max_length=1000, required=False, validators=[WebhookValidator()]
  129. )
  130. method_up = forms.ChoiceField(initial="GET", choices=zip(METHODS, METHODS))
  131. body_up = forms.CharField(max_length=1000, required=False)
  132. headers_up = HeadersField(required=False)
  133. url_up = forms.URLField(
  134. max_length=1000, required=False, validators=[WebhookValidator()]
  135. )
  136. def clean(self):
  137. super().clean()
  138. url_down = self.cleaned_data.get("url_down")
  139. url_up = self.cleaned_data.get("url_up")
  140. if not url_down and not url_up:
  141. if not self.has_error("url_down"):
  142. self.add_error("url_down", "Enter a valid URL.")
  143. def get_value(self):
  144. return json.dumps(dict(self.cleaned_data), sort_keys=True)
  145. class AddShellForm(forms.Form):
  146. error_css_class = "has-error"
  147. cmd_down = forms.CharField(max_length=1000, required=False)
  148. cmd_up = forms.CharField(max_length=1000, required=False)
  149. def get_value(self):
  150. return json.dumps(dict(self.cleaned_data), sort_keys=True)
  151. class PhoneNumberForm(forms.Form):
  152. error_css_class = "has-error"
  153. label = forms.CharField(max_length=100, required=False)
  154. phone = forms.CharField()
  155. def clean_phone(self):
  156. v = self.cleaned_data["phone"]
  157. stripped = v.encode("ascii", "ignore").decode("ascii")
  158. stripped = stripped.replace(" ", "").replace("-", "")
  159. if not re.match(r"^\+\d{5,15}$", stripped):
  160. raise forms.ValidationError("Invalid phone number format.")
  161. return stripped
  162. def get_json(self):
  163. return json.dumps({"value": self.cleaned_data["phone"]})
  164. class PhoneUpDownForm(PhoneNumberForm):
  165. up = forms.BooleanField(required=False, initial=True)
  166. down = forms.BooleanField(required=False, initial=True)
  167. def clean(self):
  168. super().clean()
  169. down = self.cleaned_data.get("down")
  170. up = self.cleaned_data.get("up")
  171. if not down and not up:
  172. self.add_error("down", "Please select at least one.")
  173. def get_json(self):
  174. return json.dumps(
  175. {
  176. "value": self.cleaned_data["phone"],
  177. "up": self.cleaned_data["up"],
  178. "down": self.cleaned_data["down"],
  179. }
  180. )
  181. class ChannelNameForm(forms.Form):
  182. name = forms.CharField(max_length=100, required=False)
  183. class AddMatrixForm(forms.Form):
  184. error_css_class = "has-error"
  185. alias = forms.CharField(max_length=100)
  186. def clean_alias(self):
  187. v = self.cleaned_data["alias"]
  188. # validate it by trying to join
  189. url = settings.MATRIX_HOMESERVER
  190. url += "/_matrix/client/r0/join/%s?" % quote(v)
  191. url += urlencode({"access_token": settings.MATRIX_ACCESS_TOKEN})
  192. r = requests.post(url, {})
  193. if r.status_code == 429:
  194. raise forms.ValidationError(
  195. "Matrix server returned status code 429 (Too Many Requests), "
  196. "please try again later."
  197. )
  198. doc = r.json()
  199. if "error" in doc:
  200. raise forms.ValidationError("Response from Matrix: %s" % doc["error"])
  201. self.cleaned_data["room_id"] = doc["room_id"]
  202. return v
  203. class AddAppriseForm(forms.Form):
  204. error_css_class = "has-error"
  205. url = forms.CharField(max_length=512)
  206. class AddPdForm(forms.Form):
  207. error_css_class = "has-error"
  208. value = forms.CharField(max_length=32)
  209. ZULIP_TARGETS = (("stream", "Stream"), ("private", "Private"))
  210. class AddZulipForm(forms.Form):
  211. error_css_class = "has-error"
  212. bot_email = forms.EmailField(max_length=100)
  213. api_key = forms.CharField(max_length=50)
  214. site = forms.URLField(max_length=100, validators=[WebhookValidator()])
  215. mtype = forms.ChoiceField(choices=ZULIP_TARGETS)
  216. to = forms.CharField(max_length=100)
  217. def get_value(self):
  218. return json.dumps(dict(self.cleaned_data), sort_keys=True)
  219. class AddTrelloForm(forms.Form):
  220. token = forms.RegexField(regex=r"^[0-9a-fA-F]{64}$")
  221. board_name = forms.CharField(max_length=100)
  222. list_name = forms.CharField(max_length=100)
  223. list_id = forms.RegexField(regex=r"^[0-9a-fA-F]{16,32}$")
  224. def get_value(self):
  225. return json.dumps(dict(self.cleaned_data), sort_keys=True)