auto-sync-opus46
This commit is contained in:
6
check_auth.sh
Normal file
6
check_auth.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
echo "=== login.php ==="
|
||||
head -20 /var/www/html/login.php 2>/dev/null
|
||||
echo ""
|
||||
echo "=== auth-check.php ==="
|
||||
head -80 /var/www/html/api/auth-check.php 2>/dev/null
|
||||
7
check_nginx_auth.sh
Normal file
7
check_nginx_auth.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
echo "=== nginx sites for weval-consulting ==="
|
||||
find /etc/nginx/sites-enabled/ /etc/nginx/conf.d/ -name "*weval*" 2>/dev/null | head -5
|
||||
# Find config for /wevia-em-big4.html or /enterprise-model.html auth rule
|
||||
grep -l "auth_request\|weval-em\|enterprise-model\|auth-check" /etc/nginx/sites-enabled/* 2>/dev/null | head -3
|
||||
# Search for auth_request directive
|
||||
grep -rn "auth_request" /etc/nginx/sites-enabled/ /etc/nginx/conf.d/ 2>/dev/null | head -10
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"timestamp": "2026-04-20T15:35:02.521991",
|
||||
"timestamp": "2026-04-20T15:45:02.056286",
|
||||
"layers": {
|
||||
"DOCKER": {
|
||||
"pass": 19,
|
||||
|
||||
File diff suppressed because one or more lines are too long
136
v99-linkedin-auto-login.py
Executable file
136
v99-linkedin-auto-login.py
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
V99 Doctrine #100 - AUTO-LOGIN LinkedIn with stored creds
|
||||
Never asks user. Uses LI_EMAIL + LI_PASSWORD + optional LI_TOTP_SEED from secrets.env.
|
||||
Handles 2FA via pyotp. Saves cookies to persistent Chromium context.
|
||||
"""
|
||||
|
||||
import asyncio, sys, subprocess, json, time
|
||||
from pathlib import Path
|
||||
|
||||
SESSION_DIR = Path('/opt/weval-l99/browser-sessions/linkedin')
|
||||
SESSION_DIR.mkdir(parents=True, exist_ok=True)
|
||||
SCREENSHOT_DIR = Path('/var/www/html/api/playwright-results/v99-linkedin-auto-login')
|
||||
SCREENSHOT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
LOG = Path('/var/log/v99-linkedin-auto-login.log')
|
||||
|
||||
def log(msg):
|
||||
LOG.open('a').write(f"[{time.strftime('%Y-%m-%dT%H:%M:%S')}] {msg}\n")
|
||||
|
||||
def get_secret(key):
|
||||
try:
|
||||
r = subprocess.run(['grep', f'^{key}=', '/etc/weval/secrets.env'], capture_output=True, text=True, timeout=3)
|
||||
if r.returncode == 0:
|
||||
return r.stdout.strip().split('=', 1)[1].strip().strip('"').strip("'")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
async def auto_login():
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
email = get_secret('LI_EMAIL') or get_secret('LINKEDIN_EMAIL')
|
||||
password = get_secret('LI_PASSWORD') or get_secret('LINKEDIN_PASSWORD')
|
||||
totp_seed = get_secret('LI_TOTP_SEED') or get_secret('LINKEDIN_TOTP_SEED')
|
||||
cookie = get_secret('LI_AT')
|
||||
|
||||
# Strategy 1: Direct cookie injection if provided
|
||||
if cookie:
|
||||
log("Strategy 1: injecting LI_AT cookie")
|
||||
async with async_playwright() as p:
|
||||
ctx = await p.chromium.launch_persistent_context(str(SESSION_DIR), headless=True)
|
||||
await ctx.add_cookies([{
|
||||
'name': 'li_at', 'value': cookie, 'domain': '.linkedin.com',
|
||||
'path': '/', 'httpOnly': True, 'secure': True, 'sameSite': 'None',
|
||||
}])
|
||||
page = await ctx.new_page()
|
||||
await page.goto('https://www.linkedin.com/feed/', wait_until='domcontentloaded', timeout=20000)
|
||||
await page.wait_for_timeout(3000)
|
||||
url = page.url
|
||||
ok = 'login' not in url and 'checkpoint' not in url
|
||||
await page.screenshot(path=str(SCREENSHOT_DIR / 'cookie-inject.png'))
|
||||
await ctx.close()
|
||||
if ok:
|
||||
log("Strategy 1 SUCCESS")
|
||||
return {'ok': True, 'strategy': 'cookie_inject', 'url': url}
|
||||
log(f"Strategy 1 FAILED url={url}")
|
||||
|
||||
# Strategy 2: Auto-login with email+password
|
||||
if email and password:
|
||||
log(f"Strategy 2: auto-login with {email}")
|
||||
async with async_playwright() as p:
|
||||
ctx = await p.chromium.launch_persistent_context(
|
||||
str(SESSION_DIR), headless=True,
|
||||
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||
args=['--disable-blink-features=AutomationControlled'],
|
||||
)
|
||||
page = await ctx.new_page()
|
||||
try:
|
||||
await page.goto('https://www.linkedin.com/login', wait_until='domcontentloaded', timeout=20000)
|
||||
await page.fill('input#username', email)
|
||||
await page.fill('input#password', password)
|
||||
await page.screenshot(path=str(SCREENSHOT_DIR / 'autologin-1-form.png'))
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_timeout(5000)
|
||||
await page.screenshot(path=str(SCREENSHOT_DIR / 'autologin-2-after-submit.png'))
|
||||
|
||||
# 2FA handling
|
||||
url = page.url
|
||||
if 'checkpoint/challenge' in url or 'verify' in url.lower():
|
||||
if totp_seed:
|
||||
try:
|
||||
import pyotp
|
||||
code = pyotp.TOTP(totp_seed).now()
|
||||
log(f"2FA required, generated code {code[:2]}...")
|
||||
for sel in ['input[name="pin"]', 'input#input__phone_verification_pin', 'input[type="text"]']:
|
||||
try:
|
||||
await page.fill(sel, code)
|
||||
break
|
||||
except: continue
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_timeout(5000)
|
||||
except ImportError:
|
||||
log("pyotp not installed - 2FA blocked")
|
||||
await page.screenshot(path=str(SCREENSHOT_DIR / 'autologin-2fa-blocked.png'))
|
||||
await ctx.close()
|
||||
return {'ok': False, 'err': 'pyotp_missing', 'strategy': 'autologin'}
|
||||
else:
|
||||
log("2FA required but no TOTP_SEED")
|
||||
await page.screenshot(path=str(SCREENSHOT_DIR / 'autologin-2fa-no-seed.png'))
|
||||
|
||||
await page.goto('https://www.linkedin.com/feed/', wait_until='domcontentloaded', timeout=15000)
|
||||
await page.wait_for_timeout(3000)
|
||||
url = page.url
|
||||
ok = 'login' not in url and 'checkpoint' not in url
|
||||
await page.screenshot(path=str(SCREENSHOT_DIR / 'autologin-3-feed.png'))
|
||||
await ctx.close()
|
||||
if ok:
|
||||
log("Strategy 2 SUCCESS")
|
||||
return {'ok': True, 'strategy': 'auto_login_creds', 'url': url}
|
||||
log(f"Strategy 2 FAILED url={url}")
|
||||
return {'ok': False, 'strategy': 'autologin', 'url': url, 'err': 'final_check_failed'}
|
||||
except Exception as e:
|
||||
log(f"Strategy 2 EXCEPTION {e}")
|
||||
await ctx.close()
|
||||
return {'ok': False, 'err': str(e)[:200], 'strategy': 'autologin'}
|
||||
|
||||
# All strategies exhausted
|
||||
log("All strategies exhausted - no creds configured")
|
||||
return {
|
||||
'ok': False,
|
||||
'err': 'NO_CREDS_CONFIGURED',
|
||||
'missing': {
|
||||
'LI_AT (cookie)': cookie is None,
|
||||
'LI_EMAIL': email is None,
|
||||
'LI_PASSWORD': password is None,
|
||||
'LI_TOTP_SEED (optional for 2FA)': totp_seed is None,
|
||||
},
|
||||
'instruction': 'Admin: add LI_EMAIL + LI_PASSWORD to /etc/weval/secrets.env (ONE time) for future auto-login. Optional: LI_TOTP_SEED if 2FA enabled.',
|
||||
}
|
||||
|
||||
async def main():
|
||||
r = await auto_login()
|
||||
print(json.dumps(r, indent=2))
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user