mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Merge pull request #890 from what-name/feature/superuser-manager
Create initial Paperless user automatically
This commit is contained in:
		@@ -28,12 +28,12 @@ initialize() {
 | 
			
		||||
 | 
			
		||||
	echo "creating directory /tmp/paperless"
 | 
			
		||||
	mkdir -p /tmp/paperless
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    set +e
 | 
			
		||||
	chown -R paperless:paperless ../
 | 
			
		||||
	chown -R paperless:paperless /tmp/paperless
 | 
			
		||||
    set -e
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    sudo -HEu paperless /sbin/docker-prepare.sh
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,16 +9,13 @@ wait_for_postgres() {
 | 
			
		||||
	host="${PAPERLESS_DBHOST}"
 | 
			
		||||
	port="${PAPERLESS_DBPORT}"
 | 
			
		||||
 | 
			
		||||
	if [[ -z $port ]] ;
 | 
			
		||||
	then
 | 
			
		||||
	if [[ -z $port ]]; then
 | 
			
		||||
		port="5432"
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	while !</dev/tcp/$host/$port ;
 | 
			
		||||
	do
 | 
			
		||||
	while ! </dev/tcp/$host/$port; do
 | 
			
		||||
 | 
			
		||||
		if [ $attempt_num -eq $max_attempts ]
 | 
			
		||||
		then
 | 
			
		||||
		if [ $attempt_num -eq $max_attempts ]; then
 | 
			
		||||
			echo "Unable to connect to database."
 | 
			
		||||
			exit 1
 | 
			
		||||
		else
 | 
			
		||||
@@ -31,13 +28,7 @@ wait_for_postgres() {
 | 
			
		||||
	done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
migrations() {
 | 
			
		||||
	if [[ -n "${PAPERLESS_DBHOST}" ]]
 | 
			
		||||
	then
 | 
			
		||||
		wait_for_postgres
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	(
 | 
			
		||||
		# flock is in place to prevent multiple containers from doing migrations
 | 
			
		||||
		# simultaneously. This also ensures that the db is ready when the command
 | 
			
		||||
@@ -45,24 +36,37 @@ migrations() {
 | 
			
		||||
		flock 200
 | 
			
		||||
		echo "Apply database migrations..."
 | 
			
		||||
		python3 manage.py migrate
 | 
			
		||||
	)  200>/usr/src/paperless/data/migration_lock
 | 
			
		||||
	) 200>/usr/src/paperless/data/migration_lock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
search_index() {
 | 
			
		||||
	index_version=1
 | 
			
		||||
	index_version_file=/usr/src/paperless/data/.index_version
 | 
			
		||||
 | 
			
		||||
	if [[ (! -f "$index_version_file") || $(< $index_version_file) != "$index_version" ]]; then
 | 
			
		||||
	if [[ (! -f "$index_version_file") || $(<$index_version_file) != "$index_version" ]]; then
 | 
			
		||||
		echo "Search index out of date. Updating..."
 | 
			
		||||
		python3 manage.py document_index reindex
 | 
			
		||||
		echo $index_version | tee $index_version_file >/dev/null
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_work() {
 | 
			
		||||
	migrations;
 | 
			
		||||
	
 | 
			
		||||
	search_index;
 | 
			
		||||
superuser() {
 | 
			
		||||
	if [[ -n "${PAPERLESS_ADMIN_USER}" ]]; then
 | 
			
		||||
		sudo -HEu paperless python3 manage.py manage_superuser
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_work;
 | 
			
		||||
do_work() {
 | 
			
		||||
	if [[ -n "${PAPERLESS_DBHOST}" ]]; then
 | 
			
		||||
		wait_for_postgres
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	migrations
 | 
			
		||||
 | 
			
		||||
	search_index
 | 
			
		||||
 | 
			
		||||
	superuser
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_work
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
for command in document_archiver document_exporter document_importer mail_fetcher document_create_classifier document_index document_renamer document_retagger document_thumbnails document_sanity_checker;
 | 
			
		||||
for command in document_archiver document_exporter document_importer mail_fetcher document_create_classifier document_index document_renamer document_retagger document_thumbnails document_sanity_checker manage_superuser;
 | 
			
		||||
do
 | 
			
		||||
	echo "installing $command..."
 | 
			
		||||
	sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command
 | 
			
		||||
 
 | 
			
		||||
@@ -177,6 +177,30 @@ PAPERLESS_AUTO_LOGIN_USERNAME=<username>
 | 
			
		||||
 | 
			
		||||
    Defaults to none, which disables this feature.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ADMIN_USER=<username>
 | 
			
		||||
    If this environment variable is specified, Paperless automatically creates 
 | 
			
		||||
    a superuser with the provided username at start. This is useful in cases 
 | 
			
		||||
    where you can not run the `createsuperuser` command seperately, such as Kubernetes 
 | 
			
		||||
    or AWS ECS.
 | 
			
		||||
    
 | 
			
		||||
    Requires `PAPERLESS_ADMIN_PASSWORD` to be set.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        This will not change an existing [super]user's password, nor will
 | 
			
		||||
        it recreate a user that already exists. You can leave this throughout
 | 
			
		||||
        the lifecycle of the containers.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ADMIN_MAIL=<email>
 | 
			
		||||
    (Optional) Specify superuser email address. Only used when 
 | 
			
		||||
    `PAPERLESS_ADMIN_USER` is set.
 | 
			
		||||
 | 
			
		||||
    Defaults to ``root@localhost``.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ADMIN_PASSWORD=<password>
 | 
			
		||||
    Only used when `PAPERLESS_ADMIN_USER` is set.
 | 
			
		||||
    This will be the password of the automatically created superuser.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_COOKIE_PREFIX=<str>
 | 
			
		||||
    Specify a prefix that is added to the cookies used by paperless to identify
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								src/documents/management/commands/manage_superuser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/documents/management/commands/manage_superuser.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.core.management.base import BaseCommand, CommandError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger("paperless.management.superuser")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Command(BaseCommand):
 | 
			
		||||
 | 
			
		||||
    help = """
 | 
			
		||||
        Creates a Django superuser based on env variables.
 | 
			
		||||
    """.replace("    ", "")
 | 
			
		||||
 | 
			
		||||
    def handle(self, *args, **options):
 | 
			
		||||
 | 
			
		||||
        username = os.getenv('PAPERLESS_ADMIN_USER')
 | 
			
		||||
        if not username:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        mail = os.getenv('PAPERLESS_ADMIN_MAIL', 'root@localhost')
 | 
			
		||||
        password = os.getenv('PAPERLESS_ADMIN_PASSWORD')
 | 
			
		||||
 | 
			
		||||
        # Check if user exists already, leave as is if it does
 | 
			
		||||
        if User.objects.filter(username=username).exists():
 | 
			
		||||
            user: User = User.objects.get_by_natural_key(username)
 | 
			
		||||
            user.set_password(password)
 | 
			
		||||
            user.save()
 | 
			
		||||
            self.stdout.write(f"Changed password of user {username}.")
 | 
			
		||||
        elif password:
 | 
			
		||||
            # Create superuser based on env variables
 | 
			
		||||
            User.objects.create_superuser(username, mail, password)
 | 
			
		||||
            self.stdout.write(
 | 
			
		||||
                f'Created superuser "{username}" with provided password.')
 | 
			
		||||
        else:
 | 
			
		||||
            self.stdout.write(
 | 
			
		||||
                f'Did not create superuser "{username}".')
 | 
			
		||||
            self.stdout.write(
 | 
			
		||||
                'Make sure you specified "PAPERLESS_ADMIN_PASSWORD" in your '
 | 
			
		||||
                '"docker-compose.env" file.')
 | 
			
		||||
							
								
								
									
										66
									
								
								src/documents/tests/test_management_superuser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/documents/tests/test_management_superuser.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
from unittest import mock
 | 
			
		||||
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.core.management import call_command
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
from documents.management.commands.document_thumbnails import _process_document
 | 
			
		||||
from documents.models import Document, Tag, Correspondent, DocumentType
 | 
			
		||||
from documents.tests.utils import DirectoriesMixin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestManageSuperUser(DirectoriesMixin, TestCase):
 | 
			
		||||
 | 
			
		||||
    def reset_environment(self):
 | 
			
		||||
        if "PAPERLESS_ADMIN_USER" in os.environ:
 | 
			
		||||
            del os.environ["PAPERLESS_ADMIN_USER"]
 | 
			
		||||
        if "PAPERLESS_ADMIN_PASSWORD" in os.environ:
 | 
			
		||||
            del os.environ["PAPERLESS_ADMIN_PASSWORD"]
 | 
			
		||||
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        super().setUp()
 | 
			
		||||
        self.reset_environment()
 | 
			
		||||
 | 
			
		||||
    def tearDown(self) -> None:
 | 
			
		||||
        super().tearDown()
 | 
			
		||||
        self.reset_environment()
 | 
			
		||||
 | 
			
		||||
    def test_no_user(self):
 | 
			
		||||
        call_command("manage_superuser")
 | 
			
		||||
 | 
			
		||||
        # just the consumer user.
 | 
			
		||||
        self.assertEqual(User.objects.count(), 1)
 | 
			
		||||
        self.assertTrue(User.objects.filter(username="consumer").exists())
 | 
			
		||||
 | 
			
		||||
    def test_create(self):
 | 
			
		||||
        os.environ["PAPERLESS_ADMIN_USER"] = "new_user"
 | 
			
		||||
        os.environ["PAPERLESS_ADMIN_PASSWORD"] = "123456"
 | 
			
		||||
 | 
			
		||||
        call_command("manage_superuser")
 | 
			
		||||
 | 
			
		||||
        user: User = User.objects.get_by_natural_key("new_user")
 | 
			
		||||
        self.assertTrue(user.check_password("123456"))
 | 
			
		||||
 | 
			
		||||
    def test_update(self):
 | 
			
		||||
        os.environ["PAPERLESS_ADMIN_USER"] = "new_user"
 | 
			
		||||
        os.environ["PAPERLESS_ADMIN_PASSWORD"] = "123456"
 | 
			
		||||
 | 
			
		||||
        call_command("manage_superuser")
 | 
			
		||||
 | 
			
		||||
        os.environ["PAPERLESS_ADMIN_USER"] = "new_user"
 | 
			
		||||
        os.environ["PAPERLESS_ADMIN_PASSWORD"] = "more_secure_pwd_7645"
 | 
			
		||||
 | 
			
		||||
        call_command("manage_superuser")
 | 
			
		||||
 | 
			
		||||
        user: User = User.objects.get_by_natural_key("new_user")
 | 
			
		||||
        self.assertTrue(user.check_password("more_secure_pwd_7645"))
 | 
			
		||||
 | 
			
		||||
    def test_no_password(self):
 | 
			
		||||
        os.environ["PAPERLESS_ADMIN_USER"] = "new_user"
 | 
			
		||||
 | 
			
		||||
        call_command("manage_superuser")
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(User.DoesNotExist):
 | 
			
		||||
            User.objects.get_by_natural_key("new_user")
 | 
			
		||||
		Reference in New Issue
	
	Block a user