예제 #1
0
def shibcallback(request):
    # Callback from Shib login. Get ALL the meta!
    appdata_signed = request.GET.get("appdata", None)
    if not appdata_signed:
        response = PrettyJsonResponse({
            "ok":
            False,
            "error": ("No signed app data returned from Shibboleth."
                      " Please use the authorise endpoint.")
        })
        response.status_code = 400
        return response

    signer = TimestampSigner()
    try:
        # Expire our signed tokens after five minutes for added security
        appdata = signer.unsign(appdata_signed, max_age=300)
    except signing.SignatureExpired:
        response = PrettyJsonResponse({
            "ok":
            False,
            "error": ("Login data has expired. Please attempt to log in "
                      "again. If the issues persist please contact the "
                      "UCL API Team to rectify this.")
        })
        response.status_code = 400
        return response
    except signing.BadSignature:
        response = PrettyJsonResponse({
            "ok":
            False,
            "error": ("Bad signature. Please attempt to log in again. "
                      "If the issues persist please contact the UCL API "
                      "Team to rectify this.")
        })
        response.status_code = 400
        return response

    client_id = appdata[:33]
    state = appdata[33:]

    # We can trust this value because it was extracted from the signed data
    # string sent via Shibboleth
    app = App.objects.get(client_id=client_id)

    # Sometimes UCL doesn't give us the expected headers.
    # If a critical header is missing we error out.
    # If non-critical headers are missing we simply put a placeholder string.
    try:
        # This is used to find the correct user
        eppn = request.META['HTTP_EPPN']
        # We don't really use cn but because it's unique in the DB we can't
        # really put a place holder value.
        cn = request.META['HTTP_CN']
        # (aka UPI), also unique in the DB
        employee_id = request.META['HTTP_EMPLOYEEID']
    except KeyError:
        response = PrettyJsonResponse({
            "ok":
            False,
            "error": ("UCL has sent incomplete headers. If the issues persist"
                      "please contact the UCL API Team to rectify this.")
        })
        response.status_code = 400
        return response

    # TODO: Ask UCL what on earth are they doing by missing out headers, and
    # remind them we need to to be informed of these types of changes.
    # TODO: log to sentry that fields were missing...
    department = request.META.get('HTTP_DEPARTMENT', '')
    given_name = request.META.get('HTTP_GIVENNAME', '')
    display_name = request.META.get('HTTP_DISPLAYNAME', '')
    groups = request.META.get('HTTP_UCLINTRANETGROUPS', '')

    # We check whether the user is a member of any UCL Intranet Groups.
    # This is a quick litmus test to determine whether they should be able to
    # use an OAuth application.
    # We deny access to alumni, which does not have this Shibboleth attribute.
    # Test accounts also do not have this attribute, but we can check the
    # department attribute for the Shibtests department.
    # This lets App Store reviewers log in to apps that use the UCL API.
    if not groups:
        if department == "Shibtests" or eppn == SHIB_TEST_USER:
            groups = "shibtests"
        else:
            response = HttpResponse(
                ("Error 403 - denied. <br>"
                 "Unfortunately, alumni are not permitted to use UCL Apps."))
            response.status_code = 403
            return response

    # If a user has never used the API before then we need to sign them up
    try:
        # TODO: Handle MultipleObjectsReturned exception.
        # email field isn't unique at database level (on our side).
        # Alternatively, switch to employee_id (which is unique).
        user = User.objects.get(email=eppn)
    except User.DoesNotExist:
        # create a new user
        user = User(email=eppn,
                    full_name=display_name,
                    given_name=given_name,
                    department=department,
                    cn=cn,
                    raw_intranet_groups=groups,
                    employee_id=employee_id)

        user.save()
    else:
        # User exists already, so update the values if new ones are non-empty.
        user = User.objects.get(email=eppn)
        user.employee_id = employee_id
        if display_name:
            user.full_name = display_name
        if given_name:
            user.given_name = given_name
        if department:
            user.department = department
        if groups:
            user.raw_intranet_groups = groups
        user.save()

    # Log the user into the system using their User ID
    request.session["user_id"] = user.id

    signer = TimestampSigner()
    response_data = {
        "client_id": app.client_id,
        "state": state,
        "user_upi": user.employee_id
    }

    response_data_str = json.dumps(response_data, cls=DjangoJSONEncoder)
    response_data_signed = signer.sign(response_data_str)

    s = Scopes()

    page_data = {
        "app_name": app.name,
        "creator": app.user.full_name,
        "client_id": app.client_id,
        "state": state,
        "scopes": s.scope_dict(app.scope.scope_number),
        "user": {
            "full_name": user.full_name,
            "cn": user.cn,
            "email": user.email,
            "department": user.department,
            "upi": user.employee_id
        },
        "signed_data": response_data_signed
    }

    initial_data = json.dumps(page_data, cls=DjangoJSONEncoder)
    return render(request, 'permissions.html', {'initial_data': initial_data})
