2
0

forms.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. from Crypto.Cipher import PKCS1_OAEP
  2. from Crypto.PublicKey import RSA
  3. from django import forms
  4. from dcim.models import Device
  5. from extras.forms import (
  6. AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldFilterForm, CustomFieldModelForm, CustomFieldModelCSVForm,
  7. )
  8. from extras.models import Tag
  9. from utilities.forms import (
  10. BootstrapMixin, CSVModelChoiceField, CSVModelForm, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
  11. SlugField, TagFilterField,
  12. )
  13. from .constants import *
  14. from .models import Secret, SecretRole, UserKey
  15. def validate_rsa_key(key, is_secret=True):
  16. """
  17. Validate the format and type of an RSA key.
  18. """
  19. if key.startswith('ssh-rsa '):
  20. raise forms.ValidationError("OpenSSH line format is not supported. Please ensure that your public is in PEM (base64) format.")
  21. try:
  22. key = RSA.importKey(key)
  23. except ValueError:
  24. raise forms.ValidationError("Invalid RSA key. Please ensure that your key is in PEM (base64) format.")
  25. except Exception as e:
  26. raise forms.ValidationError("Invalid key detected: {}".format(e))
  27. if is_secret and not key.has_private():
  28. raise forms.ValidationError("This looks like a public key. Please provide your private RSA key.")
  29. elif not is_secret and key.has_private():
  30. raise forms.ValidationError("This looks like a private key. Please provide your public RSA key.")
  31. try:
  32. PKCS1_OAEP.new(key)
  33. except Exception:
  34. raise forms.ValidationError("Error validating RSA key. Please ensure that your key supports PKCS#1 OAEP.")
  35. #
  36. # Secret roles
  37. #
  38. class SecretRoleForm(BootstrapMixin, forms.ModelForm):
  39. slug = SlugField()
  40. class Meta:
  41. model = SecretRole
  42. fields = ('name', 'slug', 'description')
  43. class SecretRoleCSVForm(CSVModelForm):
  44. slug = SlugField()
  45. class Meta:
  46. model = SecretRole
  47. fields = SecretRole.csv_headers
  48. #
  49. # Secrets
  50. #
  51. class SecretForm(BootstrapMixin, CustomFieldModelForm):
  52. device = DynamicModelChoiceField(
  53. queryset=Device.objects.all(),
  54. display_field='display_name'
  55. )
  56. plaintext = forms.CharField(
  57. max_length=SECRET_PLAINTEXT_MAX_LENGTH,
  58. required=False,
  59. label='Plaintext',
  60. widget=forms.PasswordInput(
  61. attrs={
  62. 'class': 'requires-session-key',
  63. }
  64. )
  65. )
  66. plaintext2 = forms.CharField(
  67. max_length=SECRET_PLAINTEXT_MAX_LENGTH,
  68. required=False,
  69. label='Plaintext (verify)',
  70. widget=forms.PasswordInput()
  71. )
  72. role = DynamicModelChoiceField(
  73. queryset=SecretRole.objects.all()
  74. )
  75. tags = DynamicModelMultipleChoiceField(
  76. queryset=Tag.objects.all(),
  77. required=False
  78. )
  79. class Meta:
  80. model = Secret
  81. fields = [
  82. 'device', 'role', 'name', 'plaintext', 'plaintext2', 'tags',
  83. ]
  84. def __init__(self, *args, **kwargs):
  85. super().__init__(*args, **kwargs)
  86. # A plaintext value is required when creating a new Secret
  87. if not self.instance.pk:
  88. self.fields['plaintext'].required = True
  89. def clean(self):
  90. # Verify that the provided plaintext values match
  91. if self.cleaned_data['plaintext'] != self.cleaned_data['plaintext2']:
  92. raise forms.ValidationError({
  93. 'plaintext2': "The two given plaintext values do not match. Please check your input."
  94. })
  95. # Validate uniqueness
  96. if Secret.objects.filter(
  97. device=self.cleaned_data['device'],
  98. role=self.cleaned_data['role'],
  99. name=self.cleaned_data['name']
  100. ).exclude(pk=self.instance.pk).exists():
  101. raise forms.ValidationError(
  102. "Each secret assigned to a device must have a unique combination of role and name"
  103. )
  104. class SecretCSVForm(CustomFieldModelCSVForm):
  105. device = CSVModelChoiceField(
  106. queryset=Device.objects.all(),
  107. to_field_name='name',
  108. help_text='Assigned device'
  109. )
  110. role = CSVModelChoiceField(
  111. queryset=SecretRole.objects.all(),
  112. to_field_name='name',
  113. help_text='Assigned role'
  114. )
  115. plaintext = forms.CharField(
  116. help_text='Plaintext secret data'
  117. )
  118. class Meta:
  119. model = Secret
  120. fields = Secret.csv_headers
  121. help_texts = {
  122. 'name': 'Name or username',
  123. }
  124. def save(self, *args, **kwargs):
  125. s = super().save(*args, **kwargs)
  126. s.plaintext = str(self.cleaned_data['plaintext'])
  127. return s
  128. class SecretBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
  129. pk = forms.ModelMultipleChoiceField(
  130. queryset=Secret.objects.all(),
  131. widget=forms.MultipleHiddenInput()
  132. )
  133. role = DynamicModelChoiceField(
  134. queryset=SecretRole.objects.all(),
  135. required=False
  136. )
  137. name = forms.CharField(
  138. max_length=100,
  139. required=False
  140. )
  141. class Meta:
  142. nullable_fields = [
  143. 'name',
  144. ]
  145. class SecretFilterForm(BootstrapMixin, CustomFieldFilterForm):
  146. model = Secret
  147. q = forms.CharField(
  148. required=False,
  149. label='Search'
  150. )
  151. role = DynamicModelMultipleChoiceField(
  152. queryset=SecretRole.objects.all(),
  153. to_field_name='slug',
  154. required=False
  155. )
  156. tag = TagFilterField(model)
  157. #
  158. # UserKeys
  159. #
  160. class UserKeyForm(BootstrapMixin, forms.ModelForm):
  161. class Meta:
  162. model = UserKey
  163. fields = ['public_key']
  164. help_texts = {
  165. 'public_key': "Enter your public RSA key. Keep the private one with you; you'll need it for decryption. "
  166. "Please note that passphrase-protected keys are not supported.",
  167. }
  168. labels = {
  169. 'public_key': ''
  170. }
  171. def clean_public_key(self):
  172. key = self.cleaned_data['public_key']
  173. # Validate the RSA key format.
  174. validate_rsa_key(key, is_secret=False)
  175. return key
  176. class ActivateUserKeyForm(forms.Form):
  177. _selected_action = forms.ModelMultipleChoiceField(
  178. queryset=UserKey.objects.all(),
  179. label='User Keys'
  180. )
  181. secret_key = forms.CharField(
  182. widget=forms.Textarea(
  183. attrs={
  184. 'class': 'vLargeTextField',
  185. }
  186. ),
  187. label='Your private key'
  188. )