Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sso init #2001

Merged
merged 50 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
eda5ea7
sso init
sadnub Sep 16, 2024
c8dd805
fix session auth and restrict it only to access_token view
sadnub Sep 18, 2024
4ba27ec
add auditing and session key checking to the sso auth token view
sadnub Sep 18, 2024
9c15f4b
implemented user session tracking, social account tracking, and block…
sadnub Sep 28, 2024
0deb78a
fix settings
wh1te909 Sep 30, 2024
6c44191
blacked
wh1te909 Sep 30, 2024
d894f92
format
wh1te909 Oct 6, 2024
1c1d3bd
frontend needs to come first
wh1te909 Oct 6, 2024
46f0b23
rename to avoid conflict with django settings
wh1te909 Oct 6, 2024
f326096
isort
wh1te909 Oct 6, 2024
9edb848
implement default role for sso signups and log ip for sso logins
sadnub Oct 8, 2024
ce11685
secure sso token a little more and allow for disabling sso feature.
sadnub Oct 18, 2024
ebefcb7
block local should be disabled by default
wh1te909 Oct 18, 2024
3bfa35e
move settings before local import
wh1te909 Oct 18, 2024
899111a
remove unused imports
wh1te909 Oct 18, 2024
61790d2
blacked
wh1te909 Oct 18, 2024
bacf415
fix some 500 errors
sadnub Oct 18, 2024
66c7123
allow displaying full name in UI if present
sadnub Oct 21, 2024
5520a84
fix client ip not showing in audit log for sso logon and disable some…
sadnub Oct 22, 2024
4fd772e
update reqs
wh1te909 Oct 24, 2024
c28d800
blacked
wh1te909 Oct 24, 2024
faa0e6c
handle orphaned sso providers
wh1te909 Oct 25, 2024
0bd09d0
fix tests
wh1te909 Oct 25, 2024
2c09ad6
update headers
wh1te909 Oct 25, 2024
0383043
move sso settings
wh1te909 Oct 25, 2024
0d021a8
use exists
wh1te909 Oct 25, 2024
0f86bbf
disable password/mfa reset views if block_local_logon is enabled
sadnub Oct 29, 2024
18b1afe
formatting
sadnub Oct 29, 2024
8d543dc
move inside if block
wh1te909 Oct 29, 2024
2cbecaa
don't show providers list on login screen if sso is disabled globally
wh1te909 Oct 30, 2024
41e3d1f
move check to signup
wh1te909 Oct 30, 2024
a6166a1
add random otp to social accounts
wh1te909 Oct 31, 2024
ec0a2dc
handle deployment config updates
wh1te909 Oct 31, 2024
cc1f640
set icon based on provider
wh1te909 Nov 1, 2024
3851b09
modify settings instead of local_settings
wh1te909 Nov 3, 2024
5bec476
forgot frontend
wh1te909 Nov 3, 2024
9624af4
fix tests
wh1te909 Nov 3, 2024
f8314e0
fix pop
wh1te909 Nov 4, 2024
4a5bfee
fix failsafe to ensure no lockouts and add self-reset sso perms
wh1te909 Nov 4, 2024
46c5128
move callback url info to the backend
wh1te909 Nov 4, 2024
fb47022
redo migrations
wh1te909 Nov 4, 2024
c35da67
update reqs
wh1te909 Nov 5, 2024
0d34831
also check if first name only and display
wh1te909 Nov 6, 2024
86816ce
move name stuff to the correct view and add email fallback
wh1te909 Nov 10, 2024
6394734
remove deprecated login endpoints
wh1te909 Nov 15, 2024
150e319
refurb
wh1te909 Nov 15, 2024
ecf5646
update reqs
wh1te909 Nov 15, 2024
7f9fc48
revert as these haven't changed [skip ci]
wh1te909 Nov 15, 2024
d1df406
call sync mesh after sso user created
wh1te909 Nov 16, 2024
91c33b0
add setting override to disable sso
wh1te909 Nov 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions .devcontainer/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ function check_tactical_ready {
}

