File size: 6,968 Bytes
eb796ce
8e8453e
eb796ce
239b62a
 
8e8453e
239b62a
 
0a96fb2
 
239b62a
 
 
 
 
 
 
0a96fb2
239b62a
e1cf8c3
239b62a
e1cf8c3
239b62a
23bcbb8
 
 
239b62a
8e8453e
239b62a
 
0a96fb2
239b62a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eb796ce
239b62a
0a96fb2
eb796ce
 
239b62a
eb796ce
 
 
 
 
 
 
239b62a
eb796ce
0a96fb2
 
 
239b62a
 
 
 
 
 
 
 
 
 
8e8453e
239b62a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82fade8
8e8453e
239b62a
 
 
 
 
8e8453e
 
 
 
 
 
 
 
 
239b62a
 
8e8453e
 
239b62a
8e8453e
 
 
 
 
 
 
 
 
239b62a
8e8453e
 
239b62a
 
8e8453e
239b62a
 
8e8453e
239b62a
 
 
 
8e8453e
 
239b62a
 
 
 
82fade8
239b62a
 
 
 
8e8453e
291e0d7
 
82fade8
 
33cfdd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import os
import random
import sqlite3
import csv
import json
from datetime import datetime, timedelta
from flask import Flask, request, jsonify, redirect, session, render_template_string, send_file

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import pathlib

# Paths and setup
DATABASE = "/tmp/database.db"
EXPORT_CSV = "/data/otp_logs.csv"
EXPORT_JSON = "/data/otp_logs.json"
pathlib.Path("/data").mkdir(parents=True, exist_ok=True)

# App setup
app = Flask(__name__)
app.secret_key = os.getenv("FLASK_SECRET_KEY", "IDONTKNOWWHATISTHIS")

# Rate limiter setup
limiter = Limiter(key_func=get_remote_address)
limiter.init_app(app)

# Auth
ADMIN_USER = os.getenv("ADMIN_USER", "admin")
ADMIN_PASS = os.getenv("ADMIN_PASS", "Welcome123")
OTP_API_KEY = os.getenv("OTP_API_KEY", "IDONTKNOWWHATIMDOING")

# Templates
LOGIN_TEMPLATE = """
<!DOCTYPE html><html><head><title>Login</title></head><body>
<h2>Admin Login</h2>
<form method="post">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="Login">
</form>
</body></html>
"""

GEN_TEMPLATE = """
<!DOCTYPE html><html><head><title>OTP Admin</title></head><body>
<h2>Generate OTP</h2>
<form method="post">
  <button type="submit">Generate OTP</button>
</form>
{% if otp %}
<p><b>OTP:</b> {{ otp }}<br><b>Expires:</b> {{ expires }}</p>
{% endif %}

<hr>
<h3>Recent OTPs</h3>
<table border="1" cellpadding="5">
<tr><th>OTP</th><th>Generated At</th><th>Expires</th><th>Used At</th><th>Status</th><th>IP</th></tr>
{% for row in otps %}
<tr>
<td>{{ row[0] }}</td>
<td>{{ row[1] }}</td>
<td>{{ row[2] }}</td>
<td>{{ row[3] or "-" }}</td>
<td>{{ row[5] }}</td>
<td>{{ row[4] }}</td>
</tr>
{% endfor %}
</table>
<p><a href="/download-csv" target="_blank">📥 Download CSV</a> | <a href="/download-json" target="_blank">📥 Download JSON</a></p>
<a href="/logout">Logout</a>
</body></html>
"""

# DB init
def init_db():
    with sqlite3.connect(DATABASE) as conn:
        c = conn.cursor()
        c.execute("""CREATE TABLE IF NOT EXISTS otps (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            otp TEXT,
            generated_at TEXT,
            expires_at TEXT,
            used_at TEXT,
            ip_address TEXT,
            status TEXT
        )""")
        conn.commit()

init_db()

# Routes
@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        if request.form["username"] == ADMIN_USER and request.form["password"] == ADMIN_PASS:
            session["admin"] = True
            return redirect("/generate")
        else:
            return "Invalid credentials", 403
    return LOGIN_TEMPLATE

@app.route("/logout")
def logout():
    session.clear()
    return redirect("/login")

