Exemple #1
0
def course_discovery_api_client(user):
    """ Returns a Course Discovery API client setup with authentication for the specified user. """
    course_discovery_client = Client.objects.get(name=CLIENT_NAME)
    return EdxRestApiClient(
        course_discovery_client.url,
        jwt=get_id_token(user, CLIENT_NAME)
    )
Exemple #2
0
 def get(self, request, *args, **kwargs):
     """Generate and return a token, if the integration is enabled."""
     if ProgramsApiConfig.current().is_studio_tab_enabled:
         id_token = get_id_token(request.user, 'programs')
         return JsonResponse({'id_token': id_token})
     else:
         raise Http404
Exemple #3
0
 def get(self, request, *args, **kwargs):
     """Generate and return a token, if the integration is enabled."""
     if ProgramsApiConfig.current().is_studio_tab_enabled:
         id_token = get_id_token(request.user, 'programs')
         return JsonResponse({'id_token': id_token})
     else:
         raise Http404
    def test_get_id_token(self):
        """Verify that ID tokens are signed with the correct secret and generated with the correct claims."""
        token = get_id_token(self.user, self.client_name)

        payload = jwt.decode(
            token,
            self.oauth2_client.client_secret,
            audience=self.oauth2_client.client_id,
            issuer=settings.OAUTH_OIDC_ISSUER,
        )

        now = datetime.datetime.utcnow()
        expiration = now + datetime.timedelta(
            seconds=settings.OAUTH_ID_TOKEN_EXPIRATION)

        expected_payload = {
            'preferred_username': self.user.username,
            'name': self.user_profile.name,
            'email': self.user.email,
            'administrator': self.user.is_staff,
            'iss': settings.OAUTH_OIDC_ISSUER,
            'exp': calendar.timegm(expiration.utctimetuple()),
            'iat': calendar.timegm(now.utctimetuple()),
            'aud': self.oauth2_client.client_id,
            'sub': self.user.id,  # pylint: disable=no-member
        }

        self.assertEqual(payload, expected_payload)
    def test_get_id_token(self):
        """Verify that ID tokens are signed with the correct secret and generated with the correct claims."""
        token = get_id_token(self.user, self.client_name)

        payload = jwt.decode(
            token,
            self.oauth2_client.client_secret,
            audience=self.oauth2_client.client_id,
            issuer=settings.OAUTH_OIDC_ISSUER,
        )

        now = datetime.datetime.utcnow()
        expiration = now + datetime.timedelta(seconds=settings.OAUTH_ID_TOKEN_EXPIRATION)

        expected_payload = {
            'preferred_username': self.user.username,
            'name': self.user_profile.name,
            'email': self.user.email,
            'administrator': self.user.is_staff,
            'iss': settings.OAUTH_OIDC_ISSUER,
            'exp': calendar.timegm(expiration.utctimetuple()),
            'iat': calendar.timegm(now.utctimetuple()),
            'aud': self.oauth2_client.client_id,
            'sub': anonymous_id_for_user(self.user, None),
        }

        self.assertEqual(payload, expected_payload)
Exemple #6
0
def get_edx_api_data(api_config, user, resource,
                     api=None, resource_id=None, querystring=None, cache_key=None):
    """GET data from an edX REST API.

    DRY utility for handling caching and pagination.

    Arguments:
        api_config (ConfigurationModel): The configuration model governing interaction with the API.
        user (User): The user to authenticate as when requesting data.
        resource (str): Name of the API resource being requested.

    Keyword Arguments:
        api (APIClient): API client to use for requesting data.
        resource_id (int or str): Identifies a specific resource to be retrieved.
        querystring (dict): Optional query string parameters.
        cache_key (str): Where to cache retrieved data. The cache will be ignored if this is omitted
            (neither inspected nor updated).

    Returns:
        Data returned by the API. When hitting a list endpoint, extracts "results" (list of dict)
        returned by DRF-powered APIs.
    """
    no_data = []

    if not api_config.enabled:
        log.warning('%s configuration is disabled.', api_config.API_NAME)
        return no_data

    if cache_key:
        cache_key = '{}.{}'.format(cache_key, resource_id) if resource_id else cache_key

        cached = cache.get(cache_key)
        if cached:
            return cached

    try:
        if not api:
            jwt = get_id_token(user, api_config.OAUTH2_CLIENT_NAME)
            api = EdxRestApiClient(api_config.internal_api_url, jwt=jwt)
    except:  # pylint: disable=bare-except
        log.exception('Failed to initialize the %s API client.', api_config.API_NAME)
        return no_data

    try:
        endpoint = getattr(api, resource)
        querystring = querystring if querystring else {}
        response = endpoint(resource_id).get(**querystring)

        if resource_id:
            results = response
        else:
            results = _traverse_pagination(response, endpoint, querystring, no_data)
    except:  # pylint: disable=bare-except
        log.exception('Failed to retrieve data from the %s API.', api_config.API_NAME)
        return no_data

    if cache_key:
        cache.set(cache_key, results, api_config.cache_ttl)

    return results
