mirror of
https://github.com/sissbruecker/linkding
synced 2024-11-10 06:04:15 +00:00
Add option to create initial superuser (#323)
* Add option to create initial superuser * Update .env.sample
This commit is contained in:
parent
5841ba0f4c
commit
f88cc30b48
5 changed files with 112 additions and 0 deletions
12
.env.sample
12
.env.sample
|
@ -8,7 +8,19 @@ LD_HOST_DATA_DIR=./data
|
||||||
# Must end with a slash `/`
|
# Must end with a slash `/`
|
||||||
LD_CONTEXT_PATH=
|
LD_CONTEXT_PATH=
|
||||||
|
|
||||||
|
# Username of the initial superuser to create, leave empty to not create one
|
||||||
|
LD_SUPERUSER_NAME=
|
||||||
|
# Password for the initial superuser, leave empty to disable credentials authentication and rely on proxy authentication instead
|
||||||
|
LD_SUPERUSER_PASSWORD=
|
||||||
# Option to disable background tasks
|
# Option to disable background tasks
|
||||||
LD_DISABLE_BACKGROUND_TASKS=False
|
LD_DISABLE_BACKGROUND_TASKS=False
|
||||||
# Option to disable URL validation for bookmarks completely
|
# Option to disable URL validation for bookmarks completely
|
||||||
LD_DISABLE_URL_VALIDATION=False
|
LD_DISABLE_URL_VALIDATION=False
|
||||||
|
# Enables support for authentication proxies such as Authelia
|
||||||
|
LD_ENABLE_AUTH_PROXY=False
|
||||||
|
# Name of the request header that the auth proxy passes to the application to identify the user
|
||||||
|
# See docs/Options.md for more details
|
||||||
|
LD_AUTH_PROXY_USERNAME_HEADER=
|
||||||
|
# The URL that linkding should redirect to after a logout, when using an auth proxy
|
||||||
|
# See docs/Options.md for more details
|
||||||
|
LD_AUTH_PROXY_LOGOUT_URL=
|
||||||
|
|
37
bookmarks/management/commands/create_initial_superuser.py
Normal file
37
bookmarks/management/commands/create_initial_superuser.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Creates an initial superuser for a deployment using env variables"
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
User = get_user_model()
|
||||||
|
superuser_name = os.getenv('LD_SUPERUSER_NAME', None)
|
||||||
|
superuser_password = os.getenv('LD_SUPERUSER_PASSWORD', None)
|
||||||
|
|
||||||
|
# Skip if option is undefined
|
||||||
|
if not superuser_name:
|
||||||
|
logger.info('Skip creating initial superuser, LD_SUPERUSER_NAME option is not defined')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Skip if user already exists
|
||||||
|
user_exists = User.objects.filter(username=superuser_name).exists()
|
||||||
|
if user_exists:
|
||||||
|
logger.info('Skip creating initial superuser, user already exists')
|
||||||
|
return
|
||||||
|
|
||||||
|
user = User(username=superuser_name, is_superuser=True, is_staff=True)
|
||||||
|
|
||||||
|
if superuser_password:
|
||||||
|
user.set_password(superuser_password)
|
||||||
|
else:
|
||||||
|
user.set_unusable_password()
|
||||||
|
|
||||||
|
user.save()
|
||||||
|
logger.info('Created initial superuser')
|
45
bookmarks/tests/test_create_initial_superuser_command.py
Normal file
45
bookmarks/tests/test_create_initial_superuser_command.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import os
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from bookmarks.models import User
|
||||||
|
from bookmarks.management.commands.create_initial_superuser import Command
|
||||||
|
|
||||||
|
|
||||||
|
class TestCreateInitialSuperuserCommand(TestCase):
|
||||||
|
|
||||||
|
@mock.patch.dict(os.environ, {'LD_SUPERUSER_NAME': 'john', 'LD_SUPERUSER_PASSWORD': 'password123'})
|
||||||
|
def test_create_with_password(self):
|
||||||
|
Command().handle()
|
||||||
|
|
||||||
|
self.assertEqual(1, User.objects.count())
|
||||||
|
|
||||||
|
user = User.objects.first()
|
||||||
|
self.assertEqual('john', user.username)
|
||||||
|
self.assertTrue(user.has_usable_password())
|
||||||
|
self.assertTrue(user.check_password('password123'))
|
||||||
|
|
||||||
|
@mock.patch.dict(os.environ, {'LD_SUPERUSER_NAME': 'john'})
|
||||||
|
def test_create_without_password(self):
|
||||||
|
Command().handle()
|
||||||
|
|
||||||
|
self.assertEqual(1, User.objects.count())
|
||||||
|
|
||||||
|
user = User.objects.first()
|
||||||
|
self.assertEqual('john', user.username)
|
||||||
|
self.assertFalse(user.has_usable_password())
|
||||||
|
|
||||||
|
def test_create_without_options(self):
|
||||||
|
Command().handle()
|
||||||
|
|
||||||
|
self.assertEqual(0, User.objects.count())
|
||||||
|
|
||||||
|
@mock.patch.dict(os.environ, {'LD_SUPERUSER_NAME': 'john', 'LD_SUPERUSER_PASSWORD': 'password123'})
|
||||||
|
def test_create_multiple_times(self):
|
||||||
|
Command().handle()
|
||||||
|
Command().handle()
|
||||||
|
Command().handle()
|
||||||
|
|
||||||
|
self.assertEqual(1, User.objects.count())
|
||||||
|
|
|
@ -10,6 +10,8 @@ mkdir -p data
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
# Generate secret key file if it does not exist
|
# Generate secret key file if it does not exist
|
||||||
python manage.py generate_secret_key
|
python manage.py generate_secret_key
|
||||||
|
# Create initial superuser if defined in options / environment variables
|
||||||
|
python manage.py create_initial_superuser
|
||||||
|
|
||||||
# Ensure the DB folder is owned by the right user
|
# Ensure the DB folder is owned by the right user
|
||||||
chown -R www-data: /etc/linkding/data
|
chown -R www-data: /etc/linkding/data
|
||||||
|
|
|
@ -25,6 +25,22 @@ All options need to be defined as environment variables in the environment that
|
||||||
|
|
||||||
## List of options
|
## List of options
|
||||||
|
|
||||||
|
### `LD_SUPERUSER_NAME`
|
||||||
|
|
||||||
|
Values: `String` | Default = None
|
||||||
|
|
||||||
|
When set, creates an initial superuser with the specified username when starting the container.
|
||||||
|
Does nothing if the user already exists.
|
||||||
|
|
||||||
|
See [`LD_SUPERUSER_PASSWORD`](#ld_superuser_password) on how to configure the respective password.
|
||||||
|
|
||||||
|
### `LD_SUPERUSER_PASSWORD`
|
||||||
|
|
||||||
|
Values: `String` | Default = None
|
||||||
|
|
||||||
|
The password for the initial superuser.
|
||||||
|
When left undefined, the superuser will be created without a usable password, which means the user can not authenticate using credentials / through the login form, and can only be authenticated using proxy authentication (see [`LD_ENABLE_AUTH_PROXY`](#ld_enable_auth_proxy)).
|
||||||
|
|
||||||
### `LD_DISABLE_BACKGROUND_TASKS`
|
### `LD_DISABLE_BACKGROUND_TASKS`
|
||||||
|
|
||||||
Values: `True`, `False` | Default = `False`
|
Values: `True`, `False` | Default = `False`
|
||||||
|
|
Loading…
Reference in a new issue