Пример #1
0
    def get_redirect_url(self):
        provider_name = self.request.GET.get('provider', None)
        if provider_name is None:
            raise ValidationError('No provider specified')

        badgr_app = BadgrApp.objects.get_current(request=self.request)
        if badgr_app is not None:
            set_session_badgr_app(self.request, badgr_app)
        else:
            raise ValidationError('Unable to save BadgrApp in session')

        self.request.session['source'] = self.request.GET.get('source', None)

        try:
            if 'saml2' in provider_name:
                redirect_url = reverse('saml2login', args=[provider_name])
            else:
                redirect_url = reverse('{}_login'.format(provider_name))
        except (
                NoReverseMatch,
                TypeError,
        ):
            raise ValidationError('No {} provider found'.format(provider_name))

        authcode = self.request.GET.get('authCode', None)
        if authcode is not None:
            set_session_authcode(self.request, authcode)
            return set_url_query_params(redirect_url,
                                        process=AuthProcess.CONNECT)
        else:
            return redirect_url
Пример #2
0
    def get_redirect_url(self):
        provider_name = self.request.GET.get('provider', None)
        if provider_name is None:
            raise ValidationError('No provider specified')

        badgr_app = BadgrApp.objects.get_current(request=self.request)
        if badgr_app is not None:
            set_session_badgr_app(self.request, badgr_app)
        else:
            raise ValidationError('Unable to save BadgrApp in session')

        try:
            redirect_url = reverse('{}_login'.format(
                self.request.GET.get('provider')))
        except NoReverseMatch:
            raise ValidationError('No {} provider found'.format(provider_name))

        auth_token = self.request.GET.get('authToken', None)
        if auth_token is not None:
            get_verified_user(
                auth_token
            )  # Raises AuthenticationFailed if auth token is invalid
            set_session_auth_token(self.request, auth_token)
            return set_url_query_params(redirect_url,
                                        process=AuthProcess.CONNECT)
        else:
            return redirect_url
Пример #3
0
    def get_context_data(self, **kwargs):
        context_data = super(LoginLti, self).get_context_data(**kwargs)

        context_data['ltitest'] = 'yes lti test'
        badgr_app = kwargs['tenant'].badgr_app
        context_data['login_url'] = self.get_login_url()
        context_data['login_image'] = self.get_login_image()
        if badgr_app is not None:
            set_session_badgr_app(self.request, badgr_app)
        else:
            print('badgr app is none in lti_view')

            # check login
        try:
            ltibadgetennant = LtiBadgeUserTennant.objects.get(
                lti_tennant=kwargs['tenant'],
                lti_user_id=self.request.POST['user_id'],
                staff=self.staff)
            # login_user(self.request, ltibadgetennant.badge_user)

        except Exception as e:
            pass

        context_data['after_login'] = self.get_after_login(badgr_app)
        context_data['check_login'] = self.get_check_login_url(badgr_app)
        return context_data
Пример #4
0
 def login(self, request, user):
     """
     Preserve badgr_app session data across Django login() boundary
     """
     badgr_app = get_session_badgr_app(request)
     ret = super(BadgrAccountAdapter, self).login(request, user)
     set_session_badgr_app(request, badgr_app)
     return ret
Пример #5
0
 def login(self, request, user):
     """
     Preserve badgr_app session data across Django login() boundary
     """
     badgr_app = get_session_badgr_app(request)
     ret = super(BadgrAccountAdapter, self).login(request, user)
     set_session_badgr_app(request, badgr_app)
     return ret