Exemple #7
0
    def test_get_id_token(self, has_profile):
        """Verify that ID tokens are signed with the correct secret and generated with the correct claims."""
        full_name = UserProfileFactory(
            user=self.user).name if has_profile else None

        token = get_id_token(self.user, self.client_name)

        payload = jwt.decode(
            token,
            self.oauth2_client.client_secret,
            audience=self.oauth2_client.client_id,
            issuer=settings.OAUTH_OIDC_ISSUER,
        )

        now = datetime.datetime.utcnow()
        expiration = now + datetime.timedelta(
            seconds=settings.OAUTH_ID_TOKEN_EXPIRATION)

        expected_payload = {
            'preferred_username': self.user.username,
            'name': full_name,
            'email': self.user.email,
            'administrator': self.user.is_staff,
            'iss': settings.OAUTH_OIDC_ISSUER,
            'exp': calendar.timegm(expiration.utctimetuple()),
            'iat': calendar.timegm(now.utctimetuple()),
            'aud': self.oauth2_client.client_id,
            'sub': anonymous_id_for_user(self.user, None),
        }

        self.assertEqual(payload, expected_payload)
def get_edx_api_data(api_config, user, resource, api=None, resource_id=None, querystring=None, cache_key=None):
    """GET data from an edX REST API.

    DRY utility for handling caching and pagination.

    Arguments:
        api_config (ConfigurationModel): The configuration model governing interaction with the API.
        user (User): The user to authenticate as when requesting data.
        resource (str): Name of the API resource being requested.

    Keyword Arguments:
        api (APIClient): API client to use for requesting data.
        resource_id (int or str): Identifies a specific resource to be retrieved.
        querystring (dict): Optional query string parameters.
        cache_key (str): Where to cache retrieved data. The cache will be ignored if this is omitted
            (neither inspected nor updated).

    Returns:
        Data returned by the API. When hitting a list endpoint, extracts "results" (list of dict)
        returned by DRF-powered APIs.
    """
    no_data = []

    if not api_config.enabled:
        log.warning("%s configuration is disabled.", api_config.API_NAME)
        return no_data

    if cache_key:
        cache_key = "{}.{}".format(cache_key, resource_id) if resource_id else cache_key

        cached = cache.get(cache_key)
        if cached:
            return cached

    try:
        if not api:
            jwt = get_id_token(user, api_config.OAUTH2_CLIENT_NAME)
            api = EdxRestApiClient(api_config.internal_api_url, jwt=jwt)
    except:  # pylint: disable=bare-except
        log.exception("Failed to initialize the %s API client.", api_config.API_NAME)
        return no_data

    try:
        endpoint = getattr(api, resource)
        querystring = querystring if querystring else {}
        response = endpoint(resource_id).get(**querystring)

        if resource_id:
            results = response
        else:
            results = _traverse_pagination(response, endpoint, querystring, no_data)
    except:  # pylint: disable=bare-except
        log.exception("Failed to retrieve data from the %s API.", api_config.API_NAME)
        return no_data

    if cache_key:
        cache.set(cache_key, results, api_config.cache_ttl)

    return results
Exemple #9
0
def course_discovery_api_client(user):
    """ Returns a Course Discovery API client setup with authentication for the specified user. """
    course_discovery_client = Client.objects.get(name=CLIENT_NAME)
    secret_key = helpers.get_value('JWT_AUTH', settings.JWT_AUTH)['JWT_SECRET_KEY']
    return EdxRestApiClient(
        course_discovery_client.url,
        jwt=get_id_token(user, CLIENT_NAME, secret_key=secret_key)
    )
