Compare commits

..

6 Commits

Author SHA1 Message Date
Daniel Bedeleanu
a6d2d176ba debug: add detailed LDAP authentication logging with error traceback 2026-04-11 15:02:57 +03:00
Daniel Bedeleanu
6e58cce73a fix: restore original LDAP configuration with correct group mappings
- Use proper group names: inventory_admins (admin), inventory_users (user)
- Use relative groups_dn: 'ou=groups' (not absolute path)
- Matches original user's GUI-configured settings
2026-04-11 15:01:12 +03:00
Daniel Bedeleanu
7d821d1f7b config: add LDAP configuration for LLDAP server at 192.168.84.107:3890 2026-04-11 14:59:16 +03:00
Daniel Bedeleanu
c816cb4630 fix: enable DEBUG logging for development (configurable via LOG_LEVEL env var) 2026-04-11 14:56:13 +03:00
Daniel Bedeleanu
3c8d50162b debug: add detailed logging to login authentication flow
- Log when local auth succeeds/fails
- Log when password mismatch occurs
- Log LDAP auth attempts and failures
- Helps diagnose why login is returning 401
2026-04-11 14:55:31 +03:00
Daniel Bedeleanu
483a747600 fix: CORS configuration for both docker-compose and start_server.sh deployments
- Update start_server.sh to auto-detect local IP and export ALLOWED_ORIGINS
- Includes both localhost and detected IP on HTTP/HTTPS proxy ports
- Export JWT_SECRET_KEY with ephemeral key if not set
- Fix Romanian comments in docker-compose.yml to English
- Document two deployment methods in SESSION_STATE.md
2026-04-11 14:44:00 +03:00
6 changed files with 72 additions and 15 deletions

View File

@@ -14,7 +14,9 @@ LOG_FILE_PATH = os.path.join(LOGS_DIR, "backend.log")
def setup_logger():
logger = logging.getLogger("ainventory")
logger.setLevel(logging.INFO)
# Set to DEBUG for development; change to INFO for production
log_level = os.environ.get("LOG_LEVEL", "DEBUG")
logger.setLevel(getattr(logging, log_level.upper(), logging.INFO))
# Avoid duplicate handlers if setup multiple times
if logger.handlers:

View File

@@ -23,10 +23,13 @@ def get_ldap_config():
def authenticate_ldap(username, password):
config = get_ldap_config()
if not config.get("ldap_enabled"):
log.debug("LDAP: LDAP is disabled in config")
return None
log.debug(f"LDAP: Config loaded: server_uri={config.get('server_uri')}, base_dn={config.get('base_dn')}")
try:
server = ldap3.Server(config["server_uri"], use_ssl=config.get("use_tls", False), get_info=ldap3.ALL)
log.debug(f"LDAP: Server object created: {config['server_uri']}")
user_dn = config["user_template"].format(username=username)
log.debug(f"LDAP: Attempting bind for DN: {user_dn}")
@@ -91,7 +94,9 @@ def authenticate_ldap(username, password):
return assigned_role
except Exception as e:
log.debug(f"LDAP: Auth Error: {str(e)}")
log.error(f"LDAP: Auth Error: {type(e).__name__}: {str(e)}")
import traceback
log.debug(f"LDAP: Full traceback: {traceback.format_exc()}")
return None
pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto")
@@ -159,16 +164,24 @@ def login(form_data: schemas.UserLogin, db: Session = Depends(get_db)):
authenticated = False
if user and user.hashed_password:
if verify_password(form_data.password, user.hashed_password):
log.debug(f"Local auth successful for {form_data.username}")
authenticated = True
else:
log.debug(f"Local auth failed: password mismatch for {form_data.username}")
elif user and not user.hashed_password:
log.debug(f"User {form_data.username} exists but has no hashed password (LDAP user), skipping local auth")
# [SECURITY FIX C-02] Bypass for passwordless users has been removed.
# LDAP users must authenticate via the LDAP flow below.
pass
elif not user:
log.debug(f"User {form_data.username} not found in database, will try LDAP")
# If local failed, try LDAP
if not authenticated:
log.debug(f"Local auth failed for {form_data.username}, attempting LDAP")
ldap_role = authenticate_ldap(form_data.username, form_data.password)
if ldap_role:
log.debug(f"LDAP auth successful for {form_data.username}, role={ldap_role}")
authenticated = True
# Cache hash for offline support
new_hash = get_password_hash(form_data.password)
@@ -191,6 +204,7 @@ def login(form_data: schemas.UserLogin, db: Session = Depends(get_db)):
db.commit()
db.refresh(user)
else:
log.warning(f"Login failed: LDAP auth also failed for {form_data.username}")
raise HTTPException(status_code=401, detail="Invalid username or password, or insufficient permissions")
if not authenticated or not user:

1
data/ldap_config.json Normal file
View File

@@ -0,0 +1 @@
{"ldap_enabled": true, "server_uri": "ldap://192.168.84.107:3890", "base_dn": "dc=example,dc=com", "user_template": "cn={username},ou=people,dc=example,dc=com", "groups_dn": "ou=groups", "use_tls": false, "role_mappings": [{"group": "inventory_admins", "role": "admin"}, {"group": "inventory_users", "role": "user"}]}

View File