Пример #6
0
    def test_add_samlaccount_to_existing_user_with_varying_email(self):
        email = '*****@*****.**'
        t_user = self.setup_user(
            email=email, token_scope='rw:profile rw:issuer rw:backpack')

        preflight_response = self.client.get(
            reverse('v2_api_user_socialaccount_connect') +
            '?provider={}'.format(self.config.slug))
        self.assertEqual(preflight_response.status_code, 200)
        location = urlparse(preflight_response.data['result']['url'])
        authcode = parse_qs(location.query)['authCode'][0]
        location = '?'.join([location.path, location.query])

        # the location now includes an auth code
        self.client.logout()
        response = self.client.get(location)
        self.assertEqual(response.status_code, 302)
        location = response._headers['location'][1]
        response = self.client.get(location)
        self.assertEqual(response.status_code, 302)

        # Can auto provision again
        rf = RequestFactory()
        fake_request = rf.post(
            reverse('assertion_consumer_service',
                    kwargs={'idp_name': self.config.slug}),
            {'saml_assertion': 'very fake'})

        email2 = '*****@*****.**'
        resp = auto_provision(fake_request, [email2], t_user.first_name,
                              t_user.last_name, self.config)
        self.assertEqual(resp.status_code, 302)

        fake_request.session = dict()
        set_session_authcode(fake_request, authcode)
        set_session_badgr_app(fake_request, self.badgr_app)
        fake_request.session['idp_name'] = self.config.slug

        resp = self.client.get(resp.url)
        self.assertEqual(resp.status_code, 302)
        self.assertIn("authToken", resp.url)
        self.assertIn(self.badgr_app.ui_login_redirect, resp.url)
        Saml2Account.objects.get(
            user=t_user)  # There is a Saml account associated with the user.
        CachedEmailAddress.objects.get(email=email2,
                                       user=t_user,
                                       verified=True,
                                       primary=False)  # User has the email.

        email3 = '*****@*****.**'
        resp = auto_provision(fake_request, [email2, email3],
                              t_user.first_name, t_user.last_name, self.config)

        CachedEmailAddress.objects.get(email=email3,
                                       user=t_user,
                                       verified=True,
                                       primary=False)  # User has new email.
Пример #7
0
    def login(self, request, user):
        """
        Preserve badgr_app session data across Django login() boundary
        """
        badgr_app = get_session_badgr_app(request)

        if badgr_app is None:
            url = request.build_absolute_uri()
            print(('Badgr app is none in login {}'.format(url)))
        ret = super(BadgrAccountAdapter, self).login(request, user)
        set_session_badgr_app(request, badgr_app)
        return ret
Пример #8
0
 def get(self, *args, **kwargs):
     user = BadgeUser.objects.get(pk=kwargs['id'])
     social_account = user.get_social_account()
     get_adapter(self.request).logout(self.request)
     sociallogin = SocialLogin(account=social_account, email_addresses=[email for email in user.email_items])
     sociallogin.user = user
     badgr_app = BadgrApp.objects.filter(pk=user.badgrapp_id).first()
     if not badgr_app:
         badgr_app = BadgrApp.objects.all().first()
     set_session_badgr_app(self.request, badgr_app)
     ret = perform_login(self.request, sociallogin.user,
                   email_verification=app_settings.EMAIL_VERIFICATION,
                   redirect_url=sociallogin.get_redirect_url(self.request),
                   signal_kwargs={"sociallogin": sociallogin})
     return ret
Пример #9
0
    def login(self, request, user):
        """
        Guard against unverified users and preserve badgr_app session data
        across Django login() boundary.
        """
        badgr_app = BadgrApp.objects.get_current(request)

        if not user.verified and badgr_app.ui_login_redirect != badgr_app.ui_signup_success_redirect:
            # The usual case if a user gets here without a verified recipient
            # identifier is a new sign-up with an unverified email. If that's
            # the case, we just sent them a confirmation.
            # This is for UI clients that do not have the ability to function without a verified user identifier
            raise ImmediateHttpResponse(
                self.respond_email_verification_sent(request, user))

        ret = super(BadgrAccountAdapter, self).login(request, user)
        set_session_badgr_app(request, badgr_app)
        return ret
Пример #10
0
    def get_redirect_url(self):
        provider_name = self.request.GET.get('provider', None)
        if provider_name is None:
            raise ValidationError('No provider specified')

        badgr_app = BadgrApp.objects.get_current(request=self.request)
        if badgr_app is not None:
            set_session_badgr_app(self.request, badgr_app)
        else:
            raise ValidationError('Unable to save BadgrApp in session')

        try:
            redirect_url = reverse('{}_login'.format(self.request.GET.get('provider')))
        except NoReverseMatch:
            raise ValidationError('No {} provider found'.format(provider_name))
        authcode = self.request.GET.get('authCode', None)
        if authcode is not None:
            set_session_authcode(self.request, authcode)
            return set_url_query_params(redirect_url, process=AuthProcess.CONNECT)
        else:
            validate_name = self.request.GET.get('validateName', "false").lower() == "true"
            return set_url_query_params(redirect_url, validateName=validate_name)