@app.route("/generate", methods=["GET", "POST"])
def generate_page():
    if not session.get("admin"):
        return redirect("/login")

    otp = None
    expires = None

    if request.method == "POST":
        otp = generate_otp()
        now = datetime.utcnow()
        expires_at = now + timedelta(minutes=10)
        with sqlite3.connect(DATABASE) as conn:
            c = conn.cursor()
            c.execute("INSERT INTO otps (otp, generated_at, expires_at, ip_address, status) VALUES (?, ?, ?, ?, ?)",
                      (otp, now.isoformat(), expires_at.isoformat(), request.remote_addr, "generated"))
            conn.commit()
        expires = expires_at.isoformat()
        export_logs()

    with sqlite3.connect(DATABASE) as conn:
        c = conn.cursor()
        c.execute("SELECT otp, generated_at, expires_at, used_at, ip_address, status FROM otps ORDER BY id DESC LIMIT 10")
        otps = c.fetchall()

    return render_template_string(GEN_TEMPLATE, otp=otp, expires=expires, otps=otps)

@app.route("/generate-otp", methods=["POST"])
@limiter.limit("10 per minute")
def api_generate_otp():
    api_key = request.headers.get("X-API-Key")
    if api_key != OTP_API_KEY:
        return jsonify({"error": "Invalid API key"}), 403

    otp = generate_otp()
    now = datetime.utcnow()
    expires_at = now + timedelta(minutes=10)
    with sqlite3.connect(DATABASE) as conn:
        c = conn.cursor()
        c.execute("INSERT INTO otps (otp, generated_at, expires_at, ip_address, status) VALUES (?, ?, ?, ?, ?)",
                  (otp, now.isoformat(), expires_at.isoformat(), request.remote_addr, "generated"))
        conn.commit()
    export_logs()
    return jsonify({"otp": otp, "expires": expires_at.isoformat()})

@app.route("/verify-otp", methods=["POST"])
@limiter.limit("30 per minute")
def verify_otp():
    data = request.get_json()
    otp = data.get("otp")

    with sqlite3.connect(DATABASE) as conn:
        c = conn.cursor()
        c.execute("SELECT id, expires_at, used_at, status FROM otps WHERE otp = ?", (otp,))
        row = c.fetchone()
        if not row:
            return jsonify({"valid": False, "reason": "Invalid OTP"})
        otp_id, expires_at, used_at, status = row

        if status == "used":
            return jsonify({"valid": False, "reason": "OTP already used"})

        if datetime.fromisoformat(expires_at) < datetime.utcnow():
            return jsonify({"valid": False, "reason": "OTP expired"})

        now = datetime.utcnow().isoformat()
        c.execute("UPDATE otps SET used_at = ?, status = ? WHERE id = ?", (now, "used", otp_id))
        conn.commit()
    export_logs()
    return jsonify({"valid": True})

@app.route("/download-csv")
def download_csv():
    export_logs()
    return send_file(EXPORT_CSV, as_attachment=True)

@app.route("/download-json")
def download_json():
    export_logs()
    return send_file(EXPORT_JSON, as_attachment=True)

@app.route("/debug")
def debug():
    return jsonify({
        "db_exists": os.path.exists(DATABASE),
        "csv_exists": os.path.exists(EXPORT_CSV),
        "json_exists": os.path.exists(EXPORT_JSON),
    })

# Helpers
def generate_otp():
    return str(random.randint(100000, 999999))

def export_logs():
    with sqlite3.connect(DATABASE) as conn:
        c = conn.cursor()
        c.execute("SELECT otp, generated_at, expires_at, used_at, ip_address, status FROM otps ORDER BY id DESC")
        rows = c.fetchall()

        # Save CSV
        with open(EXPORT_CSV, "w", newline="") as f:
            writer = csv.writer(f)
            writer.writerow(["otp", "generated_at", "expires_at", "used_at", "ip_address", "status"])
            writer.writerows(rows)

        # Save JSON
        json_data = [
            {
                "otp": row[0],
                "generated_at": row[1],
                "expires_at": row[2],
                "used_at": row[3],
                "ip_address": row[4],
                "status": row[5]
            }
            for row in rows
        ]
        with open(EXPORT_JSON, "w") as f:
            json.dump(json_data, f, indent=2)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=7860)