@@ -1,13 +1,52 @@
# CURRENT AI WORKING SESSION — COMPLETED
# CURRENT AI WORKING SESSION — IN PROGRESS
**Active AI:** Claude (Sonnet 4.6)
**Active AI:** Claude (Haiku 4.5)
**Last Updated:** 2026-04-11
**Current Version:** v1.3.5
**Branch:** dev (v1.3.5 release branch created)
**Branch:** dev
---
## 🎯 SESSION COMPLETED — ALL TASKS FINALIZED + ENGLISH COMPLIANCE VERIFIED
## 🔧 CURRENT ISSUE — CORS Configuration Clarification
### Two Deployment Methods
The project supports two distinct deployment approaches:
#### 1. **Docker Compose** (Production / Containerized)
- Uses `docker-compose.yml` with Caddy reverse proxy
- Backend container on internal port 8000 → exposed via Caddy on 3002
- Frontend container on internal port 3000 → exposed via Caddy on 3003
- **Configuration:** `ALLOWED_ORIGINS` environment variable in docker-compose.yml
- **Current setting:** `http://localhost:3000,http://localhost:3002`
- **For remote deployment:** Must update ALLOWED_ORIGINS to include actual domain (e.g., `https://192.168.84.140:3003`)
#### 2. **start_server.sh** (Development / Native)
- Runs Backend (uvicorn) + Frontend (Next.js) + local-ssl-proxy directly
- Backend on port 8000 → SSL proxy on 3002
- Frontend on port 3001 → SSL proxy on 3003
- **Issue Fixed:** Script now automatically detects local IP and exports ALLOWED_ORIGINS
- **Auto-configured for:** localhost + detected IP address on both HTTP and HTTPS
### Fix Applied (Commit Pending)
Updated `start_server.sh` to:
1. Detect local IP address (en0/en1 interface)
2. Export ALLOWED_ORIGINS with all applicable origins:
- HTTP localhost on dev ports (3000/3001)
- HTTPS localhost on proxy ports (3002/3003)
- HTTPS on detected IP address on proxy ports
3. Display CORS configuration when starting backend
4. Export JWT_SECRET_KEY if not already set
**Result:** Frontend at `https://<LOCAL_IP>:3003` can now access backend at `https://<LOCAL_IP>:3002` without CORS blocks.
### English Language Compliance
Fixed Romanian comments in docker-compose.yml:
- `"personalizează pentru producție"``"customize for production"`
- `"GENEREAZĂ O VALOARE SIGURĂ"``"GENERATE A SECURE VALUE"`
---
## 🎯 PREVIOUS SESSION — ALL TASKS FINALIZED + ENGLISH COMPLIANCE VERIFIED
### Final Status

View File

@@ -15,9 +15,9 @@ services:
environment:
- DATA_DIR=/app/data
- LOGS_DIR=/app/logs
# [M-01] CORS allowed origins — personalizează pentru producție
# [M-01] CORS allowed origins — customize for production
- ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3002
# [C-01] JWT secret key — GENEREAZĂ O VALOARE SIGURĂ PENTRU PRODUCȚIE!
# [C-01] JWT secret key — GENERATE A SECURE VALUE FOR PRODUCTION!
- JWT_SECRET_KEY=${JWT_SECRET_KEY:-change-me-in-production}
restart: unless-stopped

View File

@@ -25,8 +25,13 @@ source .venv/bin/activate
echo "📦 Updating Python dependencies..."
pip install -q -r backend/requirements.txt
# 4. Start Backend (Python/FastAPI)
# 4. Get Local IP and set CORS origins
LOCAL_IP=$(ipconfig getifaddr en0 || ipconfig getifaddr en1 || echo "localhost")
export ALLOWED_ORIGINS="http://localhost:$FRONTEND_PORT,http://localhost:$BACKEND_PORT,https://localhost:$FRONTEND_SSL_PORT,https://localhost:$BACKEND_SSL_PORT,https://$LOCAL_IP:$FRONTEND_SSL_PORT,https://$LOCAL_IP:$BACKEND_SSL_PORT"
export JWT_SECRET_KEY="${JWT_SECRET_KEY:-ephemeral-dev-key-$(date +%s)}"
echo "🔥 Starting Backend on port $BACKEND_PORT..."
echo " CORS origins: $ALLOWED_ORIGINS"
python3 -m uvicorn backend.main:app --host 0.0.0.0 --port $BACKEND_PORT --reload &
# 5. Start Frontend (Next.js)
@@ -39,11 +44,7 @@ cd ..
npx local-ssl-proxy --source $BACKEND_SSL_PORT --target $BACKEND_PORT --hostname 0.0.0.0 > /dev/null 2>&1 &
npx local-ssl-proxy --source $FRONTEND_SSL_PORT --target $FRONTEND_PORT --hostname 0.0.0.0 > /dev/null 2>&1 &
# 7. Get Local IP
LOCAL_IP=$(ipconfig getifaddr en0 || ipconfig getifaddr en1 || echo "<YOUR-IP>")
# 8. Printing Unified Access Banner
LOCAL_IP=$(ipconfig getifaddr en0 || ipconfig getifaddr en1 || echo "<YOUR-IP>")
# 7. Print Unified Access Banner
# Colors
GREEN='\033[0;32m'