Пример #11
0
    def get_redirect_url(self):
        provider_name = self.request.GET.get('provider', None)
        if provider_name is None:
            raise ValidationError('No provider specified')

        badgr_app = BadgrApp.objects.get_current(request=self.request)
        if badgr_app is not None:
            set_session_badgr_app(self.request, badgr_app)
        else:
            raise ValidationError('Unable to save BadgrApp in session')

        try:
            redirect_url = reverse('{}_login'.format(self.request.GET.get('provider')))
        except NoReverseMatch:
            raise ValidationError('No {} provider found'.format(provider_name))

        authcode = self.request.GET.get('authCode', None)
        if authcode is not None:
            set_session_authcode(self.request, authcode)
            return set_url_query_params(redirect_url, process=AuthProcess.CONNECT)
        else:
            return redirect_url
Пример #12
0
def callback(request):
    """
        Callback page, after user returns from "Where are you from" page.

        Steps:
        1. Exchange callback Token for id token
        2. Decode id_token with the user info in the claims
        3. Complete social login and return to frontend

        Retrieved information:
        - email: required, if not available will fail request
        - sub: required, string, user code
        - given_name: optional, string
        - family_name: optional, string
        - schac_home_organization: required

    :return: Either renders authentication error, or completes the social login
    """
    # extract the state of the redirect
    if request.user.is_authenticated:
        get_account_adapter(request).logout(request)  # logging in while being authenticated breaks the login procedure

    process, auth_token, badgr_app_pk, lti_data, lti_user_id, lti_roles, referer = json.loads(request.GET.get('state'))

    code = request.GET.get('code', None)
    if code is None:
        error = 'Server error: No userToken found in callback'
        return render_authentication_error(request, SurfConextProvider.id, error=error)

    # 1. Exchange callback Token for id token
    _current_app = SocialApp.objects.get_current(provider='surf_conext')
    payload = {
        "grant_type": "authorization_code",
        "redirect_uri": f"{settings.HTTP_ORIGIN}/account/openid/login/callback/",
        "code": code,
        "scope": "openid",
        "client_id": _current_app.client_id,
        "client_secret": _current_app.secret,
    }
    headers = {'Content-Type': "application/x-www-form-urlencoded",
               'Cache-Control': "no-cache"
               }
    response = requests.post(f"{settings.SURFCONEXT_DOMAIN_URL}/token", data=urllib.parse.urlencode(payload),
                             headers=headers)

    if response.status_code != 200:
        error = 'Server error: Token endpoint error (http %s) try alternative login methods' % response.status_code
        return render_authentication_error(request, SurfConextProvider.id, error=error)

    data = response.json()
    id_token = data.get('id_token', None)

    if id_token is None:
        error = 'Server error: No id_token token, try alternative login methods.'
        return render_authentication_error(request, SurfConextProvider.id, error=error)

    badgr_app = BadgrApp.objects.get(pk=badgr_app_pk)
    set_session_badgr_app(request, BadgrApp.objects.get(pk=badgr_app.pk))

    payload = jwt.get_unverified_claims(id_token)
    for attr in ['sub', 'email', 'schac_home_organization']:
        if attr not in payload:
            error = f"Sorry, your account does not have a {attr} attribute. Login with SURFconext and then try again"
            logger.error(error)
            return render_authentication_error(request, SurfConextProvider.id, error)

    # 3. Complete social login and return to frontend
    provider = SurfConextProvider(request)
    login = provider.sociallogin_from_response(request, payload)
    # connect process in which OpenID connects with, either login or connect, if you connect then login user with token
    login.state = {'process': process}
    # login for connect because socialLogin can only connect to request.user
    if process == 'connect' and request.user.is_anonymous and auth_token:
        request.user = get_verified_user(auth_token=auth_token)
    ret = complete_social_login(request, login)
    new_url = ret.url+'&role=teacher'
    ret = HttpResponseRedirect(new_url)

    if not request.user.is_anonymous:  # the social login succeeded
        institution_identifier = payload['schac_home_organization']
        try:
            institution = Institution.objects.get(identifier=institution_identifier)
            # the institution exists
            if request.user.invited:  # this user has been invited in the past
                provisions = request.user.match_provisionments()
                for provision in provisions:
                    provision.perform_provisioning()
            else:  # user has not been invited, check for invitations
                request.user.institution = institution
                request.user.is_teacher = True
                request.user.invited = True
                try:
                    provisionment = request.user.match_provisionments().get()  # get the initial provisioning for the first login, there can only be one
                    request.user.save()
                    provisionment.match_user(request.user)
                    provisionment.perform_provisioning()
                except (UserProvisionment.DoesNotExist, BadgrValidationError):  # there is no provisionment
                    extra_context = {}
                    if institution and institution.cached_staff():
                        cached_staff = institution.cached_staff()
                        admins = list(filter(lambda u: u.may_administrate_users, cached_staff))
                        if len(admins) > 0:
                            extra_context["admin_email"] = admins[0].user.email

                    error = 'Sorry, you can not register without an invite.'
                    extra_context["code"] = AuthErrorCode.REGISTER_WITHOUT_INVITE
                    if request.user.date_joined.today().date() == datetime.datetime.today().date():  # extra protection before deletion
                        request.user.delete()
                    return render_authentication_error(request, SurfConextProvider.id, error,
                                                       extra_context=extra_context)

        except Institution.DoesNotExist:  # no institution yet, and therefore also first login ever
            error = 'Sorry, your institution has not been created yet.'
            return render_authentication_error(request, SurfConextProvider.id, error,
                                               extra_context={"code": AuthErrorCode.REGISTER_WITHOUT_INVITE})

    request.user.accept_general_terms()

    lti_data = request.session.get('lti_data', None)
    if lti_data is not None and 'lti_user_id' in lti_data:
        if not request.user.is_anonymous:
            tenant = LTITenant.objects.get(client_key=lti_data['lti_tenant'])
            badgeuser_tennant, _ = LtiBadgeUserTennant.objects.get_or_create(lti_user_id=lti_data['lti_user_id'],
                                                                             badge_user=request.user,
                                                                             lti_tennant=tenant,
                                                                             staff=True)
            user_current_context_id, _ = UserCurrentContextId.objects.get_or_create(badge_user=request.user)
            user_current_context_id.context_id = lti_data['lti_context_id']
            user_current_context_id.save()

    request.session['lti_user_id'] = lti_user_id
    request.session['lti_roles'] = lti_roles
    # override the response with a redirect to staff dashboard if the login came from there
    if referer == 'staff':
        return HttpResponseRedirect(reverse('admin:index'))
    return ret
