Repository: zopefoundation/Zope (language: python)

Issue:
Authentication error viewing ZMI with a user defined outside of zope root
## BUG/PROBLEM REPORT / FEATURE REQUEST

`manage_page_header` makes a `head` like this:

```html
<!doctype html>
<html lang="en">
<head>

<meta http-equiv="content-type" content="text/html;charset=utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />

<title>Zope</title>

	<link rel="stylesheet" type="text/css" href="/++resource++zmi/bootstrap-4.6.0/bootstrap.min.css" />
	<link rel="stylesheet" type="text/css" href="/++resource++zmi/fontawesome-free-5.15.2/css/all.css" />
	<link rel="stylesheet" type="text/css" href="/++resource++zmi/zmi_base.css" />
	<script src="/++resource++zmi/jquery-3.5.1.min.js"></script>
	<script src="/++resource++zmi/bootstrap-4.6.0/bootstrap.bundle.min.js"></script>
	<script src="/++resource++zmi/ace.ajax.org/ace.js"></script>
	<script src="/++resource++zmi/zmi_base.js"></script>

```


Since the "Restrict access to static ZMI resources." changes from ce5288a1b (Update ACE editor 1.4.4 to 1.5.0 (#1166), 2023-10-11), the links for zmi require view management screens permission.

Because the link is like this:

```html
	<script src="/++resource++zmi/jquery-3.5.1.min.js"></script>
```

in practice they require view management screens permissions **at the root of zope**.


### What I did:

1. create a folder `subfolder`
2. create a user folder in that folder
3. create a user `u` with manager role in `/subfolder/acl_users`
4. login as `u` and visit `/subfolder/manage_main`

### What I expect to happen:

view management screens

### What actually happened:

401 error while getting CSS and javascripts

### What version of Python and Zope/Addons I am using:

this started from 5.9 and is still the case on current master.

---

I'll send a pull request, I think we can change the links generation logic to include the authentication path from the user. In the example case the link would be:


```html
	<script src="/subfolder/++resource++zmi/jquery-3.5.1.min.js"></script>
```




Discussion / hints:

Fix patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 609fd1b031..3b9767a59d 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -29,6 +29,9 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst
 - Support ``Chameleon`` ``structure`` expression type.
   Fixes `#1077 <https://github.com/zopefoundation/Zope/issues/1077>`_.
 
+- Fix authentication error viewing ZMI with a user defined outside of zope root.
+  Fixes `#1195 <https://github.com/zopefoundation/Zope/issues/1195>`_.
+
 
 5.9 (2023-11-24)
 ----------------
diff --git a/src/App/dtml/manage_page_header.dtml b/src/App/dtml/manage_page_header.dtml
index 2dcb047552..e8b8659e10 100644
--- a/src/App/dtml/manage_page_header.dtml
+++ b/src/App/dtml/manage_page_header.dtml
@@ -10,24 +10,27 @@
 </dtml-let>
 
 <title><dtml-if title_or_id><dtml-var title_or_id><dtml-else>Zope</dtml-if></title>
+<dtml-let basepath="'/'.join([''] + [p for p in (REQUEST['BASEPATH1'], REQUEST.get('AUTHENTICATION_PATH')) if p])">
+
 <dtml-in css_urls>
-	<link rel="stylesheet" type="text/css" href="&dtml-BASEPATH1;&dtml-sequence-item;" />
+	<link rel="stylesheet" type="text/css" href="&dtml-basepath;&dtml-sequence-item;" />
 </dtml-in>
 <dtml-in js_urls>
-	<script src="&dtml-BASEPATH1;&dtml-sequence-item;"></script>
+	<script src="&dtml-basepath;&dtml-sequence-item;"></script>
 </dtml-in>
 
-<link rel="shortcut icon" type="image/x-icon" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/favicon.ico" />
-<link rel="apple-touch-icon" sizes="180x180" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/apple-touch-icon.png" />
-<link rel="icon" type="image/png" sizes="32x32" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/favicon-32x32.png" />
-<link rel="icon" type="image/png" sizes="16x16" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/favicon-16x16.png" />
-<link rel="manifest" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/site.webmanifest" />
-<link rel="mask-icon" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/safari-pinned-tab.svg" color="#5bbad5" />
-<meta name="msapplication-config" content="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/browserconfig.xml"/>
+<link rel="shortcut icon" type="image/x-icon" href="&dtml-basepath;/++resource++zmi/logo/favicon/favicon.ico" />
+<link rel="apple-touch-icon" sizes="180x180" href="&dtml-basepath;/++resource++zmi/logo/favicon/apple-touch-icon.png" />
+<link rel="icon" type="image/png" sizes="32x32" href="&dtml-basepath;/++resource++zmi/logo/favicon/favicon-32x32.png" />
+<link rel="icon" type="image/png" sizes="16x16" href="&dtml-basepath;/++resource++zmi/logo/favicon/favicon-16x16.png" />
+<link rel="manifest" href="&dtml-basepath;/++resource++zmi/logo/favicon/site.webmanifest" />
+<link rel="mask-icon" href="&dtml-basepath;/++resource++zmi/logo/favicon/safari-pinned-tab.svg" color="#5bbad5" />
+<meta name="msapplication-config" content="&dtml-basepath;/++resource++zmi/logo/favicon/browserconfig.xml"/>
 <meta name="msapplication-TileColor" content="#2d89ef" />
 <meta name="theme-color" content="#ffffff" />
 
 </head>
+</dtml-let>
 <!-- REFACT what is a better way to get the last part of the current URL? -->
 <body id="nodeid-<dtml-var "getId()">" class="zmi zmi-<dtml-var "this().meta_type.replace(' ', '-').replace('(', '').replace(')', '')"> zmi-<dtml-var "URL0[_.len(URL1)+1:]">">
 </dtml-unless>


Test patch:
diff --git a/src/zmi/styles/tests.py b/src/zmi/styles/tests.py
index 256edfa74b..36c7b65bec 100644
--- a/src/zmi/styles/tests.py
+++ b/src/zmi/styles/tests.py
@@ -21,6 +21,8 @@ def setupZCML():
 class SubscriberTests(Testing.ZopeTestCase.FunctionalTestCase):
     """Testing .subscriber.*"""
 
+    base_path = f'/{Testing.ZopeTestCase.folder_name}'
+
     def call_manage_main(self):
         """Call /folder/manage_main and return the HTML text."""
         def _call_manage_main(self):
@@ -29,7 +31,7 @@ def _call_manage_main(self):
             # which the WSGI publisher does not expect.
             endInteraction()
             response = self.publish(
-                f'/{Testing.ZopeTestCase.folder_name}/manage_main',
+                f'{self.base_path}/manage_main',
                 basic=basic_auth)
             return str(response)
         return temporaryPlacelessSetUp(
@@ -40,11 +42,11 @@ def test_subscriber__css_paths__1(self):
         from .subscriber import css_paths
         body = self.call_manage_main()
         for path in css_paths(None):
-            self.assertIn(path, body)
+            self.assertIn(f'href="{self.base_path}{path}"', body)
 
     def test_subscriber__js_paths__1(self):
         """The paths it returns are rendered in the ZMI."""
         from .subscriber import js_paths
         body = self.call_manage_main()
         for path in js_paths(None):
-            self.assertIn(path, body)
+            self.assertIn(f'src="{self.base_path}{path}"', body)