@@ -3380,6 +3380,24 @@ def test_history_view_bad_url(self):
33803380 ["article with ID “foo” doesn’t exist. Perhaps it was deleted?" ],
33813381 )
33823382
3383+ def test_history_view_without_permission_returns_403 (self ):
3384+ self .client .force_login (self .adduser )
3385+ for label , pk in [("existing" , self .a1 .pk ), ("missing" , 999999 )]:
3386+ with self .subTest (pk = label ):
3387+ response = self .client .get (
3388+ reverse ("admin:admin_views_article_history" , args = (pk ,))
3389+ )
3390+ self .assertEqual (response .status_code , 403 )
3391+
3392+ def test_history_view_with_view_or_change_permission_success (self ):
3393+ for permission , user in [("view" , self .viewuser ), ("change" , self .changeuser )]:
3394+ with self .subTest (permission = permission ):
3395+ self .client .force_login (user )
3396+ response = self .client .get (
3397+ reverse ("admin:admin_views_article_history" , args = (self .a1 .pk ,))
3398+ )
3399+ self .assertEqual (response .status_code , 200 )
3400+
33833401 def test_conditionally_show_add_section_link (self ):
33843402 """
33853403 The foreign key widget should only show the "add related" button if the
@@ -3527,6 +3545,56 @@ def test_shortcut_view_only_available_to_staff(self):
35273545 # Domain may depend on contrib.sites tests also run
35283546 self .assertRegex (response .url , "http://(testserver|example.com)/dummy/foo/" )
35293547
3548+ def test_shortcut_view_without_permission_returns_403 (self ):
3549+ obj = ModelWithStringPrimaryKey .objects .create (string_pk = "bar" )
3550+ model_ctype = ContentType .objects .get_for_model (ModelWithStringPrimaryKey )
3551+ shortcut_url = reverse ("admin:view_on_site" , args = (model_ctype .pk , obj .pk ))
3552+ # deleteuser has no view permission on ModelWithStringPrimaryKey.
3553+ self .client .force_login (self .deleteuser )
3554+ response = self .client .get (shortcut_url )
3555+ self .assertEqual (response .status_code , 403 )
3556+
3557+ def test_shortcut_view_with_view_or_change_permission_success (self ):
3558+ obj = ModelWithStringPrimaryKey .objects .create (string_pk = "bar" )
3559+ model_ctype = ContentType .objects .get_for_model (ModelWithStringPrimaryKey )
3560+ shortcut_url = reverse ("admin:view_on_site" , args = (model_ctype .pk , obj .pk ))
3561+ opts = ModelWithStringPrimaryKey ._meta
3562+ for permission in ["view" , "change" ]:
3563+ codename = get_permission_codename (permission , opts )
3564+ perm = get_perm (ModelWithStringPrimaryKey , codename )
3565+ with self .subTest (permission = permission ):
3566+ self .viewuser .user_permissions .set ([perm ])
3567+ self .client .force_login (self .viewuser )
3568+ response = self .client .get (shortcut_url )
3569+ self .assertEqual (response .status_code , 302 )
3570+
3571+ def test_shortcut_view_for_invalid_content_type_returns_404 (self ):
3572+ # An unknown or non-int content type id skips the permission check and
3573+ # falls back to the contenttypes shortcut view, which raises Http404.
3574+ self .client .force_login (self .deleteuser )
3575+ for content_type_id in [9999 , "not-an-int" , None ]:
3576+ with self .subTest (content_type_id = content_type_id ):
3577+ response = self .client .get (
3578+ reverse ("admin:view_on_site" , args = (content_type_id , 1 ))
3579+ )
3580+ self .assertEqual (response .status_code , 404 )
3581+
3582+ def test_shortcut_view_does_not_repeat_content_type_query (self ):
3583+ obj = ModelWithStringPrimaryKey .objects .create (string_pk = "bar" )
3584+ model_ctype = ContentType .objects .get_for_model (ModelWithStringPrimaryKey )
3585+ shortcut_url = reverse ("admin:view_on_site" , args = (model_ctype .pk , obj .pk ))
3586+ self .client .force_login (self .superuser )
3587+ # A warmup request populates the ContentType and Site caches, so only
3588+ # relevant queries are measured. The 4 expected queries are:
3589+ # 1. Load the session.
3590+ # 2. Load the user.
3591+ # 3. Look up the content type.
3592+ # 4. Fetch the target instance.
3593+ self .client .get (shortcut_url )
3594+ with self .assertNumQueries (4 ):
3595+ response = self .client .get (shortcut_url )
3596+ self .assertEqual (response .status_code , 302 )
3597+
35303598 def test_has_module_permission (self ):
35313599 """
35323600 has_module_permission() returns True for all users who
0 commit comments