Пример #13
0
def after_terms_agreement(request, **kwargs):
    access_token = kwargs.get('access_token', None)
    if not access_token:
        error = 'Sorry, we could not find you SURFconext credentials.'
        return render_authentication_error(request, SurfConextProvider.id,
                                           error)

    headers = {'Authorization': 'bearer %s' % access_token}
    badgr_app_pk, login_type, process, auth_token, lti_context_id, lti_user_id, lti_roles, referer = json.loads(
        kwargs['state'])
    try:
        badgr_app_pk = int(badgr_app_pk)
    except:
        badgr_app_pk = settings.BADGR_APP_ID
    set_session_badgr_app(request, BadgrApp.objects.get(pk=badgr_app_pk))

    lti_data = request.session.get('lti_data', None)
    url = settings.SURFCONEXT_DOMAIN_URL + '/userinfo'

    response = requests.get(url, headers=headers)
    if response.status_code != 200:
        error = 'Server error: User info endpoint error (http %s). Try alternative login methods' % response.status_code
        return render_authentication_error(request,
                                           SurfConextProvider.id,
                                           error=error)

    # retrieved data in fields and ensure that email & sud are in extra_data
    extra_data = response.json()

    if 'email' not in extra_data or 'sub' not in extra_data:
        error = 'Sorry, your account has no email attached from SURFconext, try another login method.'
        return render_authentication_error(request, SurfConextProvider.id,
                                           error)
    if "schac_home_organization" not in extra_data:
        error = 'Sorry, your account has no home organization attached from SURFconext, try another login method.'
        return render_authentication_error(request, SurfConextProvider.id,
                                           error)

    if 'family_name' in extra_data:
        extra_data['family_name'] = ''
    if 'given_name' in extra_data:
        extra_data['given_name'] = ''

    # 3. Complete social login and return to frontend
    provider = SurfConextProvider(request)
    login = provider.sociallogin_from_response(request, extra_data)

    # Reset the badgr app id after login as django overturns it

    # connect process in which OpenID connects with, either login or connect, if you connect then login user with token
    login.state = {'process': process}

    # login for connect because socialLogin can only connect to request.user
    if process == 'connect' and request.user.is_anonymous() and auth_token:
        request.user = get_verified_user(auth_token=auth_token)

    ret = complete_social_login(request, login)
    if not request.user.is_anonymous():  # the social login succeeded
        institution_name = extra_data['schac_home_organization']
        institution, created = Institution.objects.get_or_create(
            name=institution_name)
        request.user.institution = institution
        request.user.save()
    badgr_app = BadgrApp.objects.get(pk=badgr_app_pk)

    resign = True
    check_agreed_term_and_conditions(request.user, badgr_app, resign=resign)

    if lti_data is not None and 'lti_user_id' in lti_data:
        if not request.user.is_anonymous():
            tenant = LTITenant.objects.get(client_key=lti_data['lti_tenant'])
            badgeuser_tennant, _ = LtiBadgeUserTennant.objects.get_or_create(
                lti_user_id=lti_data['lti_user_id'],
                badge_user=request.user,
                lti_tennant=tenant,
                staff=True)
            user_current_context_id, _ = UserCurrentContextId.objects.get_or_create(
                badge_user=request.user)
            user_current_context_id.context_id = lti_data['lti_context_id']
            user_current_context_id.save()

    request.session['lti_user_id'] = lti_user_id
    request.session['lti_roles'] = lti_roles
    if not request.user.is_authenticated:
        print(request.__dict__)
        a = 1
    # override the response with a redirect to staff dashboard if the login came from there
    if referer == 'staff':
        return HttpResponseRedirect(reverse('admin:index'))
    else:
        return ret
