예제 #1
0
def check_external_app(app, request=None):
    '''
    Checks possible connection with any application server
    '''

    try:
        url = get_external_app_url(app, request) + '/' + settings.TOKEN_URL
        headers = get_external_app_auth_header(app)
    except KeyError:
        logger.warning(MSG_EXTERNAL_APP_ERR.format(app=app))
        return False

    try:
        # check that the server is up
        h = exec_request(method='head', url=url)
        assert h.status_code == 403  # expected response 403 Forbidden
        logger.info(MSG_EXTERNAL_APP_UP.format(app=app, url=url))

        try:
            # check that the token is valid
            g = exec_request(method='get', url=url, headers=headers)
            g.raise_for_status()  # expected response 200 OK
            logger.info(MSG_EXTERNAL_APP_TOKEN_OK.format(app=app, url=url))

            return True  # it's possible to connect with server :D

        except Exception:
            logger.warning(MSG_EXTERNAL_APP_TOKEN_ERR.format(app=app, url=url))
    except Exception:
        logger.warning(MSG_EXTERNAL_APP_SERVER_ERR.format(app=app, url=url))

    return False  # it's not possible to connect with server :(
예제 #2
0
def check_realm(realm):
    '''
    Checks if the realm name is valid visiting its keycloak server login page.
    '''

    response = exec_request(method='head', url=f'{_KC_URL}/{realm}/account')
    response.raise_for_status()
예제 #3
0
def _get_user_info(realm, token):
    response = exec_request(
        method='get',
        url=f'{_KC_URL}/{realm}/{_KC_OID_URL}/userinfo',
        headers={'Authorization': f'Bearer {token}'},
    )
    response.raise_for_status()

    return response.json()
예제 #4
0
def _user_logged_out(sender, user, request, **kwargs):
    '''
    Removes realm and token from session also logs out from keycloak server
    making the user token invalid.
    '''

    token = request.session.get(_KC_TOKEN_SESSION)
    realm = get_current_realm(request, default_realm=None)
    if token and realm:
        # logout
        exec_request(
            method='post',
            url=f'{_KC_URL}/{realm}/{_KC_OID_URL}/logout',
            data={
                'client_id': settings.KEYCLOAK_CLIENT_ID,
                'refresh_token': token['refresh_token'],
            },
        )
예제 #5
0
def refresh_kc_token(realm, token):
    return exec_request(
        method='post',
        url=f'{_KC_URL}/{realm}/{_KC_OID_URL}/token',
        data={
            'grant_type': 'refresh_token',
            'client_id': settings.KEYCLOAK_CLIENT_ID,
            'refresh_token': token['refresh_token'],
        },
    )
예제 #6
0
def _authenticate(realm, data):
    # get user token from the returned "code"
    response = exec_request(
        method='post',
        url=f'{_KC_URL}/{realm}/{_KC_OID_URL}/token',
        data=data,
    )
    response.raise_for_status()

    token = response.json()
    userinfo = _get_user_info(realm, token['access_token'])
    return token, userinfo
예제 #7
0
    def _handle(self, request):
        def _valid_header(name):
            '''
            Validates if the header can be passed within the request headers.
            '''

            # bugfix: We need to remove the "Host" from the header
            # since the request goes to another host, otherwise
            # the webserver returns a 404 because the domain is
            # not hosted on that server. The webserver
            # should add the correct Host based on the request.
            # This problem might not be exposed running on localhost

            return (name in settings.EXPOSE_HEADERS_WHITELIST
                    or (name.startswith('CSRF_')
                        and name not in ['CSRF_COOKIE_USED']) or
                    (name.startswith('HTTP_') and name not in ['HTTP_HOST']))

        def _get_method(request):
            # Fixes:
            # django.http.request.RawPostDataException:
            #     You cannot access body after reading from request's data stream
            #
            # Django does not read twice the `request.body` on `POST` calls:
            # but it was already read while checking the CSRF token.
            # This raises an exception in the line below `data=request.body ...`.
            # The Ajax call changed it from `POST` to `PUT`,
            # here it's changed back to its real value.
            #
            # All the conditions are checked to avoid further issues with this workaround.
            if request.method == 'PUT' and request.META.get(
                    'HTTP_X_METHOD', '').upper() == 'POST':
                return 'POST'
            return request.method

        # builds request headers
        headers = {
            normalize_meta_http_name(header): str(value)
            for header, value in request.META.items()
            if _valid_header(header) and str(value)
        }
        headers = add_current_realm_in_headers(request, headers)

        method = _get_method(request)
        logger.debug(f'{method}  {request.external_url}')
        response = exec_request(
            method=method,
            url=request.external_url,
            data=request.body if request.body else None,
            headers=headers,
        )
        if response.status_code == 204:  # NO-CONTENT
            http_response = HttpResponse(status=response.status_code)
        else:
            http_response = HttpResponse(
                content=response,
                status=response.status_code,
                content_type=response.headers.get('Content-Type'),
            )

        # copy the exposed headers from the original response ones
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
        # https://fetch.spec.whatwg.org/#http-access-control-expose-headers
        expose_headers = [
            normalize_meta_http_name(header)
            for header in settings.EXPOSE_HEADERS_WHITELIST
        ] + response.headers.get('Access-Control-Expose-Headers',
                                 '').split(', ')
        for key in expose_headers:
            if key in response.headers:
                http_response[key] = response.headers[key]
        # wildcard
        if '*' in expose_headers:  # include all headers but "Authorization"
            for key in response.headers:
                if key != 'Authorization':
                    http_response[key] = response.headers[key]
        return http_response