mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2026-02-27 08:50:46 +01:00
v2.5.5-dev: phpMyAdmin CSRF exempt, login error handling, phpMyAdmin install script
- urls.py: serve_phpmyadmin with @csrf_exempt for sign-in POST - loginSystem/views.py: loadLoginPage error handling; friendly 503 on DB access denied - csrfMiddleware.py: optional path-based CSRF exempt for /phpmyadmin/, /snappymail/ - fix-phpmyadmin-install.sh: install/fix phpMyAdmin under public/phpmyadmin (signin + config)
This commit is contained in:
17
CyberCP/csrfMiddleware.py
Normal file
17
CyberCP/csrfMiddleware.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Custom CSRF middleware that exempts /phpmyadmin/ and /snappymail/ so their
|
||||
PHP sign-in forms (POST) do not get 403 CSRF verification failed.
|
||||
"""
|
||||
from django.middleware.csrf import CsrfViewMiddleware
|
||||
|
||||
|
||||
class CsrfExemptPhpMyAdminMiddleware(CsrfViewMiddleware):
|
||||
"""CSRF middleware that skips verification for phpMyAdmin and SnappyMail paths."""
|
||||
|
||||
EXEMPT_PREFIXES = ('/phpmyadmin/', '/snappymail/')
|
||||
|
||||
def process_view(self, request, callback, callback_args, callback_kwargs):
|
||||
if request.path.startswith(self.EXEMPT_PREFIXES):
|
||||
return None # Skip CSRF check
|
||||
return super().process_view(request, callback, callback_args, callback_kwargs)
|
||||
@@ -20,20 +20,27 @@ from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.views.static import serve
|
||||
from django.views.generic import RedirectView
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from firewall import views as firewall_views
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def serve_phpmyadmin(request, path):
|
||||
"""Serve phpMyAdmin files; CSRF exempt so sign-in form POST does not get 403."""
|
||||
return serve(request, path, document_root=os.path.join(settings.PUBLIC_ROOT, 'phpmyadmin'))
|
||||
|
||||
# Plugin routes are no longer hardcoded here; pluginHolder.urls dynamically
|
||||
# includes each installed plugin (under /plugins/<name>/) so settings and
|
||||
# other plugin pages work for any installed plugin.
|
||||
|
||||
# Optional app: may be missing after clean clone or git clean -fd (not in all repo trees).
|
||||
# When missing or broken, register a placeholder so {% url 'emailMarketing' %} in templates never raises Reverse not found.
|
||||
# When missing or broken, register a placeholder so {% url 'emailMarketing' %} in templates never raises Reverse not found. Redirect to Plugin Store.
|
||||
_optional_email_marketing = []
|
||||
try:
|
||||
_optional_email_marketing.append(path('emailMarketing/', include('emailMarketing.urls')))
|
||||
except (ModuleNotFoundError, ImportError, AttributeError):
|
||||
_optional_email_marketing.append(
|
||||
path('emailMarketing/', RedirectView.as_view(url='/base/', permanent=False), name='emailMarketing')
|
||||
path('emailMarketing/', RedirectView.as_view(url='/plugins/installed?view=store', permanent=False), name='emailMarketing')
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@@ -43,7 +50,7 @@ urlpatterns = [
|
||||
re_path(r'^snappymail/?$', RedirectView.as_view(url='/snappymail/index.php', permanent=False)),
|
||||
re_path(r'^snappymail/(?P<path>.*)$', serve, {'document_root': os.path.join(settings.PUBLIC_ROOT, 'snappymail')}),
|
||||
re_path(r'^phpmyadmin/?$', RedirectView.as_view(url='/phpmyadmin/index.php', permanent=False)),
|
||||
re_path(r'^phpmyadmin/(?P<path>.*)$', serve, {'document_root': os.path.join(settings.PUBLIC_ROOT, 'phpmyadmin')}),
|
||||
re_path(r'^phpmyadmin/(?P<path>.*)$', serve_phpmyadmin),
|
||||
path('base/', include('baseTemplate.urls')),
|
||||
path('imunifyav/', firewall_views.imunifyAV, name='imunifyav_root'),
|
||||
path('ImunifyAV/', firewall_views.imunifyAV, name='imunifyav_root_legacy'),
|
||||
|
||||
47
fix-phpmyadmin-install.sh
Executable file
47
fix-phpmyadmin-install.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# Install/fix phpMyAdmin under /usr/local/CyberCP/public/phpmyadmin (creates signin + full app)
|
||||
set -e
|
||||
PUBLIC=/usr/local/CyberCP/public
|
||||
PMA_DIR=$PUBLIC/phpmyadmin
|
||||
VERSION=5.2.3
|
||||
TARBALL=$PUBLIC/phpmyadmin.tar.gz
|
||||
|
||||
echo "[$(date -Iseconds)] Installing phpMyAdmin to $PMA_DIR ..."
|
||||
sudo mkdir -p "$PUBLIC"
|
||||
sudo rm -rf "$PMA_DIR"
|
||||
sudo wget -q -O "$TARBALL" "https://files.phpmyadmin.net/phpMyAdmin/${VERSION}/phpMyAdmin-${VERSION}-all-languages.tar.gz" || { echo "Download failed"; exit 1; }
|
||||
[ -f "$TARBALL" ] && [ $(stat -c%s "$TARBALL") -gt 1000000 ] || { echo "Tarball missing or too small"; exit 1; }
|
||||
sudo tar -xzf "$TARBALL" -C "$PUBLIC"
|
||||
if [ -d "$PUBLIC/phpMyAdmin-${VERSION}-all-languages" ]; then
|
||||
sudo mv "$PUBLIC/phpMyAdmin-${VERSION}-all-languages" "$PMA_DIR"
|
||||
else
|
||||
sudo mv "$PUBLIC/phpMyAdmin-"*"-all-languages" "$PMA_DIR" 2>/dev/null || true
|
||||
fi
|
||||
sudo rm -f "$TARBALL"
|
||||
|
||||
[ -d "$PMA_DIR" ] || { echo "phpmyadmin dir not created"; exit 1; }
|
||||
|
||||
# Config: use sample if present, then ensure signon block
|
||||
BLOWFISH=$(openssl rand -hex 16)
|
||||
if [ -f "$PMA_DIR/config.sample.inc.php" ]; then
|
||||
sudo cp "$PMA_DIR/config.sample.inc.php" "$PMA_DIR/config.inc.php"
|
||||
sudo sed -i "s|blowfish_secret.*|blowfish_secret'] = '${BLOWFISH}';|" "$PMA_DIR/config.inc.php" 2>/dev/null || true
|
||||
fi
|
||||
sudo bash -c 'cat >> '"$PMA_DIR"'/config.inc.php << "PMACONF"
|
||||
|
||||
$i = 0;
|
||||
$i++;
|
||||
$cfg["Servers"][$i]["AllowNoPassword"] = false;
|
||||
$cfg["Servers"][$i]["auth_type"] = "signon";
|
||||
$cfg["Servers"][$i]["SignonSession"] = "SignonSession";
|
||||
$cfg["Servers"][$i]["SignonURL"] = "phpmyadminsignin.php";
|
||||
$cfg["Servers"][$i]["LogoutURL"] = "phpmyadminsignin.php?logout";
|
||||
$cfg["Servers"][$i]["host"] = "127.0.0.1";
|
||||
$cfg["Servers"][$i]["port"] = "3306";
|
||||
$cfg["TempDir"] = "/usr/local/CyberCP/public/phpmyadmin/tmp";
|
||||
PMACONF'
|
||||
sudo mkdir -p "$PMA_DIR/tmp"
|
||||
sudo cp /usr/local/CyberCP/plogical/phpmyadminsignin.php "$PMA_DIR/phpmyadminsignin.php"
|
||||
sudo chown -R lscpd:lscpd "$PMA_DIR"
|
||||
echo "[$(date -Iseconds)] phpMyAdmin install done. Test: https://YOUR_IP:2087/phpmyadmin/phpmyadminsignin.php"
|
||||
exit 0
|
||||
@@ -165,6 +165,36 @@ def verifyLogin(request):
|
||||
|
||||
@ensure_csrf_cookie
|
||||
def loadLoginPage(request):
|
||||
try:
|
||||
return _loadLoginPage(request)
|
||||
except Exception as e:
|
||||
try:
|
||||
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||
import traceback
|
||||
logging.writeToFile("loadLoginPage error: %s\n%s" % (str(e), traceback.format_exc()))
|
||||
except Exception:
|
||||
pass
|
||||
# User-friendly message for database connection errors
|
||||
from django.db.utils import OperationalError
|
||||
err_str = str(e).lower()
|
||||
if isinstance(e, OperationalError) or 'access denied' in err_str or '1045' in err_str:
|
||||
msg = (
|
||||
"Database connection failed (Access denied for user 'cyberpanel'@'localhost'). "
|
||||
"Check: 1) MariaDB is running (systemctl status mariadb). "
|
||||
"2) Password in /etc/cyberpanel/mysqlPassword matches the MySQL user used by the panel. "
|
||||
"3) User exists: mysql -u root -p -e \"SELECT User,Host FROM mysql.user WHERE User='cyberpanel';\""
|
||||
)
|
||||
return HttpResponse(msg, status=503, content_type="text/plain; charset=utf-8")
|
||||
try:
|
||||
# Minimal cosmetic so template does not break (login.html uses cosmetic.MainDashboardCSS)
|
||||
class _MinimalCosmetic:
|
||||
MainDashboardCSS = ''
|
||||
return render(request, 'loginSystem/login.html', {'cosmetic': _MinimalCosmetic()})
|
||||
except Exception:
|
||||
return HttpResponse("Server error. Check /home/cyberpanel/error-logs.txt", status=500, content_type="text/plain")
|
||||
|
||||
|
||||
def _loadLoginPage(request):
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
currentACL = ACLManager.loadedACL(userID)
|
||||
|
||||
Reference in New Issue
Block a user