Пример #14
0
def after_terms_agreement(request, **kwargs):
    '''
    this is the second part of the callback, after consent has been given, or is user already exists
    '''
    badgr_app_pk, login_type, lti_context_id, lti_user_id, lti_roles, referer = json.loads(
        kwargs['state'])
    lti_data = request.session.get('lti_data', None)
    try:
        badgr_app_pk = int(badgr_app_pk)
    except:
        badgr_app_pk = settings.BADGR_APP_ID

    badgr_app = BadgrApp.objects.get(pk=badgr_app_pk)
    set_session_badgr_app(request, badgr_app)

    id_token = kwargs.get('id_token', None)
    if not id_token:
        error = 'Sorry, we could not find your eduID credentials.'
        return render_authentication_error(request, EduIDProvider.id, error)
    payload = jwt.get_unverified_claims(id_token)

    logger.info(
        f"Using payload attribute {settings.EDUID_IDENTIFIER} for unique identifier"
    )

    social_account = get_social_account(payload[settings.EDUID_IDENTIFIER])
    if not social_account:  # user does not exist
        # Fail fast if not all required attribbutes are there
        for attr in [
                settings.EDUID_IDENTIFIER, 'email', 'family_name', 'given_name'
        ]:
            if attr not in payload:
                error = f"Sorry, your eduID account does not have a {attr} attribute. Login to eduID and then try again"
                logger.error(error)
                return render_authentication_error(request, EduIDProvider.id,
                                                   error)
    else:  # user already exists
        update_user_params(social_account.user, payload)

    # 3. Complete social login

    provider = EduIDProvider(request)
    login = provider.sociallogin_from_response(request, payload)

    ret = complete_social_login(request, login)
    new_url = ret.url + '&role=student'
    ret = HttpResponseRedirect(new_url)

    set_session_badgr_app(request, badgr_app)

    request.user.accept_general_terms()

    logger.info(f"payload from surfconext {json.dumps(payload)}")

    if "acr" in payload and payload[
            "acr"] == "https://eduid.nl/trust/validate-names":
        request.user.validated_name = f"{payload['given_name']} {payload['family_name']}"
        logger.info(
            f"Stored validated name {payload['given_name']} {payload['family_name']}"
        )

    access_token = kwargs.get('access_token', None)
    headers = {
        "Accept": "application/json, application/json;charset=UTF-8",
        "Authorization": f"Bearer {access_token}"
    }
    response = requests.get(
        f"{settings.EDUID_API_BASE_URL}/myconext/api/eduid/eppn",
        headers=headers)
    if response.status_code != 200:
        error = f"Server error: eduID eppn endpoint error ({response.status_code})"
        logger.debug(error)
        return render_authentication_error(request,
                                           EduIDProvider.id,
                                           error=error)
    eppn_json = response.json()
    request.user.clear_affiliations()
    for info in eppn_json:
        request.user.add_affiliations([{
            'eppn':
            info["eppn"],
            'schac_home':
            info["schac_home_organization"]
        }])
        logger.info(
            f"Stored affiliations {info['eppn']} {info['schac_home_organization']}"
        )
    request.user.save()

    # create lti_connection
    if lti_data is not None and 'lti_user_id' in lti_data:
        if not request.user.is_anonymous:
            tenant = LTITenant.objects.get(client_key=lti_data['lti_tenant'])
            badgeuser_tennant, _ = LtiBadgeUserTennant.objects.get_or_create(
                lti_user_id=lti_data['lti_user_id'],
                badge_user=request.user,
                lti_tennant=tenant,
                staff=False)
            user_current_context_id, _ = UserCurrentContextId.objects.get_or_create(
                badge_user=request.user)
            user_current_context_id.context_id = lti_data['lti_context_id']
            user_current_context_id.save()
    request.session['lti_user_id'] = lti_user_id
    request.session['lti_roles'] = lti_roles

    if not social_account:
        # Create an eduIDBadge
        create_edu_id_badge_instance(login)

    return ret