예제 #2
0
def shibcallback(request):
    # Callback from Shib login. Get ALL the meta!
    appdata_signed = request.GET.get("appdata", None)
    if not appdata_signed:
        response = PrettyJsonResponse({
            "ok":
            False,
            "error": ("No signed app data returned from Shibboleth."
                      " Please use the authorise endpoint.")
        })
        response.status_code = 400
        return response

    signer = TimestampSigner()
    try:
        # Expire our signed tokens after five minutes for added security
        appdata = signer.unsign(appdata_signed, max_age=300)
    except signing.SignatureExpired:
        response = PrettyJsonResponse({
            "ok":
            False,
            "error": ("Login data has expired. Please attempt to log in "
                      "again. If the issues persist please contact the "
                      "UCL API Team to rectify this.")
        })
        response.status_code = 400
        return response
    except signing.BadSignature:
        response = PrettyJsonResponse({
            "ok":
            False,
            "error": ("Bad signature. Please attempt to log in again. "
                      "If the issues persist please contact the UCL API "
                      "Team to rectify this.")
        })
        response.status_code = 400
        return response

    client_id = appdata[:33]
    state = appdata[33:]

    # We can trust this value because it was extracted from the signed data
    # string sent via Shibboleth
    app = App.objects.get(client_id=client_id)

    eppn = request.META['HTTP_EPPN']
    groups = request.META['HTTP_UCLINTRANETGROUPS']
    cn = request.META['HTTP_CN']
    department = request.META['HTTP_DEPARTMENT']
    given_name = request.META['HTTP_GIVENNAME']
    display_name = request.META['HTTP_DISPLAYNAME']
    employee_id = request.META['HTTP_EMPLOYEEID']

    # If a user has never used the API before then we need to sign them up
    try:
        user = User.objects.get(email=eppn)
    except User.DoesNotExist:
        # create a new user
        user = User(email=eppn,
                    full_name=display_name,
                    given_name=given_name,
                    department=department,
                    cn=cn,
                    raw_intranet_groups=groups,
                    employee_id=employee_id)

        user.save()
        keen_add_event.delay("signup", {
            "id": user.id,
            "email": eppn,
            "name": display_name
        })
    else:
        # User exists already, so update the values
        user = User.objects.get(email=eppn)
        user.full_name = display_name
        user.given_name = given_name
        user.department = department
        user.raw_intranet_groups = groups
        user.employee_id = employee_id
        user.save()

        keen_add_event.delay("User data updated", {
            "id": user.id,
            "email": eppn,
            "name": display_name
        })

    # Log the user into the system using their User ID
    request.session["user_id"] = user.id

    signer = TimestampSigner()
    response_data = {
        "client_id": app.client_id,
        "state": state,
        "user_upi": user.employee_id
    }

    response_data_str = json.dumps(response_data, cls=DjangoJSONEncoder)
    response_data_signed = signer.sign(response_data_str)

    s = Scopes()

    page_data = {
        "app_name": app.name,
        "creator": app.user.full_name,
        "client_id": app.client_id,
        "state": state,
        "scopes": s.scope_dict(app.scope.scope_number),
        "user": {
            "full_name": user.full_name,
            "cn": user.cn,
            "email": user.email,
            "department": user.department,
            "upi": user.employee_id
        },
        "signed_data": response_data_signed
    }

    initial_data = json.dumps(page_data, cls=DjangoJSONEncoder)
    return render(request, 'permissions.html', {'initial_data': initial_data})