views.py 16 KB

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