Пример #15
0
def after_terms_agreement(request, **kwargs):
    '''
    this is the second part of the callback, after consent has been given, or is user already exists
    '''
    badgr_app_pk, login_type, lti_context_id, lti_user_id, lti_roles, referer = json.loads(
        kwargs['state'])
    lti_data = request.session.get('lti_data', None)
    try:
        badgr_app_pk = int(badgr_app_pk)
    except:
        badgr_app_pk = settings.BADGR_APP_ID

    badgr_app = BadgrApp.objects.get(pk=badgr_app_pk)
    set_session_badgr_app(request, badgr_app)

    id_token = kwargs.get('id_token', None)
    if not id_token:
        error = 'Sorry, we could not find your eduID credentials.'
        return render_authentication_error(request, EduIDProvider.id, error)
    payload = jwt.get_unverified_claims(id_token)

    social_account = get_social_account(payload['sub'])
    if not social_account:  # user does not exist
        # ensure that email & names are in extra_data
        if 'email' not in payload:
            error = 'Sorry, your eduID account does not have your institution mail. Login to eduID and link your institution account, then try again.'
            logger.error(error)
            return render_authentication_error(request, EduIDProvider.id,
                                               error)
        if 'family_name' not in payload:
            error = 'Sorry, your eduID account has no family_name attached from SURFconext. Login to eduID and link your institution account, then try again.'
            logger.error(error)
            return render_authentication_error(request, EduIDProvider.id,
                                               error)
        if 'given_name' not in payload:
            error = 'Sorry, your eduID account has no first_name attached from SURFconext. Login to eduID and link your institution account, then try again.'
            logger.error(error)
            return render_authentication_error(request, EduIDProvider.id,
                                               error)
    else:  # user already exists
        update_user_params(social_account.user, payload)

    # 3. Complete social login

    provider = EduIDProvider(request)
    login = provider.sociallogin_from_response(request, payload)

    ret = complete_social_login(request, login)
    new_url = ret.url + '&role=student'
    ret = HttpResponseRedirect(new_url)

    set_session_badgr_app(request, badgr_app)

    request.user.accept_general_terms()

    logger.info(f"payload from surfconext {json.dumps(payload)}")

    if "acr" in payload and payload[
            "acr"] == "https://eduid.nl/trust/validate-names":
        request.user.validated_name = f"{payload['given_name']} {payload['family_name']}"
        request.user.save()
        logger.info(
            f"Stored validated name {payload['given_name']} {payload['family_name']}"
        )

    # create lti_connection
    if lti_data is not None and 'lti_user_id' in lti_data:
        if not request.user.is_anonymous:
            tenant = LTITenant.objects.get(client_key=lti_data['lti_tenant'])
            badgeuser_tennant, _ = LtiBadgeUserTennant.objects.get_or_create(
                lti_user_id=lti_data['lti_user_id'],
                badge_user=request.user,
                lti_tennant=tenant,
                staff=False)
            user_current_context_id, _ = UserCurrentContextId.objects.get_or_create(
                badge_user=request.user)
            user_current_context_id.context_id = lti_data['lti_context_id']
            user_current_context_id.save()
    request.session['lti_user_id'] = lti_user_id
    request.session['lti_roles'] = lti_roles

    if not social_account:
        # Create an eduIDBadge
        create_edu_id_badge_instance(login)

    return ret
