Skip to content

Commit c7228d0

Browse files
authored
Merge pull request #54 from RCAccelerator/db
Add utils to create RCAccelerator users/tokens
2 parents de2a318 + 974324e commit c7228d0

7 files changed

Lines changed: 138 additions & 3 deletions

File tree

Containerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ RUN chown -R tools:tools /app
1212
COPY feedback_exporter feedback_exporter
1313
COPY data_scraper data_scraper
1414
COPY evaluation evaluation
15+
COPY chatbot_db chatbot_db
1516
COPY pdm.lock pyproject.toml Makefile .
1617
RUN make install-pdm install-global
1718

chatbot_db/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# User and Token creation script for RCAccelerator
2+
3+
This Python script creates a user and an associated access token in a PostgreSQL database, based on a schema with `users` and `tokens` tables. It is designed for RCAccelerator, to be interactive and secure, using bcrypt for password hashing.
4+
5+
Run the script:
6+
7+
```bash
8+
python add_user.py
9+
```
10+
11+
You will be prompted for:
12+
13+
* The PostgreSQL DATABASE_URL (format: postgresql://user:pass@host:port/dbname)
14+
* The username and email of the user
15+
* The password (input hidden)
16+
17+
A hashed password will be stored in the users table, and a 30-days token will be created in the tokens table.

chatbot_db/__init__.py

Whitespace-only changes.

chatbot_db/add_user.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""
2+
Tool to add new users to the chatbot database with authentication tokens.
3+
Handles user creation and token generation with secure password hashing.
4+
"""
5+
import uuid
6+
import getpass
7+
import sys
8+
from datetime import datetime, timedelta, UTC
9+
10+
import bcrypt
11+
from sqlalchemy import create_engine, Column, String, TIMESTAMP, Table, MetaData, select, exc
12+
from sqlalchemy.dialects.postgresql import UUID
13+
14+
def main():
15+
"""Entry point for chatbot_db module."""
16+
database_url = input("Enter your DATABASE URL "
17+
"(e.g. postgresql://user:pass@host:port/db): ").strip()
18+
username = input("Enter username: ").strip()
19+
email = input("Enter email: ").strip()
20+
password = getpass.getpass("Enter password (will be hashed): ")
21+
password_hash = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
22+
engine = create_engine(database_url)
23+
metadata = MetaData()
24+
users = Table(
25+
"users", metadata,
26+
Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4),
27+
Column("username", String(150), nullable=False, unique=True),
28+
Column("password_hash", String(255), nullable=False),
29+
Column("email", String(150), nullable=False)
30+
)
31+
tokens = Table(
32+
"tokens", metadata,
33+
Column("token", String(64), primary_key=True),
34+
Column("username", String(50)),
35+
Column("created_at", TIMESTAMP, default=datetime.now(UTC)),
36+
Column("expires_at", TIMESTAMP, nullable=False)
37+
)
38+
metadata.create_all(engine)
39+
with engine.connect() as conn:
40+
user_exists = conn.execute(
41+
select(users.c.username).where(users.c.username == username)
42+
).fetchone()
43+
44+
if user_exists:
45+
print(f"Error: User '{username}' already exists. Please choose a different username.")
46+
sys.exit(1)
47+
48+
try:
49+
with engine.begin() as conn:
50+
user_id = uuid.uuid4()
51+
conn.execute(users.insert().values(
52+
id=user_id,
53+
username=username,
54+
password_hash=password_hash,
55+
email=email
56+
))
57+
58+
token_value = uuid.uuid4().hex
59+
conn.execute(tokens.insert().values(
60+
token=token_value,
61+
username=username,
62+
created_at=datetime.now(UTC),
63+
expires_at=datetime.now(UTC) + timedelta(days=30)
64+
))
65+
66+
print(f"\n User '{username}' created with token: {token_value}")
67+
except exc.IntegrityError as e:
68+
print("Error: Database integrity error occurred. User may already exist or "
69+
"there's a constraint violation.")
70+
print(f"Details: {str(e)}")
71+
sys.exit(1)
72+
except (ConnectionError, TimeoutError) as e:
73+
print(f"Error: An unexpected error occurred: {str(e)}")
74+
sys.exit(1)
75+
76+
if __name__ == "__main__":
77+
main()

pdm.lock

Lines changed: 39 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies = [
2020
"requests-kerberos>=0.15.0",
2121
"browser-cookie3>=0.19.1",
2222
"httpx-gssapi>=0.2.0",
23+
"bcrypt>=4.3.0",
2324
]
2425
requires-python = "==3.12.*"
2526

@@ -42,6 +43,7 @@ feedback_exporter = "feedback_exporter.export_feedback:main"
4243
evaluation = "evaluation.evaluation:main"
4344
osp_doc_scraper = "data_scraper.main:osp_doc_scraper"
4445
solutions_scraper = "data_scraper.main:solutions_scraper"
46+
chatbot_db = "chatbot_db:main"
4547

4648
[tool.setuptools.packages.find]
4749
include = ["data_scraper*", "feedback_exporter*"]

tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ commands =
1818
description = run Pylint
1919
commands =
2020
{[testenv]commands}
21-
pylint {posargs:./data_scraper ./feedback_exporter ./evaluation}
21+
pylint {posargs:./data_scraper ./feedback_exporter ./evaluation ./chatbot_db}
2222

2323
[testenv:ruff]
2424
description = run ruff
2525
commands =
2626
{[testenv]commands}
27-
ruff check {posargs:./data_scraper ./feedback_exporter ./evaluation}
27+
ruff check {posargs:./data_scraper ./feedback_exporter ./evaluation ./chatbot_db}

0 commit comments

Comments
 (0)