Skip to content

Commit

Permalink
Add a "create-user" management command #458
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Druez <tdruez@nexb.com>
  • Loading branch information
tdruez committed Jun 23, 2022
1 parent e391966 commit 0ed467d
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ v31.0.0 (next)
Reference: https://tracker.debian.org/pkg/wait-for-it
https://github.com/nexB/scancode.io/issues/387

- Add a "create-user" management command to create new user with its API key.
https://github.com/nexB/scancode.io/issues/458

- Add a "tag" field on the CodebaseResource model.
The layer details are stored in this field in the "docker" pipeline.
https://github.com/nexB/scancode.io/issues/443
Expand Down
14 changes: 12 additions & 2 deletions docs/scancodeio-settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,20 @@ The authentication system can be enable with this settings::

SCANCODEIO_REQUIRE_AUTHENTICATION=True

Once enabled, all the Web UI views and REST API endpoints will force theuser to login
Once enabled, all the Web UI views and REST API endpoints will force the user to login
to gain access.

See :ref:`rest_api_authentication` for ``API key`` system with the REST API.
Create a superuser with the following command and follow the prompts:
$ ./manage.py createsuperuser

Generate a unique API key for this user using providing the username used in the previous command:
$ ./manage.py drf_create_token USERNAME
Generated token 3bf81af7f100548219e73ec69669ee7acabf8fd8 for user USERNAME

The 3bf81af7f100548219e73ec69669ee7acabf8fd8 valie is your API key.

See :ref:`rest_api_authentication` for details on the``API key`` authentication system
in the REST API.

.. _scancodeio_settings_workspace_location:

Expand Down
4 changes: 2 additions & 2 deletions scanpipe/management/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,9 @@ def handle_input_urls(self, input_urls):
self.stdout.write(msg)

if errors:
self.stdout.write(self.style.ERROR("Could not fetch URL(s):"))
self.stderr.write(self.style.ERROR("Could not fetch URL(s):"))
msg = "\n".join(["- " + url for url in errors])
self.stdout.write(self.style.ERROR(msg))
self.stderr.write(self.style.ERROR(msg))


def validate_input_files(file_locations):
Expand Down
74 changes: 74 additions & 0 deletions scanpipe/management/commands/create-user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# SPDX-License-Identifier: Apache-2.0
#
# http://nexb.com and https://github.com/nexB/scancode.io
# The ScanCode.io software is licensed under the Apache License version 2.0.
# Data generated with ScanCode.io is provided as-is without warranties.
# ScanCode is a trademark of nexB Inc.
#
# You may not use this software except in compliance with the License.
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Data Generated with ScanCode.io is provided on an "AS IS" BASIS, WITHOUT WARRANTIES
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
# ScanCode.io should be considered or used as legal advice. Consult an Attorney
# for any legal advice.
#
# ScanCode.io is a free software code scanning tool from nexB Inc. and others.
# Visit https://github.com/nexB/scancode.io for support and download.

from django.contrib.auth import get_user_model
from django.core import exceptions
from django.core.management.base import BaseCommand
from django.core.management.base import CommandError

from rest_framework.authtoken.models import Token


class Command(BaseCommand):
help = "Create a user and generate an API key for authentication."
requires_migrations_checks = True

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.UserModel = get_user_model()
self.username_field = self.UserModel._meta.get_field(
self.UserModel.USERNAME_FIELD
)

def add_arguments(self, parser):
parser.add_argument("username", help="Specifies the username for the user.")

def handle(self, *args, **options):
username = options["username"]

error_msg = self._validate_username(username)
if error_msg:
raise CommandError(error_msg)

user = self.UserModel._default_manager.create_user(username=username)
token, _ = Token._default_manager.get_or_create(user=user)

if options["verbosity"] >= 1:
msg = f"User {username} created with API key: {token.key}"
self.stdout.write(self.style.SUCCESS(msg))

def _validate_username(self, username):
"""
Validate username. If invalid, return a string error message.
"""
if self.username_field.unique:
try:
self.UserModel._default_manager.get_by_natural_key(username)
except self.UserModel.DoesNotExist:
pass
else:
return "Error: That username is already taken."

try:
self.username_field.clean(username, None)
except exceptions.ValidationError as e:
return "; ".join(e.messages)
1 change: 0 additions & 1 deletion scanpipe/management/commands/list-project.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

from scanpipe.filters import ProjectFilterSet
from scanpipe.management.commands import RunStatusCommandMixin
from scanpipe.models import Project


class Command(BaseCommand, RunStatusCommandMixin):
Expand Down
26 changes: 26 additions & 0 deletions scanpipe/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from unittest import mock

from django.apps import apps
from django.contrib.auth import get_user_model
from django.core.management import CommandError
from django.core.management import call_command
from django.test import TestCase
Expand Down Expand Up @@ -506,3 +507,28 @@ def test_scanpipe_management_command_reset_project(self):
self.assertEqual(0, project.discoveredpackages.count())
self.assertEqual(1, len(Project.get_root_content(project.input_path)))
self.assertEqual(0, len(Project.get_root_content(project.codebase_path)))

def test_scanpipe_management_command_create_user(self):
out = StringIO()

expected = "Error: the following arguments are required: username"
with self.assertRaisesMessage(CommandError, expected):
call_command("create-user")

username = "my_username"
call_command("create-user", username, stdout=out)
self.assertIn(f"User {username} created with API key:", out.getvalue())
user = get_user_model().objects.get(username=username)
self.assertTrue(user.auth_token)

expected = "Error: That username is already taken."
with self.assertRaisesMessage(CommandError, expected):
call_command("create-user", username)

username = "^&*"
expected = (
"Enter a valid username. This value may contain only letters, numbers, "
"and @/./+/-/_ characters."
)
with self.assertRaisesMessage(CommandError, expected):
call_command("create-user", username)

0 comments on commit 0ed467d

Please # to comment.