Пример #16
0
def callback(request):
    """
        Callback page, after user returns from "Where are you from" page.
        Due to limited scope support (without tokenized user information) the OpenID workflow is extended.

        Steps:
        1. Exchange callback Token for access token
        2. Retrieve user information with the access token
        3. Complete social login and return to frontend

        Retrieved information:
        - email: Obligated, if not available will fail request
        - sub: optional, string, user code
        - given_name: optional, string
        - family_name: optional, string
        - edu_person_targeted_id: optional
        - schac_home_organization: optional

    :return: Either renders authentication error, or completes the social login
    """
    # extract the state of the redirect
    if request.user.is_authenticated:
        get_account_adapter(request).logout(
            request
        )  # logging in while being authenticated breaks the login procedure

    process, auth_token, badgr_app_pk, lti_data, lti_user_id, lti_roles, referer = json.loads(
        request.GET.get('state'))

    if badgr_app_pk is None:
        print('none here')
    # check if code is given

    code = request.GET.get('code', None)
    if code is None:
        error = 'Server error: No userToken found in callback'
        return render_authentication_error(request,
                                           SurfConextProvider.id,
                                           error=error)

    # 1. Exchange callback Token for access token
    _current_app = SocialApp.objects.get_current(provider='surf_conext')
    data = {
        'redirect_uri':
        '%s/account/openid/login/callback/' % settings.HTTP_ORIGIN,
        'client_id': _current_app.client_id,
        'client_secret': _current_app.secret,
        'scope': 'openid',
        'grant_type': 'authorization_code',
        'code': code
    }

    url = settings.SURFCONEXT_DOMAIN_URL + '/token?%s' % (
        urllib.parse.urlencode(data))

    response = requests.post(url)

    if response.status_code != 200:
        error = 'Server error: Token endpoint error (http %s) try alternative login methods' % response.status_code
        return render_authentication_error(request,
                                           SurfConextProvider.id,
                                           error=error)

    data = response.json()
    access_token = data.get('access_token', None)
    if access_token is None:
        error = 'Server error: No access token, try alternative login methods.'
        return render_authentication_error(request,
                                           SurfConextProvider.id,
                                           error=error)

    # 2. Retrieve user information with the access token
    headers = {'Authorization': 'bearer %s' % data['access_token']}

    url = settings.SURFCONEXT_DOMAIN_URL + '/userinfo'

    response = requests.get(url, headers=headers)
    if response.status_code != 200:
        error = 'Server error: User info endpoint error (http %s). Try alternative login methods' % response.status_code
        return render_authentication_error(request,
                                           SurfConextProvider.id,
                                           error=error)

    # retrieved data in fields and ensure that email & sud are in extra_data
    extra_data = response.json()

    keyword_arguments = {
        'access_token':
        access_token,
        'state':
        json.dumps([
            badgr_app_pk, 'surf_conext', process, auth_token, lti_data,
            lti_user_id, lti_roles, referer
        ]),
        'after_terms_agreement_url_name':
        'surf_conext_terms_accepted_callback'
    }

    if not get_social_account(extra_data['sub']):
        return HttpResponseRedirect(
            reverse('accept_terms', kwargs=keyword_arguments))
    social_account = get_social_account(extra_data['sub'])
    badgr_app = BadgrApp.objects.get(pk=badgr_app_pk)

    set_session_badgr_app(request, BadgrApp.objects.get(pk=badgr_app.pk))
    if 'edu_person_affiliations' in extra_data:
        if not ('employee' in extra_data['edu_person_affiliations']
                or 'faculty' in extra_data['edu_person_affiliations']):
            error = 'Must be employee or faculty member to login. If You are a student, please login with EduID'
            return render_authentication_error(request, SurfConextProvider.id,
                                               error)

    if not check_agreed_term_and_conditions(social_account.user, badgr_app):
        return HttpResponseRedirect(
            reverse('accept_terms_resign', kwargs=keyword_arguments))

    return after_terms_agreement(request, **keyword_arguments)
