views.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. from django.core.exceptions import ValidationError
  2. from django.db.models import Count
  3. from django.utils.translation import gettext_lazy as _
  4. from core.models import ObjectChange
  5. from core.tables import ObjectChangeTable
  6. from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename
  7. from netbox.ui import actions, layout
  8. from netbox.ui.panels import (
  9. ContextTablePanel,
  10. JSONPanel,
  11. ObjectsTablePanel,
  12. OrganizationalObjectPanel,
  13. RelatedObjectsPanel,
  14. TemplatePanel,
  15. )
  16. from netbox.views import generic
  17. from users.ui import panels
  18. from utilities.query import count_related
  19. from utilities.views import GetRelatedModelsMixin, register_model_view
  20. from . import filtersets, forms, tables
  21. from .models import Group, ObjectPermission, Owner, OwnerGroup, Token, User
  22. #
  23. # Tokens
  24. #
  25. @register_model_view(Token, 'list', path='', detail=False)
  26. class TokenListView(generic.ObjectListView):
  27. queryset = Token.objects.all()
  28. filterset = filtersets.TokenFilterSet
  29. filterset_form = forms.TokenFilterForm
  30. table = tables.TokenTable
  31. actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  32. @register_model_view(Token)
  33. class TokenView(generic.ObjectView):
  34. queryset = Token.objects.all()
  35. layout = layout.SimpleLayout(
  36. left_panels=[
  37. panels.TokenPanel(),
  38. ],
  39. right_panels=[
  40. panels.TokenExamplePanel(),
  41. ],
  42. )
  43. def get_extra_context(self, request, instance):
  44. # Pop a one-time plaintext value (set by TokenEditView.post_save when a token is first created) and assemble
  45. # the full HTTP authorization string for display. The plaintext is never persisted; popping ensures the
  46. # banner only renders once.
  47. plaintext = request.session.pop(f'_token_plaintext_{instance.pk}', None)
  48. if plaintext:
  49. return {'token_auth_string': f'{instance.get_auth_header_prefix()}{plaintext}'}
  50. return {}
  51. @register_model_view(Token, 'add', detail=False)
  52. @register_model_view(Token, 'edit')
  53. class TokenEditView(generic.ObjectEditView):
  54. queryset = Token.objects.all()
  55. form = forms.TokenForm
  56. template_name = 'users/token_edit.html'
  57. def alter_object(self, obj, request, url_args, url_kwargs):
  58. # Attach the request so that UserTokenForm.save() can stash the newly-generated plaintext on the session.
  59. obj._request = request
  60. return obj
  61. @register_model_view(Token, 'delete')
  62. class TokenDeleteView(generic.ObjectDeleteView):
  63. queryset = Token.objects.all()
  64. @register_model_view(Token, 'bulk_import', path='import', detail=False)
  65. class TokenBulkImportView(generic.BulkImportView):
  66. queryset = Token.objects.all()
  67. model_form = forms.TokenImportForm
  68. def save_object(self, object_form, request):
  69. # Creating a Token on behalf of another user requires the grant_token permission. This is enforced only on
  70. # creation; TokenImportForm disables the user field on update, so an existing Token's owner cannot change.
  71. token_user = object_form.cleaned_data.get('user')
  72. if object_form.instance._state.adding and token_user and token_user != request.user \
  73. and not request.user.has_perm('users.grant_token'):
  74. raise ValidationError(_("This user does not have permission to create tokens for other users."))
  75. return super().save_object(object_form, request)
  76. @register_model_view(Token, 'bulk_edit', path='edit', detail=False)
  77. class TokenBulkEditView(generic.BulkEditView):
  78. queryset = Token.objects.all()
  79. table = tables.TokenTable
  80. form = forms.TokenBulkEditForm
  81. @register_model_view(Token, 'bulk_delete', path='delete', detail=False)
  82. class TokenBulkDeleteView(generic.BulkDeleteView):
  83. queryset = Token.objects.all()
  84. table = tables.TokenTable
  85. #
  86. # Users
  87. #
  88. @register_model_view(User, 'list', path='', detail=False)
  89. class UserListView(generic.ObjectListView):
  90. queryset = User.objects.all()
  91. filterset = filtersets.UserFilterSet
  92. filterset_form = forms.UserFilterForm
  93. table = tables.UserTable
  94. @register_model_view(User)
  95. class UserView(generic.ObjectView):
  96. queryset = User.objects.all()
  97. layout = layout.SimpleLayout(
  98. left_panels=[
  99. panels.UserPanel(),
  100. ],
  101. right_panels=[
  102. ObjectsTablePanel(
  103. 'users.Group', title=_('Assigned Groups'), filters={'user_id': lambda ctx: ctx['object'].pk}
  104. ),
  105. ObjectsTablePanel(
  106. 'users.ObjectPermission',
  107. title=_('Assigned Permissions'),
  108. filters={'user_id': lambda ctx: ctx['object'].pk},
  109. ),
  110. ObjectsTablePanel(
  111. 'users.Owner', title=_('Owner Membership'), filters={'user_id': lambda ctx: ctx['object'].pk}
  112. ),
  113. ],
  114. bottom_panels=[
  115. ContextTablePanel(
  116. 'changelog_table',
  117. title=_('Recent Activity'),
  118. actions=[
  119. actions.LinkAction(
  120. view_name='core:objectchange_list',
  121. url_params={'user_id': lambda ctx: ctx['object'].pk},
  122. label=_('View All'),
  123. button_icon='arrow-right-thick',
  124. permissions=['core.view_objectchange'],
  125. ),
  126. ],
  127. ),
  128. ],
  129. )
  130. def get_extra_context(self, request, instance):
  131. changelog = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(user=instance)[:20]
  132. changelog_table = ObjectChangeTable(changelog)
  133. changelog_table.orderable = False
  134. changelog_table.configure(request)
  135. return {
  136. 'changelog_table': changelog_table,
  137. }
  138. @register_model_view(User, 'add', detail=False)
  139. @register_model_view(User, 'edit')
  140. class UserEditView(generic.ObjectEditView):
  141. queryset = User.objects.all()
  142. form = forms.UserForm
  143. @register_model_view(User, 'delete')
  144. class UserDeleteView(generic.ObjectDeleteView):
  145. queryset = User.objects.all()
  146. @register_model_view(User, 'bulk_import', path='import', detail=False)
  147. class UserBulkImportView(generic.BulkImportView):
  148. queryset = User.objects.all()
  149. model_form = forms.UserImportForm
  150. @register_model_view(User, 'bulk_edit', path='edit', detail=False)
  151. class UserBulkEditView(generic.BulkEditView):
  152. queryset = User.objects.all()
  153. filterset = filtersets.UserFilterSet
  154. table = tables.UserTable
  155. form = forms.UserBulkEditForm
  156. @register_model_view(User, 'bulk_rename', path='rename', detail=False)
  157. class UserBulkRenameView(generic.BulkRenameView):
  158. queryset = User.objects.all()
  159. field_name = 'username'
  160. filterset = filtersets.UserFilterSet
  161. @register_model_view(User, 'bulk_delete', path='delete', detail=False)
  162. class UserBulkDeleteView(generic.BulkDeleteView):
  163. queryset = User.objects.all()
  164. filterset = filtersets.UserFilterSet
  165. table = tables.UserTable
  166. #
  167. # Groups
  168. #
  169. @register_model_view(Group, 'list', path='', detail=False)
  170. class GroupListView(generic.ObjectListView):
  171. queryset = Group.objects.annotate(users_count=Count('user')).order_by('name')
  172. filterset = filtersets.GroupFilterSet
  173. filterset_form = forms.GroupFilterForm
  174. table = tables.GroupTable
  175. @register_model_view(Group)
  176. class GroupView(generic.ObjectView):
  177. queryset = Group.objects.all()
  178. layout = layout.SimpleLayout(
  179. left_panels=[
  180. OrganizationalObjectPanel(),
  181. ],
  182. right_panels=[
  183. ObjectsTablePanel(
  184. 'users.User',
  185. filters={'group_id': lambda ctx: ctx['object'].pk},
  186. ),
  187. ObjectsTablePanel(
  188. 'users.ObjectPermission',
  189. title=_('Assigned Permissions'),
  190. filters={'group_id': lambda ctx: ctx['object'].pk},
  191. ),
  192. ObjectsTablePanel(
  193. 'users.Owner', title=_('Owner Membership'), filters={'user_group_id': lambda ctx: ctx['object'].pk}
  194. ),
  195. ],
  196. )
  197. @register_model_view(Group, 'add', detail=False)
  198. @register_model_view(Group, 'edit')
  199. class GroupEditView(generic.ObjectEditView):
  200. queryset = Group.objects.all()
  201. form = forms.GroupForm
  202. @register_model_view(Group, 'delete')
  203. class GroupDeleteView(generic.ObjectDeleteView):
  204. queryset = Group.objects.all()
  205. @register_model_view(Group, 'bulk_import', path='import', detail=False)
  206. class GroupBulkImportView(generic.BulkImportView):
  207. queryset = Group.objects.all()
  208. model_form = forms.GroupImportForm
  209. @register_model_view(Group, 'bulk_edit', path='edit', detail=False)
  210. class GroupBulkEditView(generic.BulkEditView):
  211. queryset = Group.objects.all()
  212. filterset = filtersets.GroupFilterSet
  213. table = tables.GroupTable
  214. form = forms.GroupBulkEditForm
  215. @register_model_view(Group, 'bulk_rename', path='rename', detail=False)
  216. class GroupBulkRenameView(generic.BulkRenameView):
  217. queryset = Group.objects.all()
  218. filterset = filtersets.GroupFilterSet
  219. @register_model_view(Group, 'bulk_delete', path='delete', detail=False)
  220. class GroupBulkDeleteView(generic.BulkDeleteView):
  221. queryset = Group.objects.annotate(users_count=Count('user')).order_by('name')
  222. filterset = filtersets.GroupFilterSet
  223. table = tables.GroupTable
  224. #
  225. # ObjectPermissions
  226. #
  227. @register_model_view(ObjectPermission, 'list', path='', detail=False)
  228. class ObjectPermissionListView(generic.ObjectListView):
  229. queryset = ObjectPermission.objects.all()
  230. filterset = filtersets.ObjectPermissionFilterSet
  231. filterset_form = forms.ObjectPermissionFilterForm
  232. table = tables.ObjectPermissionTable
  233. actions = (AddObject, BulkExport, BulkEdit, BulkRename, BulkDelete)
  234. @register_model_view(ObjectPermission)
  235. class ObjectPermissionView(generic.ObjectView):
  236. queryset = ObjectPermission.objects.all()
  237. layout = layout.SimpleLayout(
  238. left_panels=[
  239. panels.ObjectPermissionPanel(),
  240. panels.ObjectPermissionActionsPanel(),
  241. JSONPanel('constraints', title=_('Constraints')),
  242. ],
  243. right_panels=[
  244. TemplatePanel('users/panels/object_types.html'),
  245. ObjectsTablePanel(
  246. 'users.User', title=_('Assigned Users'), filters={'permission_id': lambda ctx: ctx['object'].pk}
  247. ),
  248. ObjectsTablePanel(
  249. 'users.Group', title=_('Assigned Groups'), filters={'permission_id': lambda ctx: ctx['object'].pk}
  250. ),
  251. ],
  252. )
  253. @register_model_view(ObjectPermission, 'add', detail=False)
  254. @register_model_view(ObjectPermission, 'edit')
  255. class ObjectPermissionEditView(generic.ObjectEditView):
  256. queryset = ObjectPermission.objects.all()
  257. form = forms.ObjectPermissionForm
  258. @register_model_view(ObjectPermission, 'delete')
  259. class ObjectPermissionDeleteView(generic.ObjectDeleteView):
  260. queryset = ObjectPermission.objects.all()
  261. filterset = filtersets.ObjectPermissionFilterSet
  262. @register_model_view(ObjectPermission, 'bulk_edit', path='edit', detail=False)
  263. class ObjectPermissionBulkEditView(generic.BulkEditView):
  264. queryset = ObjectPermission.objects.all()
  265. filterset = filtersets.ObjectPermissionFilterSet
  266. table = tables.ObjectPermissionTable
  267. form = forms.ObjectPermissionBulkEditForm
  268. @register_model_view(ObjectPermission, 'bulk_rename', path='rename', detail=False)
  269. class ObjectPermissionBulkRenameView(generic.BulkRenameView):
  270. queryset = ObjectPermission.objects.all()
  271. @register_model_view(ObjectPermission, 'bulk_delete', path='delete', detail=False)
  272. class ObjectPermissionBulkDeleteView(generic.BulkDeleteView):
  273. queryset = ObjectPermission.objects.all()
  274. filterset = filtersets.ObjectPermissionFilterSet
  275. table = tables.ObjectPermissionTable
  276. #
  277. # Owner groups
  278. #
  279. @register_model_view(OwnerGroup, 'list', path='', detail=False)
  280. class OwnerGroupListView(generic.ObjectListView):
  281. queryset = OwnerGroup.objects.annotate(
  282. owner_count=count_related(Owner, 'group')
  283. )
  284. filterset = filtersets.OwnerGroupFilterSet
  285. filterset_form = forms.OwnerGroupFilterForm
  286. table = tables.OwnerGroupTable
  287. @register_model_view(OwnerGroup)
  288. class OwnerGroupView(generic.ObjectView):
  289. queryset = OwnerGroup.objects.all()
  290. layout = layout.SimpleLayout(
  291. left_panels=[
  292. OrganizationalObjectPanel(),
  293. ],
  294. right_panels=[
  295. ObjectsTablePanel(
  296. 'users.Owner',
  297. filters={'group_id': lambda ctx: ctx['object'].pk},
  298. title=_('Members'),
  299. exclude_columns=['group'],
  300. actions=[
  301. actions.AddObject(
  302. 'users.Owner',
  303. url_params={'group': lambda ctx: ctx['object'].pk},
  304. ),
  305. ],
  306. ),
  307. ],
  308. )
  309. @register_model_view(OwnerGroup, 'add', detail=False)
  310. @register_model_view(OwnerGroup, 'edit')
  311. class OwnerGroupEditView(generic.ObjectEditView):
  312. queryset = OwnerGroup.objects.all()
  313. form = forms.OwnerGroupForm
  314. @register_model_view(OwnerGroup, 'delete')
  315. class OwnerGroupDeleteView(generic.ObjectDeleteView):
  316. queryset = OwnerGroup.objects.all()
  317. @register_model_view(OwnerGroup, 'bulk_import', path='import', detail=False)
  318. class OwnerGroupBulkImportView(generic.BulkImportView):
  319. queryset = OwnerGroup.objects.all()
  320. model_form = forms.OwnerGroupImportForm
  321. @register_model_view(OwnerGroup, 'bulk_edit', path='edit', detail=False)
  322. class OwnerGroupBulkEditView(generic.BulkEditView):
  323. queryset = OwnerGroup.objects.all()
  324. filterset = filtersets.OwnerGroupFilterSet
  325. table = tables.OwnerGroupTable
  326. form = forms.OwnerGroupBulkEditForm
  327. @register_model_view(OwnerGroup, 'bulk_rename', path='rename', detail=False)
  328. class OwnerGroupBulkRenameView(generic.BulkRenameView):
  329. queryset = OwnerGroup.objects.all()
  330. @register_model_view(OwnerGroup, 'bulk_delete', path='delete', detail=False)
  331. class OwnerGroupBulkDeleteView(generic.BulkDeleteView):
  332. queryset = OwnerGroup.objects.all()
  333. filterset = filtersets.OwnerGroupFilterSet
  334. table = tables.OwnerGroupTable
  335. #
  336. # Owners
  337. #
  338. @register_model_view(Owner, 'list', path='', detail=False)
  339. class OwnerListView(generic.ObjectListView):
  340. queryset = Owner.objects.all()
  341. filterset = filtersets.OwnerFilterSet
  342. filterset_form = forms.OwnerFilterForm
  343. table = tables.OwnerTable
  344. @register_model_view(Owner)
  345. class OwnerView(GetRelatedModelsMixin, generic.ObjectView):
  346. queryset = Owner.objects.all()
  347. layout = layout.SimpleLayout(
  348. left_panels=[
  349. panels.OwnerPanel(),
  350. ObjectsTablePanel(
  351. 'users.Group',
  352. filters={'owner_id': lambda ctx: ctx['object'].pk},
  353. ),
  354. ObjectsTablePanel(
  355. 'users.User',
  356. filters={'owner_id': lambda ctx: ctx['object'].pk},
  357. ),
  358. ],
  359. right_panels=[
  360. RelatedObjectsPanel(),
  361. ],
  362. )
  363. def get_extra_context(self, request, instance):
  364. return {
  365. 'related_models': self.get_related_models(
  366. request,
  367. instance,
  368. omit=(Group, User),
  369. ),
  370. }
  371. @register_model_view(Owner, 'add', detail=False)
  372. @register_model_view(Owner, 'edit')
  373. class OwnerEditView(generic.ObjectEditView):
  374. queryset = Owner.objects.all()
  375. form = forms.OwnerForm
  376. @register_model_view(Owner, 'delete')
  377. class OwnerDeleteView(generic.ObjectDeleteView):
  378. queryset = Owner.objects.all()
  379. @register_model_view(Owner, 'bulk_import', path='import', detail=False)
  380. class OwnerBulkImportView(generic.BulkImportView):
  381. queryset = Owner.objects.all()
  382. model_form = forms.OwnerImportForm
  383. @register_model_view(Owner, 'bulk_edit', path='edit', detail=False)
  384. class OwnerBulkEditView(generic.BulkEditView):
  385. queryset = Owner.objects.all()
  386. filterset = filtersets.OwnerFilterSet
  387. table = tables.OwnerTable
  388. form = forms.OwnerBulkEditForm
  389. @register_model_view(Owner, 'bulk_rename', path='rename', detail=False)
  390. class OwnerBulkRenameView(generic.BulkRenameView):
  391. queryset = Owner.objects.all()
  392. @register_model_view(Owner, 'bulk_delete', path='delete', detail=False)
  393. class OwnerBulkDeleteView(generic.BulkDeleteView):
  394. queryset = Owner.objects.all()
  395. filterset = filtersets.OwnerFilterSet
  396. table = tables.OwnerTable