Jun 28 - Update tenant self-service signup, trial period enforcement
This commit is contained in:
+34
-13
@@ -156,19 +156,31 @@ def drop_mysql_db_and_user(db_name, db_user, user_host='%'):
|
||||
|
||||
# ── admin seeding (writes into the tenant DB) ────────────────────────────────
|
||||
|
||||
def seed_admin(db_uri, email, username=None, full_name=None, expires_hours=72):
|
||||
"""Insert the first admin into a tenant DB with a set-password token.
|
||||
def seed_admin(db_uri, email, username=None, full_name=None, expires_hours=72,
|
||||
password_hash=None):
|
||||
"""Insert the first admin into a tenant DB.
|
||||
|
||||
Returns (username, token). The account is created with password_set=0 and an
|
||||
unusable placeholder hash; the admin completes setup via the returned link.
|
||||
When `password_hash` is provided the account is created ready-to-use
|
||||
(password_set=1, no token). Otherwise a set-password token is generated and
|
||||
returned so the admin completes setup via a one-time link.
|
||||
|
||||
Returns (username, token). token is None when password_hash is supplied.
|
||||
"""
|
||||
username = username or re.sub(r'[^a-zA-Z0-9_.-]', '', email.split('@')[0]) or 'admin'
|
||||
full_name = full_name or username
|
||||
token = secrets.token_hex(32)
|
||||
placeholder = generate_password_hash(secrets.token_urlsafe(32))
|
||||
now = now_eastern()
|
||||
from datetime import timedelta
|
||||
expires = now + timedelta(hours=expires_hours)
|
||||
|
||||
if password_hash:
|
||||
ph = password_hash
|
||||
token = None
|
||||
password_set = 1
|
||||
expires = None
|
||||
else:
|
||||
token = secrets.token_hex(32)
|
||||
ph = generate_password_hash(secrets.token_urlsafe(32))
|
||||
password_set = 0
|
||||
expires = now + timedelta(hours=expires_hours)
|
||||
|
||||
eng = create_engine(db_uri, future=True)
|
||||
try:
|
||||
@@ -180,9 +192,9 @@ def seed_admin(db_uri, email, username=None, full_name=None, expires_hours=72):
|
||||
set_password_token, set_password_token_expires)
|
||||
VALUES
|
||||
(:u, :fn, :em, :ph, 'admin',
|
||||
:ca, 1, 0, :tok, :exp)
|
||||
"""), {'u': username, 'fn': full_name, 'em': email, 'ph': placeholder,
|
||||
'ca': now, 'tok': token, 'exp': expires})
|
||||
:ca, 1, :ps, :tok, :exp)
|
||||
"""), {'u': username, 'fn': full_name, 'em': email, 'ph': ph,
|
||||
'ca': now, 'ps': password_set, 'tok': token, 'exp': expires})
|
||||
finally:
|
||||
eng.dispose()
|
||||
return username, token
|
||||
@@ -219,7 +231,7 @@ def _add_domains(session, tenant_id, slug, base_domain, custom_domain=None):
|
||||
|
||||
def create_tenant(slug, name, plan_code, admin_email, admin_username=None,
|
||||
admin_full_name=None, custom_domain=None, base_domain=None,
|
||||
db_host=None, user_host='%'):
|
||||
db_host=None, user_host='%', admin_password=None, trial_days=14):
|
||||
if not _SLUG_RE.match(slug or ''):
|
||||
raise ValueError(f"Invalid slug '{slug}' (must be a DNS label).")
|
||||
base = _base_domain(base_domain)
|
||||
@@ -238,6 +250,7 @@ def create_tenant(slug, name, plan_code, admin_email, admin_username=None,
|
||||
|
||||
job_id = None
|
||||
tenant_id = None
|
||||
trial_ends_at = None
|
||||
db_created = False
|
||||
try:
|
||||
with control_session() as s:
|
||||
@@ -249,13 +262,18 @@ def create_tenant(slug, name, plan_code, admin_email, admin_username=None,
|
||||
db_created = True
|
||||
|
||||
with control_session() as s:
|
||||
from datetime import timedelta
|
||||
t = Tenant(slug=slug, name=name, plan_id=plan_id, status='provisioning',
|
||||
db_host=host, db_port=3306, db_name=dbname, db_user=dbuser,
|
||||
created_at=now_eastern())
|
||||
if trial_days:
|
||||
t.subscription_status = 'trial'
|
||||
t.trial_ends_at = now_eastern() + timedelta(days=trial_days)
|
||||
t.set_db_password(password)
|
||||
s.add(t); s.flush()
|
||||
tenant_id = t.id
|
||||
db_uri = t.db_uri
|
||||
trial_ends_at = t.trial_ends_at
|
||||
j = s.get(ProvisioningJob, job_id)
|
||||
if j:
|
||||
j.tenant_id = tenant_id
|
||||
@@ -267,7 +285,9 @@ def create_tenant(slug, name, plan_code, admin_email, admin_username=None,
|
||||
bootstrap_tenant(ref)
|
||||
|
||||
# first admin + setup link
|
||||
username, token = seed_admin(db_uri, admin_email, admin_username, admin_full_name)
|
||||
pw_hash = generate_password_hash(admin_password) if admin_password else None
|
||||
username, token = seed_admin(db_uri, admin_email, admin_username, admin_full_name,
|
||||
password_hash=pw_hash)
|
||||
|
||||
with control_session() as s:
|
||||
primary = _add_domains(s, tenant_id, slug, base, custom_domain)
|
||||
@@ -282,7 +302,8 @@ def create_tenant(slug, name, plan_code, admin_email, admin_username=None,
|
||||
'tenant_id': tenant_id, 'slug': slug, 'db_name': dbname,
|
||||
'db_user': dbuser, 'primary_domain': primary,
|
||||
'custom_domain': custom_domain, 'admin_username': username,
|
||||
'setup_link': setup_link(primary, token),
|
||||
'setup_link': setup_link(primary, token) if token else None,
|
||||
'trial_ends_at': trial_ends_at,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user