function django_setup {
until (echo > /dev/tcp/"${POSTGRES_HOST}"/"${POSTGRES_PORT}") &> /dev/null; do
until (echo >/dev/tcp/"${POSTGRES_HOST}"/"${POSTGRES_PORT}") &>/dev/null; do
echo "waiting for postgresql container to be ready..."
sleep 5
done

until (echo > /dev/tcp/"${MESH_SERVICE}"/4443) &> /dev/null; do
until (echo >/dev/tcp/"${MESH_SERVICE}"/4443) &>/dev/null; do
echo "waiting for meshcentral container to be ready..."
sleep 5
done
Expand All @@ -49,8 +49,11 @@ function django_setup {
MESH_TOKEN="$(cat ${TACTICAL_DIR}/tmp/mesh_token)"

DJANGO_SEKRET=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 80 | head -n 1)

localvars="$(cat << EOF

BASE_DOMAIN=$(echo "import tldextract; no_fetch_extract = tldextract.TLDExtract(suffix_list_urls=()); extracted = no_fetch_extract('${API_HOST}'); print(f'{extracted.domain}.{extracted.suffix}')" | python)

localvars="$(
cat <<EOF
SECRET_KEY = '${DJANGO_SEKRET}'

DEBUG = True
Expand All @@ -64,12 +67,17 @@ KEY_FILE = '${CERT_PRIV_PATH}'

SCRIPTS_DIR = '/community-scripts'

ALLOWED_HOSTS = ['${API_HOST}', '*']

ADMIN_URL = 'admin/'

CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = ['https://${API_HOST}']
ALLOWED_HOSTS = ['${API_HOST}', '${APP_HOST}', '*']

CORS_ORIGIN_WHITELIST = ['https://${APP_HOST}']

SESSION_COOKIE_DOMAIN = '${BASE_DOMAIN}'
CSRF_COOKIE_DOMAIN = '${BASE_DOMAIN}'
CSRF_TRUSTED_ORIGINS = ['https://${API_HOST}', 'https://${APP_HOST}']

HEADLESS_FRONTEND_URLS = {'socialaccount_login_error': 'https://${APP_HOST}/account/provider/callback'}

DATABASES = {
'default': {
Expand Down Expand Up @@ -101,9 +109,9 @@ MESH_WS_URL = '${MESH_WS_URL}'
ADMIN_ENABLED = True
TRMM_INSECURE = True
EOF
)"
)"

echo "${localvars}" > ${WORKSPACE_DIR}/api/tacticalrmm/tacticalrmm/local_settings.py
echo "${localvars}" >${WORKSPACE_DIR}/api/tacticalrmm/tacticalrmm/local_settings.py

# run migrations and init scripts
"${VIRTUAL_ENV}"/bin/python manage.py pre_update_tasks
Expand All @@ -118,9 +126,8 @@ EOF
"${VIRTUAL_ENV}"/bin/python manage.py create_natsapi_conf
"${VIRTUAL_ENV}"/bin/python manage.py create_installer_user
"${VIRTUAL_ENV}"/bin/python manage.py post_update_tasks


# create super user
# create super user
echo "from accounts.models import User; User.objects.create_superuser('${TRMM_USER}', 'admin@example.com', '${TRMM_PASS}') if not User.objects.filter(username='${TRMM_USER}').exists() else 0;" | python manage.py shell
}

Expand Down
5 changes: 3 additions & 2 deletions api/tacticalrmm/accounts/management/commands/reset_2fa.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import subprocess

import pyotp
from django.conf import settings
from django.core.management.base import BaseCommand

from accounts.models import User
from tacticalrmm.helpers import get_webdomain
from tacticalrmm.util_settings import get_webdomain


class Command(BaseCommand):
Expand All @@ -26,7 +27,7 @@ def handle(self, *args, **kwargs):
user.save(update_fields=["totp_key"])

url = pyotp.totp.TOTP(code).provisioning_uri(
username, issuer_name=get_webdomain()
username, issuer_name=get_webdomain(settings.CORS_ORIGIN_WHITELIST[0])
)
subprocess.run(f'qr "{url}"', shell=True)
self.stdout.write(
Expand Down
5 changes: 5 additions & 0 deletions api/tacticalrmm/accounts/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Optional

from allauth.socialaccount.models import SocialAccount
from django.contrib.auth.models import AbstractUser
from django.core.cache import cache
from django.db import models
Expand Down Expand Up @@ -73,6 +74,10 @@ def mesh_username(self):
# lower() needed for mesh api
return f"{self.username.replace(' ', '').lower()}___{self.pk}"

@property
def is_sso_user(self):
return SocialAccount.objects.filter(user_id=self.pk).exists()

@staticmethod
def serialize(user):
# serializes the task and returns json
Expand Down
12 changes: 12 additions & 0 deletions api/tacticalrmm/accounts/permissions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rest_framework import permissions

from tacticalrmm.permissions import _has_perm
from tacticalrmm.utils import get_core_settings


class AccountsPerms(permissions.BasePermission):
Expand Down Expand Up @@ -40,3 +41,14 @@ def has_permission(self, r, view) -> bool:
return _has_perm(r, "can_list_api_keys")

return _has_perm(r, "can_manage_api_keys")


class LocalUserPerms(permissions.BasePermission):
def has_permission(self, r, view) -> bool:
settings = get_core_settings()
return not settings.block_local_user_logon


class SelfResetSSOPerms(permissions.BasePermission):
def has_permission(self, r, view) -> bool:
return not r.user.is_sso_user
5 changes: 3 additions & 2 deletions api/tacticalrmm/accounts/serializers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import pyotp
from django.conf import settings
from rest_framework.serializers import (
ModelSerializer,
ReadOnlyField,
SerializerMethodField,
)

from tacticalrmm.helpers import get_webdomain
from tacticalrmm.util_settings import get_webdomain

from .models import APIKey, Role, User

Expand Down Expand Up @@ -63,7 +64,7 @@ class Meta:

def get_qr_url(self, obj):
return pyotp.totp.TOTP(obj.totp_key).provisioning_uri(
obj.username, issuer_name=get_webdomain()
obj.username, issuer_name=get_webdomain(settings.CORS_ORIGIN_WHITELIST[0])
)


Expand Down
1 change: 1 addition & 0 deletions api/tacticalrmm/accounts/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

class TestAccounts(TacticalTestCase):
def setUp(self):
self.setup_coresettings()
self.setup_client()
self.bob = User(username="bob")
self.bob.set_password("hunter2")
Expand Down
4 changes: 4 additions & 0 deletions api/tacticalrmm/accounts/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
urlpatterns = [
path("users/", views.GetAddUsers.as_view()),
path("<int:pk>/users/", views.GetUpdateDeleteUser.as_view()),
path("sessions/<str:pk>/", views.DeleteActiveLoginSession.as_view()),
path(
"users/<int:pk>/sessions/", views.GetDeleteActiveLoginSessionsPerUser.as_view()
),
path("users/reset/", views.UserActions.as_view()),
path("users/reset_totp/", views.UserActions.as_view()),
path("users/setup_totp/", views.TOTPSetup.as_view()),
Expand Down
Loading
Loading