Exemple #10
0
def course_discovery_api_client(user):
    """ Returns a Course Discovery API client setup with authentication for the specified user. """
    course_discovery_client = Client.objects.get(name=CLIENT_NAME)
    secret_key = helpers.get_value('JWT_AUTH',
                                   settings.JWT_AUTH)['JWT_SECRET_KEY']
    return EdxRestApiClient(course_discovery_client.url,
                            jwt=get_id_token(user,
                                             CLIENT_NAME,
                                             secret_key=secret_key))
def get_edx_api_data(api_config, user, resource, querystring=None, cache_key=None):
    """Fetch data from an API using provided API configuration and resource
        name.

    Arguments:
        api_config (ConfigurationModel): The configuration model governing
            interaction with the API.
        user (User): The user to authenticate as when requesting data.
        resource(str): Name of the API resource for which data is being
            requested.
        querystring(dict): Querystring parameters that might be required to
            request data.
        cache_key(str): Where to cache retrieved data. Omitting this will cause the
            cache to be bypassed.

    Returns:
        list of dict, representing data returned by the API.
    """
    no_data = []

    if not api_config.enabled:
        log.warning('%s configuration is disabled.', api_config.API_NAME)
        return no_data

    if cache_key:
        cached = cache.get(cache_key)
        if cached is not None:
            return cached

    try:
        jwt = get_id_token(user, api_config.OAUTH2_CLIENT_NAME)
        api = EdxRestApiClient(api_config.internal_api_url, jwt=jwt)
    except Exception:  # pylint: disable=broad-except
        log.exception('Failed to initialize the %s API client.', api_config.API_NAME)
        return no_data

    try:
        querystring = {} if not querystring else querystring
        response = getattr(api, resource).get(**querystring)
        results = response.get('results', no_data)
        page = 1
        next_page = response.get('next', None)
        while next_page:
            page += 1
            querystring['page'] = page
            response = getattr(api, resource).get(**querystring)
            results += response.get('results', no_data)
            next_page = response.get('next', None)
    except Exception:  # pylint: disable=broad-except
        log.exception('Failed to retrieve data from the %s API.', api_config.API_NAME)
        return no_data

    if cache_key:
        cache.set(cache_key, results, api_config.cache_ttl)

    return results
Exemple #12
0
def get_api_client(api_config, student):
    """
    Create and configure an API client for authenticated HTTP requests.

    Args:
        api_config: ProgramsApiConfig or CredentialsApiConfig object
        student: User object as whom to authenticate to the API

    Returns:
        EdxRestApiClient

    """
    id_token = get_id_token(student, api_config.OAUTH2_CLIENT_NAME)
    return EdxRestApiClient(api_config.internal_api_url, jwt=id_token)
def get_api_client(api_config, student):
    """
    Create and configure an API client for authenticated HTTP requests.

    Args:
        api_config: ProgramsApiConfig or CredentialsApiConfig object
        student: User object as whom to authenticate to the API

    Returns:
        EdxRestApiClient

    """
    id_token = get_id_token(student, api_config.OAUTH2_CLIENT_NAME)
    return EdxRestApiClient(api_config.internal_api_url, jwt=id_token)
Exemple #14
0
def get_programs(user):
    """Given a user, get programs from the Programs service.

    Returned value is cached depending on user permissions. Staff users making requests
    against Programs will receive unpublished programs, while regular users will only receive
    published programs.

    Arguments:
        user (User): The user to authenticate as when requesting programs.

    Returns:
        list of dict, representing programs returned by the Programs service.
    """
    programs_config = ProgramsApiConfig.current()
    no_programs = []

    # Bypass caching for staff users, who may be creating Programs and want to see them displayed immediately.
    use_cache = programs_config.is_cache_enabled and not user.is_staff

    if not programs_config.enabled:
        log.warning('Programs configuration is disabled.')
        return no_programs

    if use_cache:
        cached = cache.get(programs_config.CACHE_KEY)
        if cached is not None:
            return cached

    try:
        jwt = get_id_token(user, programs_config.OAUTH2_CLIENT_NAME)
        api = EdxRestApiClient(programs_config.internal_api_url, jwt=jwt)
    except Exception:  # pylint: disable=broad-except
        log.exception('Failed to initialize the Programs API client.')
        return no_programs

    try:
        response = api.programs.get()
    except Exception:  # pylint: disable=broad-except
        log.exception('Failed to retrieve programs from the Programs API.')
        return no_programs

    results = response.get('results', no_programs)

    if use_cache:
        cache.set(programs_config.CACHE_KEY, results,
                  programs_config.cache_ttl)

    return results
