rbac: revisions (#7188)
* improve system migration logging Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix filter for internal service accounts Signed-off-by: Jens Langhammer <jens@goauthentik.io> * merge migration Signed-off-by: Jens Langhammer <jens@goauthentik.io> * bump go api Signed-off-by: Jens Langhammer <jens@goauthentik.io> * sources/ldap: check if we need to connect to ldap before connecting Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
6deb231e0e
commit
0697e3d5a4
|
@ -4,6 +4,8 @@ from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework_guardian.filters import ObjectPermissionsFilter
|
from rest_framework_guardian.filters import ObjectPermissionsFilter
|
||||||
|
|
||||||
|
from authentik.core.models import UserTypes
|
||||||
|
|
||||||
|
|
||||||
class ObjectFilter(ObjectPermissionsFilter):
|
class ObjectFilter(ObjectPermissionsFilter):
|
||||||
"""Object permission filter that grants global permission higher priority than
|
"""Object permission filter that grants global permission higher priority than
|
||||||
|
@ -19,6 +21,11 @@ class ObjectFilter(ObjectPermissionsFilter):
|
||||||
if request.user.has_perm(permission):
|
if request.user.has_perm(permission):
|
||||||
return queryset
|
return queryset
|
||||||
queryset = super().filter_queryset(request, queryset, view)
|
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():
|
if not queryset.exists():
|
||||||
# User doesn't have direct permission to all objects
|
# User doesn't have direct permission to all objects
|
||||||
# and also no object permissions assigned (directly or via role)
|
# and also no object permissions assigned (directly or via role)
|
||||||
|
|
|
@ -26,6 +26,8 @@ class Migration(migrations.Migration):
|
||||||
("run_system_tasks", "Can run system tasks"),
|
("run_system_tasks", "Can run system tasks"),
|
||||||
("access_admin_interface", "Can access admin interface"),
|
("access_admin_interface", "Can access admin interface"),
|
||||||
],
|
],
|
||||||
|
"verbose_name": "System permission",
|
||||||
|
"verbose_name_plural": "System permissions",
|
||||||
"managed": False,
|
"managed": False,
|
||||||
"default_permissions": (),
|
"default_permissions": (),
|
||||||
},
|
},
|
||||||
|
|
|
@ -49,6 +49,11 @@ class LDAPPasswordChanger:
|
||||||
self._source = source
|
self._source = source
|
||||||
self._connection = source.connection()
|
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:
|
def get_domain_root_dn(self) -> str:
|
||||||
"""Attempt to get root DN via MS specific fields or generic LDAP fields"""
|
"""Attempt to get root DN via MS specific fields or generic LDAP fields"""
|
||||||
info = self._connection.server.info
|
info = self._connection.server.info
|
||||||
|
|
|
@ -41,11 +41,12 @@ def ldap_password_validate(sender, password: str, plan_context: dict[str, Any],
|
||||||
if not sources.exists():
|
if not sources.exists():
|
||||||
return
|
return
|
||||||
source = sources.first()
|
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)
|
changer = LDAPPasswordChanger(source)
|
||||||
if changer.check_ad_password_complexity_enabled():
|
if changer.check_ad_password_complexity_enabled():
|
||||||
passing = changer.ad_password_complexity(
|
passing = changer.ad_password_complexity(password, user)
|
||||||
password, plan_context.get(PLAN_CONTEXT_PENDING_USER, None)
|
|
||||||
)
|
|
||||||
if not passing:
|
if not passing:
|
||||||
raise ValidationError(_("Password does not match Active Directory Complexity."))
|
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():
|
if not sources.exists():
|
||||||
return
|
return
|
||||||
source = sources.first()
|
source = sources.first()
|
||||||
|
if not LDAPPasswordChanger.should_check_user(user):
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
changer = LDAPPasswordChanger(source)
|
changer = LDAPPasswordChanger(source)
|
||||||
changer.change_password(user, password)
|
changer.change_password(user, password)
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -27,7 +27,7 @@ require (
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/stretchr/testify v1.8.4
|
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/exp v0.0.0-20230210204819-062eb4c674ab
|
||||||
golang.org/x/oauth2 v0.13.0
|
golang.org/x/oauth2 v0.13.0
|
||||||
golang.org/x/sync v0.4.0
|
golang.org/x/sync v0.4.0
|
||||||
|
|
4
go.sum
4
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.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 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
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.7 h1:/nS5Cgg+daTmsHVoFNxANLUQXVsJMAu4U8P7OyxeZf0=
|
||||||
goauthentik.io/api/v3 v3.2023083.6/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
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-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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
|
|
@ -81,8 +81,8 @@ if __name__ == "__main__":
|
||||||
)
|
)
|
||||||
curr = conn.cursor()
|
curr = conn.cursor()
|
||||||
try:
|
try:
|
||||||
for migration in Path(__file__).parent.absolute().glob("system_migrations/*.py"):
|
for migration_path in Path(__file__).parent.absolute().glob("system_migrations/*.py"):
|
||||||
spec = spec_from_file_location("lifecycle.system_migrations", migration)
|
spec = spec_from_file_location("lifecycle.system_migrations", migration_path)
|
||||||
if not spec:
|
if not spec:
|
||||||
continue
|
continue
|
||||||
mod = module_from_spec(spec)
|
mod = module_from_spec(spec)
|
||||||
|
@ -94,9 +94,9 @@ if __name__ == "__main__":
|
||||||
migration = sub(curr, conn)
|
migration = sub(curr, conn)
|
||||||
if migration.needs_migration():
|
if migration.needs_migration():
|
||||||
wait_for_lock()
|
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()
|
migration.run()
|
||||||
LOGGER.info("Migration finished applying", migration=sub)
|
LOGGER.info("Migration finished applying", migration=migration_path.name)
|
||||||
release_lock()
|
release_lock()
|
||||||
LOGGER.info("applying django migrations")
|
LOGGER.info("applying django migrations")
|
||||||
environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
|
environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
|
||||||
|
|
Reference in a new issue