Hello,
I'm attempting to create an application in Luci JS and wish to restrict access to this application for specific users while hiding options like Overview, Network, etc.
I've tried using ACL to control access to different directories for different users, but I'm facing an issue:
When I hide item at the highest order (such as Overview), Luci still opens Overview item at the root URL, resulting in a 403 Forbidden error.
"admin/status": {
"title": "Status",
"order": 10,
"action": {
"type": "firstchild",
"preferred": "overview",
"recurse": true
},
"depends": {
"acl": [ "luci-app-ap-admin" ]
}
},
So the question is: How can I ensure that the root URL opens an item that is not restricted by ACL permissions?
Any help would be greatly appreciated.
mikma
July 30, 2024, 9:42pm
2
I know this is an old question but I ran into the same problem myself. I have posted an issue which also contains a bug fix:
opened 09:20PM - 30 Jul 24 UTC
A bug in resolve_firstchild means it may return a page the user hasn't access to… causing 403 Forbidden, A patch which fixes the bug is included at the bottom.
The following can reproduce the problem:
1. Create a user such as test in the openwrt shell, and set the password (the script below expects "test"):
```
echo "test::0:0:99999:7:::" >> /etc/shadow
echo "test:x:1047:100:Test user:/tmp:/bin/false" >> /etc/passwd
passwd test
```
2. Add a login section to rpcd for the user:
```
uci batch <<EOF
add rpcd login
set rpcd.@login[-1].username='test'
set rpcd.@login[-1].password='\$p\$test'
add_list rpcd.@login[-1].read='unauthenticated'
add_list rpcd.@login[-1].read='luci-base'
commit rpcd
EOF
```
3. Add a menu item:
```
cat > /usr/share/luci/menu.d/test-acl.json <<EOF
{
"admin/system/test-acl": {
"title": "Test ACL",
"order": 100,
"action": {
"type": "template",
"path": "test_acl"
}
}
}
EOF
```
4. Add a page template:
```
cat > /usr/share/ucode/luci/template/test_acl.ut <<EOF
{% include('header') %}
<p>Hello World</p>
{% include('footer') %}
EOF
```
5. Run the following function with the hostname/IP address as the argument:
```
run_test() {
hostname=$1
tmpfile=$(mktemp)
cookiefile=$(mktemp)
curl --silent --cookie-jar $cookiefile --location --data-urlencode "luci_username=test" --data-urlencode "luci_password=test" "http://$hostname/cgi-bin/luci/" > $tmpfile
if ! grep "Hello World" $tmpfile >/dev/null; then
echo "Test FAILED"
else
echo "Test PASSED"
fi
rm "$tmpfile"
rm "$cookiefile"
}
run_test 192.168.0.1
```
Actual behavior:
Test FAILED
Expected behavior:
Test PASSED
## Additional Information:
OpenWrt version information from system `/etc/openwrt_release`
```
DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='SNAPSHOT'
DISTRIB_REVISION='r27041-7686ce4a91'
DISTRIB_TARGET='x86/64'
DISTRIB_ARCH='x86_64'
DISTRIB_DESCRIPTION='OpenWrt SNAPSHOT r27041-7686ce4a91'
DISTRIB_TAINTS=''
```
## Patch
```
--- /usr/share/ucode/luci/dispatcher.uc.orig 2024-07-30 19:31:27.890643889 +0000
+++ /usr/share/ucode/luci/dispatcher.uc 2024-07-30 19:31:39.122534632 +0000
@@ -582,7 +582,7 @@
session = is_authenticated(node.auth);
let cacl = child.depends?.acl;
- let login = login_allowed || child.auth?.login;
+ let login = !session && (login_allowed || child.auth?.login);
if (login || check_acl_depends(cacl, session?.acls?.["access-group"]) != null) {
if (child.title && type(child.action) == "object") {
```