From 0697e3d5a402f8396be27e975378e75150f63643 Mon Sep 17 00:00:00 2001 From: Jens L Date: Mon, 16 Oct 2023 19:42:19 +0200 Subject: [PATCH] rbac: revisions (#7188) * improve system migration logging Signed-off-by: Jens Langhammer * fix filter for internal service accounts Signed-off-by: Jens Langhammer * merge migration Signed-off-by: Jens Langhammer * bump go api Signed-off-by: Jens Langhammer * sources/ldap: check if we need to connect to ldap before connecting Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer --- authentik/rbac/filters.py | 7 +++++++ authentik/rbac/migrations/0002_systempermission.py | 2 ++ authentik/sources/ldap/password.py | 5 +++++ authentik/sources/ldap/signals.py | 9 ++++++--- go.mod | 2 +- go.sum | 4 ++-- lifecycle/migrate.py | 8 ++++---- .../{to_0_14_events..py => to_0_14_events.py} | 0 8 files changed, 27 insertions(+), 10 deletions(-) rename lifecycle/system_migrations/{to_0_14_events..py => to_0_14_events.py} (100%) diff --git a/authentik/rbac/filters.py b/authentik/rbac/filters.py index 395efd673..22f5a768e 100644 --- a/authentik/rbac/filters.py +++ b/authentik/rbac/filters.py @@ -4,6 +4,8 @@ from rest_framework.exceptions import PermissionDenied from rest_framework.request import Request from rest_framework_guardian.filters import ObjectPermissionsFilter +from authentik.core.models import UserTypes + class ObjectFilter(ObjectPermissionsFilter): """Object permission filter that grants global permission higher priority than @@ -19,6 +21,11 @@ class ObjectFilter(ObjectPermissionsFilter): if request.user.has_perm(permission): return queryset queryset = super().filter_queryset(request, queryset, view) + # Outposts (which are the only objects using internal service accounts) + # except requests to return an empty list when they have no objects + # assigned + if request.user.type == UserTypes.INTERNAL_SERVICE_ACCOUNT: + return queryset if not queryset.exists(): # User doesn't have direct permission to all objects # and also no object permissions assigned (directly or via role) diff --git a/authentik/rbac/migrations/0002_systempermission.py b/authentik/rbac/migrations/0002_systempermission.py index eb4a03bb4..8a08c09fb 100644 --- a/authentik/rbac/migrations/0002_systempermission.py +++ b/authentik/rbac/migrations/0002_systempermission.py @@ -26,6 +26,8 @@ class Migration(migrations.Migration): ("run_system_tasks", "Can run system tasks"), ("access_admin_interface", "Can access admin interface"), ], + "verbose_name": "System permission", + "verbose_name_plural": "System permissions", "managed": False, "default_permissions": (), }, diff --git a/authentik/sources/ldap/password.py b/authentik/sources/ldap/password.py index 43b09befc..d4e0dff4e 100644 --- a/authentik/sources/ldap/password.py +++ b/authentik/sources/ldap/password.py @@ -49,6 +49,11 @@ class LDAPPasswordChanger: self._source = source self._connection = source.connection() + @staticmethod + def should_check_user(user: User) -> bool: + """Check if the user has LDAP parameters and needs to be checked""" + return LDAP_DISTINGUISHED_NAME in user.attributes + def get_domain_root_dn(self) -> str: """Attempt to get root DN via MS specific fields or generic LDAP fields""" info = self._connection.server.info diff --git a/authentik/sources/ldap/signals.py b/authentik/sources/ldap/signals.py index a5f7ea037..5af97376d 100644 --- a/authentik/sources/ldap/signals.py +++ b/authentik/sources/ldap/signals.py @@ -41,11 +41,12 @@ def ldap_password_validate(sender, password: str, plan_context: dict[str, Any], if not sources.exists(): return source = sources.first() + user = plan_context.get(PLAN_CONTEXT_PENDING_USER, None) + if user and not LDAPPasswordChanger.should_check_user(user): + return changer = LDAPPasswordChanger(source) if changer.check_ad_password_complexity_enabled(): - passing = changer.ad_password_complexity( - password, plan_context.get(PLAN_CONTEXT_PENDING_USER, None) - ) + passing = changer.ad_password_complexity(password, user) if not passing: raise ValidationError(_("Password does not match Active Directory Complexity.")) @@ -57,6 +58,8 @@ def ldap_sync_password(sender, user: User, password: str, **_): if not sources.exists(): return source = sources.first() + if not LDAPPasswordChanger.should_check_user(user): + return try: changer = LDAPPasswordChanger(source) changer.change_password(user, password) diff --git a/go.mod b/go.mod index 0b26945b5..5f5394955 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/stretchr/testify v1.8.4 - goauthentik.io/api/v3 v3.2023083.6 + goauthentik.io/api/v3 v3.2023083.7 golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab golang.org/x/oauth2 v0.13.0 golang.org/x/sync v0.4.0 diff --git a/go.sum b/go.sum index d9899e759..75d8b4a2d 100644 --- a/go.sum +++ b/go.sum @@ -355,8 +355,8 @@ go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyK go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -goauthentik.io/api/v3 v3.2023083.6 h1:VYVnE/3CYhggmobeZ+V3ka0TwswrUhKasxwGPmXTq0M= -goauthentik.io/api/v3 v3.2023083.6/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= +goauthentik.io/api/v3 v3.2023083.7 h1:/nS5Cgg+daTmsHVoFNxANLUQXVsJMAu4U8P7OyxeZf0= +goauthentik.io/api/v3 v3.2023083.7/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= diff --git a/lifecycle/migrate.py b/lifecycle/migrate.py index 68d45dbc3..4476a4681 100755 --- a/lifecycle/migrate.py +++ b/lifecycle/migrate.py @@ -81,8 +81,8 @@ if __name__ == "__main__": ) curr = conn.cursor() try: - for migration in Path(__file__).parent.absolute().glob("system_migrations/*.py"): - spec = spec_from_file_location("lifecycle.system_migrations", migration) + for migration_path in Path(__file__).parent.absolute().glob("system_migrations/*.py"): + spec = spec_from_file_location("lifecycle.system_migrations", migration_path) if not spec: continue mod = module_from_spec(spec) @@ -94,9 +94,9 @@ if __name__ == "__main__": migration = sub(curr, conn) if migration.needs_migration(): wait_for_lock() - LOGGER.info("Migration needs to be applied", migration=sub) + LOGGER.info("Migration needs to be applied", migration=migration_path.name) migration.run() - LOGGER.info("Migration finished applying", migration=sub) + LOGGER.info("Migration finished applying", migration=migration_path.name) release_lock() LOGGER.info("applying django migrations") environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings") diff --git a/lifecycle/system_migrations/to_0_14_events..py b/lifecycle/system_migrations/to_0_14_events.py similarity index 100% rename from lifecycle/system_migrations/to_0_14_events..py rename to lifecycle/system_migrations/to_0_14_events.py