forms.py 6.0 KB

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