Project

General

Profile

0005-models-mind-auth-level-at-service-access-authorizati.patch

Valentin Deniaud, 21 June 2019 02:19 PM

Download (8.92 KB)

View differences:

Subject: [PATCH] models: mind auth level at service access authorization

Allows to enable multi-factor authentication on a per-service basis via
service access restriction to some roles.
 src/authentic2/idp/saml/saml2_endpoints.py |  2 +-
 src/authentic2/manager/role_views.py       |  8 ++++----
 src/authentic2/manager/utils.py            |  5 -----
 src/authentic2/manager/views.py            |  4 ++--
 src/authentic2/middleware.py               |  2 ++
 src/authentic2/models.py                   | 15 +++++++++++----
 src/authentic2/utils.py                    |  9 +++++++++
 src/authentic2_idp_cas/views.py            |  2 +-
 src/authentic2_idp_oidc/views.py           |  2 +-
 9 files changed, 31 insertions(+), 18 deletions(-)
src/authentic2/idp/saml/saml2_endpoints.py
set_saml2_response_responder_status_code(login.response, lasso.SAML2_STATUS_CODE_NO_PASSIVE)
return finish_sso(request, login)
service.authorize(request.user)
service.authorize(request)
hooks.call_hooks('event', name='sso-request', idp='saml2', service=service)
src/authentic2/manager/role_views.py
from django_rbac.exceptions import InsufficientAuthLevel
from django_rbac.utils import get_role_model, get_permission_model, get_ou_model
from authentic2.utils import redirect
from authentic2.utils import redirect, increase_auth_level
from authentic2 import hooks, data_transfer
from . import tables, views, resources, forms, app_settings, utils
......
user=self.request.user, role=self.object, member=user)
else:
if self.could_change:
return utils.increase_auth_level(self.request)
return increase_auth_level(self.request)
messages.warning(self.request, _('You are not authorized'))
return super(RoleMembersView, self).form_valid(form)
......
def post(self, request, *args, **kwargs):
if not self.can_delete:
if self.could_delete:
return utils.increase_auth_level(self.request)
return increase_auth_level(self.request)
raise PermissionDenied
return super(RoleDeleteView, self).post(request, *args, **kwargs)
......
user=self.request.user, role=self.object, permission=perm)
else:
if self.could_change:
return utils.increase_auth_level(self.request)
return increase_auth_level(self.request)
messages.warning(self.request, _('You are not authorized'))
return super(RolePermissionsView, self).form_valid(form)
src/authentic2/manager/utils.py
@GlobalCache(timeout=10)
def get_ou_count():
return get_ou_model().objects.count()
def increase_auth_level(request):
current_auth_level = request.session.get('auth_level', 1)
return login_require(request, params={'auth_level': current_auth_level + 1})
src/authentic2/manager/views.py
from authentic2.data_transfer import export_site, import_site, DataImportError, ImportContext
from authentic2.forms.profile import modelform_factory
from authentic2.utils import redirect, batch_queryset
from authentic2.utils import redirect, batch_queryset, increase_auth_level
from authentic2.decorators import json as json_view
from authentic2 import hooks
......
try:
response = self.authorize(request, *args, **kwargs)
except InsufficientAuthLevel:
return utils.increase_auth_level(request)
return increase_auth_level(request)
if response is not None:
return response
return super(PermissionMixin, self).dispatch(request, *args, **kwargs)
src/authentic2/middleware.py
def process_exception(self, request, exception):
if not isinstance(exception, (utils.ServiceAccessDenied,)):
return None
if isinstance(exception, (utils.IncreaseAuthLevel,)):
return utils.increase_auth_level(request)
return utils.unauthorized_view(request, exception.service)
src/authentic2/models.py
from . import managers
# install our natural_key implementation
from . import natural_key as unused_natural_key # noqa: F401
from .utils import ServiceAccessDenied
from .utils import ServiceAccessDenied, IncreaseAuthLevel
class DeletedUser(models.Model):
......
def __repr__(self):
return '<%s %r>' % (self.__class__.__name__, six.text_type(self))
def authorize(self, user):
def authorize(self, request):
if not self.authorized_roles.exists():
return True
if user.roles_and_parents().filter(allowed_services=self).exists():
return True
allowed_roles = request.user.roles_and_parents() \
.filter(allowed_services=self) \
.set_needed_auth_levels(request.user)
user_auth_level = request.session.get('auth_level', 1)
for role in allowed_roles:
if role.needed_auth_level <= user_auth_level:
return True
if allowed_roles:
raise IncreaseAuthLevel(service=self)
raise ServiceAccessDenied(service=self)
def add_authorized_role(self, role):
src/authentic2/utils.py
self.service = service
class IncreaseAuthLevel(ServiceAccessDenied):
pass
def unauthorized_view(request, service):
context = {'callback_url': service.unauthorized_url or reverse('auth_homepage')}
return render(request, 'authentic2/unauthorized.html', context=context)
......
if session is not None:
return session.get(constants.AUTHENTICATION_EVENTS_SESSION_KEY, [])
return []
def increase_auth_level(request):
current_auth_level = request.session.get('auth_level', 1)
return login_require(request, params={'auth_level': current_auth_level + 1})
src/authentic2_idp_cas/views.py
return self.authenticate(request, st)
# if user not authorized, a ServiceAccessDenied exception
# is raised and handled by ServiceAccessMiddleware
st.service.authorize(request.user)
st.service.authorize(request)
self.validate_ticket(request, st)
if st.valid():
src/authentic2_idp_oidc/views.py
# if user not authorized, a ServiceAccessDenied exception
# is raised and handled by ServiceAccessMiddleware
client.authorize(request.user)
client.authorize(request)
last_auth = last_authentication_event(request=request)
if max_age is not None and time.time() - last_auth['when'] >= max_age: