Example #1
0
def is_request_blocked_from_viewing_domain_due_to_sso(request, domain_obj):
    """
    Checks whether a given request is allowed to view a domain.

    :param request: HttpRequest
    :param domain_obj: Domain object
    :return: Boolean (True if request is blocked)
    """
    if not is_request_using_sso(request):
        # Request is not using SSO, so it's never blocked
        return False

    username = request.user.username
    idp = IdentityProvider.get_active_identity_provider_by_username(username)
    if not idp:
        raise SingleSignOnError(
            f"User {username} was authenticated via SSO, but does not appear "
            f"to be associated with an Identity Provider!")

    if idp.does_domain_trust_this_idp(domain_obj.name):
        return False

    if domain_obj.creating_user == username:
        # The user created this domain and thus should never be blocked.
        # However, a Trust was not yet established. Since the current user
        # owns this domain, a Trust will be created automatically and a message
        # will be displayed to the user.
        if idp.create_trust_with_domain(domain_obj.name,
                                        request.user.username):
            messages.success(
                request, get_success_message_for_trusted_idp(idp, domain_obj))
        return False

    # None of the above criteria was met so the user is definitely blocked!
    return True
Example #2
0
def render_untrusted_identity_provider_for_domain_view(request, domain):
    """
    This is a "faux" view that doesn't map to a url, but returns a rendered
    page response which alerts the user that they do not have access (based
    on unmet sso requirements) to view this Domain.

    :param request: HttpRequest
    :param domain: Domain (object)
    :return: HttpResponse
    """
    identity_provider = IdentityProvider.get_active_identity_provider_by_username(
        request.user.username)
    template = "sso/permissions/untrusted_identity_provider_for_domain.html"
    context = {
        'section': {
            'page_name': domain.name,
        },
        'current_page': {
            'title': _("Untrusted Identity Provider"),
            'page_name': _("Untrusted Identity Provider"),
        },
        'domain': domain.name,
        'identity_provider': identity_provider.name,
    }
    return render(request, template, context)
Example #3
0
 def test_cache_cleanup_when_email_domain_is_added_and_removed(self):
     """
     Ensure that the cache for
     IdentityProvider.get_active_identity_provider_by_email_domain
     is cleared  when an AuthenticatedEmailDomain is added or removed.
     """
     self.assertIsNone(
         IdentityProvider.get_active_identity_provider_by_email_domain(
             'vaultwax.com'))
     email_domain = AuthenticatedEmailDomain.objects.create(
         email_domain='vaultwax.com', identity_provider=self.idp)
     self.assertEqual(
         IdentityProvider.get_active_identity_provider_by_email_domain(
             'vaultwax.com'), self.idp)
     email_domain.delete()
     self.assertIsNone(
         IdentityProvider.get_active_identity_provider_by_email_domain(
             'vaultwax.com'))
Example #4
0
    def register_new_user(self, data):
        idp = None
        if settings.ENFORCE_SSO_LOGIN:
            idp = IdentityProvider.get_required_identity_provider(
                data['data']['email'])

        reg_form = RegisterWebUserForm(data['data'], is_sso=idp is not None)
        if reg_form.is_valid():
            ab_test = ab_tests.SessionAbTest(ab_tests.APPCUES_V3_APP,
                                             self.request)
            appcues_ab_test = ab_test.context['version']

            if idp:
                signup_request = AsyncSignupRequest.create_from_registration_form(
                    reg_form,
                    additional_hubspot_data={
                        "appcues_test": appcues_ab_test,
                    })
                return {
                    'success': True,
                    'appcues_ab_test': appcues_ab_test,
                    'ssoLoginUrl': idp.get_login_url(signup_request.username),
                    'ssoIdpName': idp.name,
                }

            self._create_new_account(reg_form,
                                     additional_hubspot_data={
                                         "appcues_test": appcues_ab_test,
                                     })
            try:
                request_new_domain(self.request,
                                   reg_form.cleaned_data['project_name'],
                                   is_new_user=True)
            except NameUnavailableException:
                # technically, the form should never reach this as names are
                # auto-generated now. But, just in case...
                logging.error(
                    "There as an issue generating a unique domain name "
                    "for a user during new registration.")
                return {
                    'errors': {
                        'project name unavailable': [],
                    }
                }
            return {
                'success': True,
                'appcues_ab_test': appcues_ab_test,
            }
        logging.error(
            "There was an error processing a new user registration form."
            "This shouldn't happen as validation should be top-notch "
            "client-side. Here is what the errors are: {}".format(
                reg_form.errors))
        return {
            'errors': reg_form.errors,
        }
Example #5
0
 def test_cache_cleanup_when_identity_provider_is_active_status_changes(
         self):
     """
     Ensure that the cache for
     IdentityProvider.get_active_identity_provider_by_email_domain
     is cleared when the IdentityProvider's is_active status changes from
     True to False.
     """
     self.addCleanup(self._cleanup_identity_provider)
     AuthenticatedEmailDomain.objects.create(email_domain='vaultwax.com',
                                             identity_provider=self.idp)
     self.assertEqual(
         IdentityProvider.get_active_identity_provider_by_email_domain(
             'vaultwax.com'), self.idp)
     self.idp.is_active = False
     self.idp.save()
     self.assertIsNone(
         IdentityProvider.get_active_identity_provider_by_email_domain(
             'vaultwax.com'))
Example #6
0
    def page_context(self):
        if not (toggles.ENTERPRISE_SSO.enabled_for_request(self.request)
                and is_request_using_sso(self.request)):
            return {}

        idp = IdentityProvider.get_active_identity_provider_by_username(
            self.request.user.username)
        return {
            'is_using_sso': True,
            'idp_name': idp.name,
        }
Example #7
0
 def page_context(self):
     is_using_sso = is_request_using_sso(self.request)
     idp_name = None
     if is_using_sso:
         idp = IdentityProvider.get_active_identity_provider_by_username(
             self.request.user.username)
         idp_name = idp.name
     return {
         'form': self.password_change_form,
         'hide_password_feedback': has_custom_clean_password(),
         'is_using_sso': is_using_sso,
         'idp_name': idp_name,
     }
Example #8
0
def sso_saml_login(request, idp_slug):
    """
    This view initiates a SAML 2.0 login request with the Identity Provider.
    """
    login_url = request.saml2_auth.login(return_to=request.GET.get('next'))
    username = request.GET.get('username')
    if username:
        # verify that the stored user data actually the current IdP
        idp = IdentityProvider.get_active_identity_provider_by_username(
            username)
        if idp and idp.slug == idp_slug:
            # pre-populate username for Azure AD
            login_url = f'{login_url}&login_hint={username}'
    return HttpResponseRedirect(login_url)
Example #9
0
    def check_username_availability(self, data):
        email = data['email'].strip()
        duplicate = CouchUser.get_by_username(email)
        is_existing = User.objects.filter(
            username__iexact=email).count() > 0 or duplicate

        message = None
        restricted_by_domain = None
        if is_existing:
            message = _("There is already a user with this email.")
        else:
            domain = email[email.find("@") + 1:]
            for account in BillingAccount.get_enterprise_restricted_signup_accounts(
            ):
                if domain in account.enterprise_restricted_signup_domains:
                    restricted_by_domain = domain
                    message = account.restrict_signup_message
                    regex = r'(\b[a-zA-Z0-9_.+%-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+\b)'
                    subject = _("CommCareHQ account request")
                    message = re.sub(
                        regex,
                        "<a href='mailto:\\1?subject={}'>\\1</a>".format(
                            subject), message)
                    break

        response = {
            'isValid': message is None,
            'restrictedByDomain': restricted_by_domain,
            'message': message,
        }
        if settings.ENFORCE_SSO_LOGIN and response['isValid']:
            idp = IdentityProvider.get_required_identity_provider(email)
            if idp:
                response.update({
                    'isSso':
                    True,
                    'ssoMessage':
                    _("This email is managed by {}. You will be asked to login "
                      "with Single Sign-On after the next step.").format(
                          idp.name),
                })
        return response
Example #10
0
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        if is_request_using_sso(self.request):
            idp = IdentityProvider.get_active_identity_provider_by_username(
                self.request.user.username)
            context.update({
                'is_using_sso': True,
                'idp_name': idp.name,
            })
        elif context.get('default_device'):
            # Default device means the user has 2FA already enabled
            has_existing_backup_phones = bool(context.get('backup_phones'))
            context.update({
                'allow_phone_2fa':
                has_existing_backup_phones
                or _user_can_use_phone(self.request.couch_user),
            })

        return context
Example #11
0
    def __call__(self, request, uuid, **kwargs):
        # add the correct parameters to this instance
        self.request = request
        if 'domain' in kwargs:
            self.domain = kwargs['domain']

        if request.GET.get('switch') == 'true':
            logout(request)
            return redirect_to_login(request.path)
        if request.GET.get('create') == 'true':
            logout(request)
            return HttpResponseRedirect(request.path)
        try:
            invitation = Invitation.objects.get(uuid=uuid)
        except (Invitation.DoesNotExist, ValidationError):
            messages.error(
                request,
                _("Sorry, it looks like your invitation has expired. "
                  "Please check the invitation link you received and try again, or "
                  "request a project administrator to send you the invitation again."
                  ))
            return HttpResponseRedirect(reverse("login"))

        if invitation.is_accepted:
            messages.error(
                request,
                _("Sorry, that invitation has already been used up. "
                  "If you feel this is a mistake please ask the inviter for "
                  "another invitation."))
            return HttpResponseRedirect(reverse("login"))

        self.validate_invitation(invitation)

        if invitation.is_expired:
            return HttpResponseRedirect(reverse("no_permissions"))

        # Add zero-width space to username for better line breaking
        username = self.request.user.username.replace("@", "&#x200b;@")
        context = {
            'formatted_username': username,
            'domain': self.domain,
            'invite_to': self.domain,
            'invite_type': _('Project'),
            'hide_password_feedback': has_custom_clean_password(),
        }
        if request.user.is_authenticated:
            context['current_page'] = {'page_name': _('Project Invitation')}
        else:
            context['current_page'] = {
                'page_name': _('Project Invitation, Account Required')
            }
        if request.user.is_authenticated:
            is_invited_user = request.couch_user.username.lower(
            ) == invitation.email.lower()
            if self.is_invited(invitation, request.couch_user
                               ) and not request.couch_user.is_superuser:
                if is_invited_user:
                    # if this invite was actually for this user, just mark it accepted
                    messages.info(
                        request,
                        _("You are already a member of {entity}.").format(
                            entity=self.inviting_entity))
                    invitation.is_accepted = True
                    invitation.save()
                else:
                    messages.error(
                        request,
                        _("It looks like you are trying to accept an invitation for "
                          "{invited} but you are already a member of {entity} with the "
                          "account {current}. Please sign out to accept this invitation "
                          "as another user.").format(
                              entity=self.inviting_entity,
                              invited=invitation.email,
                              current=request.couch_user.username))
                return HttpResponseRedirect(
                    self.redirect_to_on_success(invitation.email, self.domain))

            if not is_invited_user:
                messages.error(
                    request,
                    _("The invited user {invited} and your user {current} "
                      "do not match!").format(
                          invited=invitation.email,
                          current=request.couch_user.username))

            if request.method == "POST":
                couch_user = CouchUser.from_django_user(request.user,
                                                        strict=True)
                invitation.accept_invitation_and_join_domain(couch_user)
                log_user_change(
                    by_domain=invitation.domain,
                    for_domain=invitation.domain,
                    couch_user=couch_user,
                    changed_by_user=CouchUser.get_by_user_id(
                        invitation.invited_by),
                    changed_via=USER_CHANGE_VIA_INVITATION,
                    change_messages=UserChangeMessage.domain_addition(
                        invitation.domain))
                track_workflow(
                    request.couch_user.get_email(),
                    "Current user accepted a project invitation",
                    {"Current user accepted a project invitation": "yes"})
                send_hubspot_form(HUBSPOT_EXISTING_USER_INVITE_FORM, request)
                return HttpResponseRedirect(
                    self.redirect_to_on_success(invitation.email, self.domain))
            else:
                mobile_user = CouchUser.from_django_user(
                    request.user).is_commcare_user()
                context.update({
                    'mobile_user':
                    mobile_user,
                    "invited_user":
                    invitation.email
                    if request.couch_user.username != invitation.email else "",
                })
                return render(request, self.template, context)
        else:
            idp = None
            if settings.ENFORCE_SSO_LOGIN:
                idp = IdentityProvider.get_active_identity_provider_by_username(
                    invitation.email)

            if request.method == "POST":
                form = WebUserInvitationForm(request.POST,
                                             is_sso=idp is not None)
                if form.is_valid():
                    # create the new user
                    invited_by_user = CouchUser.get_by_user_id(
                        invitation.invited_by)

                    if idp:
                        signup_request = AsyncSignupRequest.create_from_invitation(
                            invitation)
                        return HttpResponseRedirect(
                            idp.get_login_url(signup_request.username))

                    user = activate_new_user_via_reg_form(
                        form,
                        created_by=invited_by_user,
                        created_via=USER_CHANGE_VIA_INVITATION,
                        domain=invitation.domain,
                        is_domain_admin=False,
                    )
                    user.save()
                    messages.success(
                        request,
                        _("User account for %s created!") %
                        form.cleaned_data["email"])
                    invitation.accept_invitation_and_join_domain(user)
                    messages.success(
                        self.request,
                        _('You have been added to the "{}" project space.').
                        format(self.domain))
                    authenticated = authenticate(
                        username=form.cleaned_data["email"],
                        password=form.cleaned_data["password"],
                        request=request)
                    if authenticated is not None and authenticated.is_active:
                        login(request, authenticated)
                    track_workflow(
                        request.POST['email'],
                        "New User Accepted a project invitation",
                        {"New User Accepted a project invitation": "yes"})
                    send_hubspot_form(HUBSPOT_NEW_USER_INVITE_FORM, request,
                                      user)
                    return HttpResponseRedirect(
                        self.redirect_to_on_success(invitation.email,
                                                    invitation.domain))
            else:
                if (CouchUser.get_by_username(invitation.email)
                        or User.objects.filter(
                            username__iexact=invitation.email).count() > 0):
                    login_url = reverse("login")
                    accept_invitation_url = reverse(
                        'domain_accept_invitation',
                        args=[invitation.domain, invitation.uuid])
                    return HttpResponseRedirect(
                        f"{login_url}"
                        f"?next={accept_invitation_url}"
                        f"&username={invitation.email}")
                form = WebUserInvitationForm(
                    initial={
                        'email': invitation.email,
                    },
                    is_sso=idp is not None,
                )

            context.update({
                'is_sso': idp is not None,
                'idp_name': idp.name if idp else None,
                'invited_user': invitation.email,
            })

        context.update({"form": form})
        return render(request, self.template, context)
Example #12
0
def create_idp(slug, account, include_certs=False):
    idp = IdentityProvider(
        name=f"Azure AD for {account.name}",
        slug=slug,
        owner=account,
    )
    idp.save()
    if include_certs:
        idp.create_service_provider_certificate()
        idp.entity_id = "https://testidp.com/saml2/entity_id"
        idp.login_url = "https://testidp.com/saml2/login"
        idp.logout_url = "https://testidp.com/saml2/logout"
        key_pair = certificates.create_key_pair()
        cert = certificates.create_self_signed_cert(key_pair)
        idp.idp_cert_public = certificates.get_public_key(cert)
        idp.date_idp_cert_expiration = certificates.get_expiration_date(cert)
        idp.save()
    return idp
Example #13
0
def create_idp(account=None, include_certs=False):
    if not account:
        account = get_billing_account_for_idp()
    idp_slug = data_gen.arbitrary_unique_name()[:20]
    idp = IdentityProvider(name=f"Azure AD for {account.name}",
                           slug=idp_slug,
                           owner=account)
    idp.save()
    if include_certs:
        idp.create_service_provider_certificate()
        idp.entity_id = "https://testidp.com/saml2/entity_id"
        idp.login_url = "https://testidp.com/saml2/login"
        idp.logout_url = "https://testidp.com/saml2/logout"
        key_pair = certificates.create_key_pair()
        cert = certificates.create_self_signed_cert(key_pair)
        idp.idp_cert_public = certificates.get_public_key(cert)
        idp.date_idp_cert_expiration = certificates.get_expiration_date(cert)
        idp.save()
    return idp