Skip to content

Commit

Permalink
Merge pull request #715 from intuitem/hotfix/idp-initiated-sso
Browse files Browse the repository at this point in the history
Hotfix/idp initiated sso
  • Loading branch information
ab-smith authored Aug 14, 2024
2 parents d6f41d3 + a33eb36 commit 8f78535
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 17 deletions.
72 changes: 55 additions & 17 deletions backend/iam/sso/saml/views.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
from allauth.socialaccount.models import SocialLogin
import structlog
from allauth.core.exceptions import SignupClosedException
from allauth.socialaccount.adapter import get_account_adapter
from allauth.socialaccount.internal.flows.login import (
pre_social_login,
record_authentication,
)
from allauth.socialaccount.models import PermissionDenied, SocialLogin
from allauth.socialaccount.providers.saml.views import (
AuthError,
AuthProcess,
LoginSession,
OneLogin_Saml2_Error,
SAMLViewMixin,
binascii,
build_auth,
complete_social_login,
decode_relay_state,
httpkit,
render_authentication_error,
)
from allauth.socialaccount.providers.saml.views import AuthError as AllauthAuthError
from allauth.utils import ValidationError
from django.http import HttpRequest, HttpResponseRedirect
from django.http.response import Http404
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views import View
from rest_framework.views import csrf_exempt

import structlog

from iam.models import User
from iam.sso.models import SSOSettings
from iam.utils import generate_token

logger = structlog.get_logger(__name__)


class AuthError(AllauthAuthError):
IDP_INITIATED_SSO_REJECTED = "idpInitiatedSSORejected"
SIGNUP_CLOSED = "signupClosed"
PERMISSION_DENIED = "permissionDenied"
FAILED_SSO = "failedSSO"
USER_DOES_NOT_EXIST = "UserDoesNotExist"


@method_decorator(csrf_exempt, name="dispatch")
class ACSView(SAMLViewMixin, View):
def dispatch(self, request, organization_slug):
Expand All @@ -44,6 +57,8 @@ def dispatch(self, request, organization_slug):

class FinishACSView(SAMLViewMixin, View):
def dispatch(self, request, organization_slug):
error = None
next_url = "/"
if len(SSOSettings.objects.all()) == 0:
raise Http404()
try:
Expand Down Expand Up @@ -108,11 +123,14 @@ def dispatch(self, request, organization_slug):
)
if reject:
logger.error("IdP initiated SSO rejected")
return render_authentication_error(request, provider)
next_url = decode_relay_state(acs_request.POST.get("RelayState"))
return render_authentication_error(
request, provider, error=AuthError.IDP_INITIATED_SSO_REJECTED
)
next_url = (
decode_relay_state(acs_request.POST.get("RelayState")) or next_url
)
login.state["process"] = AuthProcess.LOGIN
if next_url:
login.state["next"] = next_url
login.state["next"] = next_url
try:
email = auth._nameid
user = User.objects.get(email=email)
Expand All @@ -130,12 +148,32 @@ def dispatch(self, request, organization_slug):
user.save()
token = generate_token(user)
login.state["next"] += f"sso/authenticate/{token}"
return complete_social_login(request, login)
except User.DoesNotExist:
pre_social_login(request, login)
if request.user.is_authenticated:
get_account_adapter(request).logout(request)
login._accept_login()
record_authentication(request, login)
except User.DoesNotExist as e:
# NOTE: We might want to allow signup some day
logger.warning("User does not exist")
return render_authentication_error(
request, provider, error="UserDoesNotExist"
)
except:
return render_authentication_error(request, provider, error="failedSSO")
error = AuthError.USER_DOES_NOT_EXIST
logger.error("User does not exist", exc_info=e)
except SignupClosedException as e:
error = AuthError.SIGNUP_CLOSED
logger.error("Signup closed", exc_info=e)
except PermissionDenied as e:
error = AuthError.PERMISSION_DENIED
logger.error("Permission denied", exc_info=e)
except ValidationError as e:
error = e.code
logger.error("Validation error", exc_info=e)
except Exception as e:
error = AuthError.FAILED_SSO
logger.error("SSO failed", exc_info=e)
finally:
next_url = login.state["next"]
if error:
next_url = httpkit.add_query_params(
next_url,
{"error": error, "error_process": login.state["process"]},
)
return HttpResponseRedirect(next_url)
3 changes: 3 additions & 0 deletions frontend/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,9 @@
"enableSSO": "Enable SSO",
"failedSSO": "SSO authentication failed, please contact your administrator",
"UserDoesNotExist": "User not declared, please contact your administrator",
"idPInitiatedSSORejected": "IdP initiated SSO rejected, please contact your administrator",
"permissionDenied": "Permission denied",
"signupClosed": "Signup closed",
"loginSSO": "Login with SSO",
"or": "or",
"errorImportingLibrary": "Error during library import",
Expand Down

0 comments on commit 8f78535

Please sign in to comment.