Config superuser (#59)
* backend: automatically create super user, fixes #57 - if SUPERUSER_EMAIL is set, superuser is created with `is_superuser` and `is_verified` settings, if user doesn't already exist. - if SUPERUSER_PASSWORD if set, the password for superuser is set, otherwise a random password is generated update sample SUPERUSER_EMAIL and SUPERUSER_PASSWORD in config file and chart. - ensure verification email is not sent if user already verified
This commit is contained in:
parent
eaf8055063
commit
53beb84c01
@ -34,13 +34,13 @@ class EmailSender:
|
|||||||
|
|
||||||
def get_origin(self, headers):
|
def get_origin(self, headers):
|
||||||
""" Return origin of the received request"""
|
""" Return origin of the received request"""
|
||||||
scheme = headers.get("X-Forwarded-Proto")
|
if not headers:
|
||||||
if not scheme:
|
return self.default_origin
|
||||||
scheme = "http"
|
|
||||||
|
|
||||||
|
scheme = headers.get("X-Forwarded-Proto")
|
||||||
host = headers.get("Host")
|
host = headers.get("Host")
|
||||||
if not host:
|
if not scheme or not host:
|
||||||
host = self.default_origin
|
return self.default_origin
|
||||||
|
|
||||||
return scheme + "://" + host
|
return scheme + "://" + host
|
||||||
|
|
||||||
|
@ -9,10 +9,12 @@ import asyncio
|
|||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from pydantic import EmailStr, UUID4
|
from pydantic import EmailStr, UUID4
|
||||||
|
import passlib.pwd
|
||||||
|
|
||||||
from fastapi import Request, Response, HTTPException, Depends
|
from fastapi import Request, Response, HTTPException, Depends
|
||||||
|
|
||||||
from fastapi_users import FastAPIUsers, models, BaseUserManager
|
from fastapi_users import FastAPIUsers, models, BaseUserManager
|
||||||
|
from fastapi_users.manager import UserAlreadyExists
|
||||||
from fastapi_users.authentication import JWTAuthentication
|
from fastapi_users.authentication import JWTAuthentication
|
||||||
from fastapi_users.db import MongoDBUserDatabase
|
from fastapi_users.db import MongoDBUserDatabase
|
||||||
|
|
||||||
@ -35,10 +37,10 @@ class User(models.BaseUser):
|
|||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# use custom model as model.BaseeserCreate includes is_* fields which should not be set
|
# use custom model as model.BaseUserCreate includes is_* field
|
||||||
class UserCreate(models.CreateUpdateDictModel):
|
class UserCreateIn(models.CreateUpdateDictModel):
|
||||||
"""
|
"""
|
||||||
User Creation Model
|
User Creation Model exposed to API
|
||||||
"""
|
"""
|
||||||
|
|
||||||
email: EmailStr
|
email: EmailStr
|
||||||
@ -52,6 +54,20 @@ class UserCreate(models.CreateUpdateDictModel):
|
|||||||
newArchiveName: Optional[str] = ""
|
newArchiveName: Optional[str] = ""
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
class UserCreate(models.BaseUserCreate):
|
||||||
|
"""
|
||||||
|
User Creation Model
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: Optional[str] = ""
|
||||||
|
|
||||||
|
inviteToken: Optional[str]
|
||||||
|
|
||||||
|
newArchive: bool
|
||||||
|
newArchiveName: Optional[str] = ""
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
class UserUpdate(User, models.CreateUpdateDictModel):
|
class UserUpdate(User, models.CreateUpdateDictModel):
|
||||||
"""
|
"""
|
||||||
@ -124,6 +140,27 @@ class UserManager(BaseUserManager[UserCreate, UserDB]):
|
|||||||
)
|
)
|
||||||
return await cursor.to_list(length=1000)
|
return await cursor.to_list(length=1000)
|
||||||
|
|
||||||
|
async def create_super_user(self):
|
||||||
|
""" Initialize a super user from env vars """
|
||||||
|
email = os.environ.get("SUPERUSER_EMAIL")
|
||||||
|
password = os.environ.get("SUPERUSER_PASSWORD")
|
||||||
|
if not email:
|
||||||
|
print("No superuser defined", flush=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not password:
|
||||||
|
password = passlib.pwd.genword()
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = await self.create(
|
||||||
|
UserCreate(email=email, password=password, is_superuser=True, newArchive=False, is_verified=True)
|
||||||
|
)
|
||||||
|
print(f"Super user {email} created", flush=True)
|
||||||
|
print(res, flush=True)
|
||||||
|
|
||||||
|
except UserAlreadyExists:
|
||||||
|
print(f"User {email} already exists", flush=True)
|
||||||
|
|
||||||
async def on_after_register_custom(
|
async def on_after_register_custom(
|
||||||
self, user: UserDB, user_create: UserCreate, request: Optional[Request]
|
self, user: UserDB, user_create: UserCreate, request: Optional[Request]
|
||||||
):
|
):
|
||||||
@ -144,6 +181,8 @@ class UserManager(BaseUserManager[UserCreate, UserDB]):
|
|||||||
user=user,
|
user=user,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is_verified = hasattr(user_create, "is_verified") and user_create.is_verified
|
||||||
|
|
||||||
if user_create.inviteToken:
|
if user_create.inviteToken:
|
||||||
try:
|
try:
|
||||||
await self.archive_ops.handle_new_user_invite(
|
await self.archive_ops.handle_new_user_invite(
|
||||||
@ -152,10 +191,11 @@ class UserManager(BaseUserManager[UserCreate, UserDB]):
|
|||||||
except HTTPException as exc:
|
except HTTPException as exc:
|
||||||
print(exc)
|
print(exc)
|
||||||
|
|
||||||
|
if not is_verified:
|
||||||
# if user has been invited, mark as verified immediately
|
# if user has been invited, mark as verified immediately
|
||||||
await self._update(user, {"is_verified": True})
|
await self._update(user, {"is_verified": True})
|
||||||
|
|
||||||
else:
|
elif not is_verified:
|
||||||
asyncio.create_task(self.request_verify(user, request))
|
asyncio.create_task(self.request_verify(user, request))
|
||||||
|
|
||||||
async def on_after_forgot_password(
|
async def on_after_forgot_password(
|
||||||
@ -202,7 +242,7 @@ def init_users_api(app, user_manager):
|
|||||||
lambda: user_manager,
|
lambda: user_manager,
|
||||||
[jwt_authentication],
|
[jwt_authentication],
|
||||||
User,
|
User,
|
||||||
UserCreate,
|
UserCreateIn,
|
||||||
UserUpdate,
|
UserUpdate,
|
||||||
UserDB,
|
UserDB,
|
||||||
)
|
)
|
||||||
@ -261,4 +301,6 @@ def init_users_api(app, user_manager):
|
|||||||
|
|
||||||
app.include_router(users_router, prefix="/users", tags=["users"])
|
app.include_router(users_router, prefix="/users", tags=["users"])
|
||||||
|
|
||||||
|
asyncio.create_task(user_manager.create_super_user())
|
||||||
|
|
||||||
return fastapi_users
|
return fastapi_users
|
||||||
|
@ -23,6 +23,9 @@ stringData:
|
|||||||
EMAIL_SENDER: "{{ .Values.email.sender_email }}"
|
EMAIL_SENDER: "{{ .Values.email.sender_email }}"
|
||||||
EMAIL_PASSWORD: "{{ .Values.email.password }}"
|
EMAIL_PASSWORD: "{{ .Values.email.password }}"
|
||||||
|
|
||||||
|
SUPERUSER_EMAIL: "{{ .Values.superuser.email }}"
|
||||||
|
SUPERUSER_PASSWORD: "{{ .Values.superuser.password }}"
|
||||||
|
|
||||||
{{- range $storage := .Values.storages }}
|
{{- range $storage := .Values.storages }}
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
|
@ -5,6 +5,13 @@ name: browsertrix-cloud
|
|||||||
registration_enabled: 1
|
registration_enabled: 1
|
||||||
jwt_token_lifetime_minutes: 60
|
jwt_token_lifetime_minutes: 60
|
||||||
|
|
||||||
|
superuser:
|
||||||
|
# set this to enable a superuser admin
|
||||||
|
email: admin@example.com
|
||||||
|
|
||||||
|
# optional: if not set, automatically generated
|
||||||
|
password:
|
||||||
|
|
||||||
|
|
||||||
# API Image
|
# API Image
|
||||||
# =========================================
|
# =========================================
|
||||||
|
@ -9,6 +9,11 @@ MONGO_INITDB_ROOT_PASSWORD=example
|
|||||||
MINIO_ROOT_USER=ADMIN
|
MINIO_ROOT_USER=ADMIN
|
||||||
MINIO_ROOT_PASSWORD=PASSW0RD
|
MINIO_ROOT_PASSWORD=PASSW0RD
|
||||||
|
|
||||||
|
SUPERUSER_EMAIL=admin@example.com
|
||||||
|
|
||||||
|
# if blank, a password is generated automatically
|
||||||
|
SUPERUSER_PASSWORD=
|
||||||
|
|
||||||
STORE_ENDPOINT_URL=http://minio:9000/test-bucket/
|
STORE_ENDPOINT_URL=http://minio:9000/test-bucket/
|
||||||
STORE_ACCESS_ENDPOINT_URL=http://localhost:9000/test-bucket/
|
STORE_ACCESS_ENDPOINT_URL=http://localhost:9000/test-bucket/
|
||||||
STORE_ACCESS_KEY=ADMIN
|
STORE_ACCESS_KEY=ADMIN
|
||||||
|
Loading…
Reference in New Issue
Block a user