Exemple #15
0
def get_programs(user):
    """Given a user, get programs from the Programs service.

    Returned value is cached depending on user permissions. Staff users making requests
    against Programs will receive unpublished programs, while regular users will only receive
    published programs.

    Arguments:
        user (User): The user to authenticate as when requesting programs.

    Returns:
        list of dict, representing programs returned by the Programs service.
    """
    programs_config = ProgramsApiConfig.current()
    no_programs = []

    # Bypass caching for staff users, who may be creating Programs and want to see them displayed immediately.
    use_cache = programs_config.is_cache_enabled and not user.is_staff

    if not programs_config.enabled:
        log.warning('Programs configuration is disabled.')
        return no_programs

    if use_cache:
        cached = cache.get(programs_config.CACHE_KEY)
        if cached is not None:
            return cached

    try:
        jwt = get_id_token(user, programs_config.OAUTH2_CLIENT_NAME)
        api = EdxRestApiClient(programs_config.internal_api_url, jwt=jwt)
    except Exception:  # pylint: disable=broad-except
        log.exception('Failed to initialize the Programs API client.')
        return no_programs

    try:
        response = api.programs.get()
    except Exception:  # pylint: disable=broad-except
        log.exception('Failed to retrieve programs from the Programs API.')
        return no_programs

    results = response.get('results', no_programs)

    if use_cache:
        cache.set(programs_config.CACHE_KEY, results, programs_config.cache_ttl)

    return results
Exemple #16
0
def get_edxnotes_id_token(user):
    """
    Returns generated ID Token for edxnotes.
    """
    return get_id_token(user, CLIENT_NAME)
 def test_get_id_token_invalid_client(self):
     """Verify that ImproperlyConfigured is raised when an invalid client name is provided."""
     with self.assertRaises(ImproperlyConfigured):
         get_id_token(self.user, 'does-not-exist')
 def test_get_id_token_invalid_client(self):
     """Verify that ImproperlyConfigured is raised when an invalid client name is provided."""
     with self.assertRaises(ImproperlyConfigured):
         get_id_token(self.user, 'does-not-exist')
Exemple #19
0
def get_edxnotes_id_token(user):
    """
    Returns generated ID Token for edxnotes.
    """
    return get_id_token(user, CLIENT_NAME)
def get_edx_api_data(api_config,
                     user,
                     resource,
                     querystring=None,
                     cache_key=None):
    """Fetch data from an API using provided API configuration and resource
        name.

    Arguments:
        api_config (ConfigurationModel): The configuration model governing
            interaction with the API.
        user (User): The user to authenticate as when requesting data.
        resource(str): Name of the API resource for which data is being
            requested.
        querystring(dict): Querystring parameters that might be required to
            request data.
        cache_key(str): Where to cache retrieved data. Omitting this will cause the
            cache to be bypassed.

    Returns:
        list of dict, representing data returned by the API.
    """
    no_data = []

    if not api_config.enabled:
        log.warning('%s configuration is disabled.', api_config.API_NAME)
        return no_data

    if cache_key:
        cached = cache.get(cache_key)
        if cached is not None:
            return cached

    try:
        jwt = get_id_token(user, api_config.OAUTH2_CLIENT_NAME)
        api = EdxRestApiClient(api_config.internal_api_url, jwt=jwt)
    except Exception:  # pylint: disable=broad-except
        log.exception('Failed to initialize the %s API client.',
                      api_config.API_NAME)
        return no_data

    try:
        querystring = {} if not querystring else querystring
        response = getattr(api, resource).get(**querystring)
        results = response.get('results', no_data)
        page = 1
        next_page = response.get('next', None)
        while next_page:
            page += 1
            querystring['page'] = page
            response = getattr(api, resource).get(**querystring)
            results += response.get('results', no_data)
            next_page = response.get('next', None)
    except Exception:  # pylint: disable=broad-except
        log.exception('Failed to retrieve data from the %s API.',
                      api_config.API_NAME)
        return no_data

    if cache_key:
        cache.set(cache_key, results, api_config.cache_ttl)

    return results