Пример #17
0
def after_terms_agreement(request, **kwargs):
    '''
    this is the second part of the callback, after consent has been given, or is user already exists
    '''
    badgr_app_pk, login_type, lti_context_id, lti_user_id, lti_roles, referer = json.loads(
        kwargs['state'])
    lti_data = request.session.get('lti_data', None)

    badgr_app = BadgrApp.objects.get(pk=badgr_app_pk)
    set_session_badgr_app(request, badgr_app)

    access_token = kwargs.get('access_token', None)
    if not access_token:
        error = 'Sorry, we could not find your eduID credentials.'
        return render_authentication_error(request, EduIDProvider.id, error)

    headers = {"Authorization": "Bearer " + access_token}
    response = requests.get("{}/userinfo".format(settings.EDUID_PROVIDER_URL),
                            headers=headers)
    if response.status_code != 200:
        error = 'Server error: User info endpoint error (http %s). Try alternative login methods' % response.status_code
        logger.debug(error)
        return render_authentication_error(request,
                                           EduIDProvider.id,
                                           error=error)
    userinfo_json = response.json()

    if 'sub' not in userinfo_json:
        error = 'Sorry, your eduID account has no identifier.'
        logger.debug(error)
        return render_authentication_error(request, EduIDProvider.id, error)

    social_account = get_social_account(userinfo_json['sub'])
    if not social_account:  # user does not exist
        # ensure that email & names are in extra_data
        if 'email' not in userinfo_json:
            error = 'Sorry, your eduID account does not have your institution mail. Login to eduID and link your institution account, then try again.'
            logger.debug(error)
            return render_authentication_error(request, EduIDProvider.id,
                                               error)
        if 'family_name' not in userinfo_json:
            error = 'Sorry, your eduID account has no family_name attached from SURFconext. Login to eduID and link your institution account, then try again.'
            logger.debug(error)
            return render_authentication_error(request, EduIDProvider.id,
                                               error)
        if 'given_name' not in userinfo_json:
            error = 'Sorry, your eduID account has no first_name attached from SURFconext. Login to eduID and link your institution account, then try again.'
            logger.debug(error)
            return render_authentication_error(request, EduIDProvider.id,
                                               error)
    else:  # user already exists
        update_user_params(social_account.user, userinfo_json)

    # 3. Complete social login

    provider = EduIDProvider(request)
    login = provider.sociallogin_from_response(request, userinfo_json)

    ret = complete_social_login(request, login)
    set_session_badgr_app(request, badgr_app)
    resign = True
    check_agreed_term_and_conditions(request.user, badgr_app, resign=resign)

    #create lti_connection
    if lti_data is not None and 'lti_user_id' in lti_data:
        if not request.user.is_anonymous():
            tenant = LTITenant.objects.get(client_key=lti_data['lti_tenant'])
            badgeuser_tennant, _ = LtiBadgeUserTennant.objects.get_or_create(
                lti_user_id=lti_data['lti_user_id'],
                badge_user=request.user,
                lti_tennant=tenant,
                staff=False)
            user_current_context_id, _ = UserCurrentContextId.objects.get_or_create(
                badge_user=request.user)
            user_current_context_id.context_id = lti_data['lti_context_id']
            user_current_context_id.save()
    request.session['lti_user_id'] = lti_user_id
    request.session['lti_roles'] = lti_roles

    # 4. Return the user to where she came from (ie the referer: public enrollment or main page)
    if 'public' in referer:
        if 'badges' in referer:
            badgeclass_slug = referer[-1]
            if badgeclass_slug:
                edu_id = userinfo_json['sub']
                enrolled = enroll_student(request.user, edu_id,
                                          badgeclass_slug)
        url = ret.url + '&public=true' + '&badgeclassSlug=' + badgeclass_slug + '&enrollmentStatus=' + enrolled
        return HttpResponseRedirect(url)
    else:
        return ret