Esempio n. 1
0
def callback(request):
    if request.user.is_authenticated:
        get_account_adapter(request).logout(request)  # logging in while being authenticated breaks the login procedure

    current_app = SocialApp.objects.get_current(provider='edu_id')
    #extract state of redirect
    state = json.loads(request.GET.get('state'))
    referer, badgr_app_pk, lti_context_id,lti_user_id,lti_roles = state
    lti_data = request.session.get('lti_data', None);
    code = request.GET.get('code', None)  # access codes to access user info endpoint
    if code is None: #check if code is given
        error = 'Server error: No userToken found in callback'
        logger.debug(error)
        return render_authentication_error(request, EduIDProvider.id, error=error)
    # 1. Exchange callback Token for access token
    payload = {
     "grant_type": "authorization_code",
     "redirect_uri": '%s/account/eduid/login/callback/' % settings.HTTP_ORIGIN,
     "code": code,
     "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("{}/token".format(settings.EDUID_PROVIDER_URL), data=urllib.parse.urlencode(payload), headers=headers)
    token_json = response.json()
    
    # 2. now with access token we can request userinfo
    headers = {"Authorization": "Bearer " + token_json['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()
    
    keyword_arguments = {'access_token':token_json['access_token'], 
                        'state': json.dumps([str(badgr_app_pk), 'edu_id', lti_context_id,lti_user_id,lti_roles]+ [json.loads(referer)]),
                         'after_terms_agreement_url_name': 'eduid_terms_accepted_callback'}

    if not get_social_account(userinfo_json['sub']):
        return HttpResponseRedirect(reverse('accept_terms', kwargs=keyword_arguments))
    social_account =  get_social_account(userinfo_json['sub'])

    badgr_app = BadgrApp.objects.get(pk=badgr_app_pk)
    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)
Esempio n. 2
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
Esempio n. 3
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
    print(request.__dict__)
    print(request.__dict__['session'].__dict__)

    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.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 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)
Esempio n. 4
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
Esempio n. 5
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,
                                           SurfconextAlaProvider.id,
                                           error=error)

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

    # data = {
    #   "issuer": "https://connect.test.surfconext.nl",
    #   "authorization_endpoint": "https://connect.test.surfconext.nl/oidc/authorize",
    #   "userinfo_endpoint": "https://connect.test.surfconext.nl/oidc/userinfo",
    #   "token_endpoint": "https://connect.test.surfconext.nl/oidc/token",
    #   "redirect_uri": '%s/account/openid_ala/login/callback/' % settings.HTTP_ORIGIN,
    #   "guest": {
    #     "client_id": _current_app.client_id,
    #     "client_secret":  _current_app.secret
    #   }
    # }

    url = settings.SURFCONEXT_ALA_DOMAIN_URL + '/oidc/token?%s' % (
        urllib.parse.urlencode(data))
    # url = settings.SURFCONEXT_ALA_DOMAIN_URL + '/oidc/token'
    headers = {'Content-type': 'application/x-www-form-urlencoded'}
    response = requests.post(url, json=data, 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,
                                           SurfconextAlaProvider.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,
                                           SurfconextAlaProvider.id,
                                           error=error)

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

    url = settings.SURFCONEXT_ALA_DOMAIN_URL + '/oidc/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,
                                           SurfconextAlaProvider.id,
                                           error=error)

    # retrieved data in fields and ensure that email & sud are in extra_data
    extra_data = response.json()
    if 'eduperson_entitlement' not in extra_data or \
            extra_data['eduperson_entitlement'][0] != 'urn:mace:eduid.nl:entitlement:verified-by-institution':
        url = settings.ALA_RELYING_URL + "?redirect_uri={}://{}{}".format(
            request.scheme, request.META['HTTP_HOST'],
            reverse('surfconext_ala_login'))
        return HttpResponseRedirect(url)
    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':
        'ala_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, SurfconextAlaProvider.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)