Compare commits
6 Commits
v1.3.5
...
a6d2d176ba
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6d2d176ba | ||
|
|
6e58cce73a | ||
|
|
7d821d1f7b | ||
|
|
c816cb4630 | ||
|
|
3c8d50162b | ||
|
|
483a747600 |
@@ -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:
|
||||
|
||||
@@ -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
1
data/ldap_config.json
Normal 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"}]}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user