Beispiel #1
0
def test_callback(mock_session_interface, mock_uuid4, mock_post, kc_config):
    mock_session_interface.return_value = MagicMock()
    mock_session_interface.return_value.open_session.return_value = {
        "state": "state123"
    }
    mock_uuid4.return_value = MagicMock()
    mock_uuid4.return_value.hex.return_value = "0123456789"
    mock_post.return_value = MagicMock()
    mock_post.return_value.json.return_value = {"access_token": "token123"}
    token_endpoint_payload = {
        "code": "code123",
        "grant_type": GrantTypes.authorization_code,
        "redirect_uri": "http://localhost/kc/callback",
        "client_id": kc_config.client.client_id,
        "client_secret": kc_config.client.client_secret,
    }
    userinfo_endpoint_header = auth_header("token123")
    expected_calls = [
        call(kc_config.openid.token_endpoint, data=token_endpoint_payload),
        call().raise_for_status(),
        call().json(),
        call(kc_config.openid.userinfo_endpoint, headers=userinfo_endpoint_header),
        call().raise_for_status(),
        call().json(),
    ]
    app = get_app()
    client = app.test_client()
    response = client.get("http://testserver/kc/callback?state=state123&code=code123")
    assert response.status_code == 302
    mock_post.assert_has_calls(expected_calls)
    def ticket(self, resources: List = [], access_token: str = None) -> Dict:
        """
        retrieve permission ticket from keycloak server
        see `docs <https://www.keycloak.org/docs/latest/authorization_services/#_overview_terminology_permission_ticket>`__ for more details

        >>>
        >>> from keycloak import Client
        >>> kc = Client()
        >>> kc.ticket(kc.resources)
        {'ticket': 'eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI5MmE5NTI2NC0wNTAyLTQzN2ItYWE3ZS01ZGIwNjFlYzMwOWYifQ.eyJwZXJtaXNzaW9ucyI6W3sicnNpZCI6IjQ4MTUyMTI2LWFhOTEtNGE0Zi04ZWU4LTczOTI4ZjViNmMwMCJ9XSwianRpIjoiNDhlZmVmZDQtMDg5NC00NjA2LTk0YjUtMDhlNjZiNTE4YWM2LTE1ODQxODUyOTUyMzkiLCJleHAiOjE1ODQxODUzNTUsIm5iZiI6MCwiaWF0IjoxNTg0MTg1Mjk1LCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvbWFzdGVyIiwic3ViIjoiNGM5YzI0MzAtYjJlNy00ZjBiLTkzMjUtYWE4MWRmZmUwNDYzIiwiYXpwIjoia2V5Y2xvYWstY2xpZW50In0.Or_6wzK9wlQMBPpi8bWIioWCeO6QuolKjr4mKC4YWpA'}
        >>>

        :param resources: list of resources
        :param access_token: access token to be used

        :returns: dictionary
        """
        access_token = access_token or self.access_token  # type: ignore
        resources = resources or self.resources  # type: ignore
        headers = auth_header(access_token, TokenType.bearer)
        payload = [{
            "resource_id": x["_id"],
            "resource_scopes": x["resource_scopes"]
        } for x in resources]
        log.debug("Retrieving permission ticket from keycloak")
        response = httpx.post(config.uma2.permission_endpoint,
                              json=payload,
                              headers=headers)
        response.raise_for_status()
        log.debug("Permission ticket retrieved successfully")
        return response.json()
Beispiel #3
0
def test_kc_callback(mock_request, mock_post, kc_config):
    mock_request.return_value = MagicMock()
    mock_request.return_value.method = "GET"
    mock_request.return_value.session = {"state": "state123"}
    mock_request.return_value.query_params = {
        "state": "state123",
        "code": "code123"
    }
    mock_request.return_value.url = MagicMock()
    mock_request.return_value.url.path = "/kc/callback"
    mock_post.return_value = MagicMock()
    mock_post.return_value.json.return_value = {"access_token": "token12345"}
    payload = {
        "code": "code123",
        "grant_type": GrantTypes.authorization_code,
        "client_id": kc_config.client.client_id,
        "client_secret": kc_config.client.client_secret,
        "redirect_uri": "http://localhost/kc/callback",
    }
    headers = auth_header("token12345")
    expected_calls = [
        call(kc_config.openid.token_endpoint, data=payload),
        call().raise_for_status(),
        call().json(),
        call(kc_config.openid.userinfo_endpoint, headers=headers),
        call().raise_for_status(),
        call().json(),
    ]
    with TestClient(app) as client:
        response = client.get("/kc/callback?state=state123&code=code123",
                              allow_redirects=False)
        assert response.status_code == 307
        assert response.headers["Location"] == "/"
        mock_post.assert_has_calls(expected_calls)
    def rpt(self, access_token: str) -> Dict:
        """
        retrieve request party token (RPT)
        see `docs <https://www.keycloak.org/docs/latest/authorization_services/#_service_rpt_overview>`__ for more details

        >>>
        >>> form keycloak import Client
        >>> kc = Client(username='******', password='******')
        >>> kc.rpt(kc.access_token)
        2020-08-03 11:47:54,568 [DEBUG] Retrieving RPT from keycloak
        2020-08-03 11:47:54,581 [DEBUG] RPT retrieved successfully
        2020-08-03 11:47:54,581 [DEBUG] Retrieving JWKs from keycloak server
        2020-08-03 11:47:54,587 [DEBUG] JWKs retrieved successfully
        {'exp': 1596435534, 'iat': 1596435474, 'jti': '822c7f6f-cd0a-4b9d-b55c-72803755ca7c', 'iss': 'http://localhost:8080/auth/realms/master', 'aud': ['kc', 'account'], 'sub': 'fce7f440-2e40-4161-90b4-61a1913c4b28', 'typ': 'Bearer', 'azp': 'kc', 'session_state': 'b3498962-3973-4f2d-9150-b1338e974d08', 'acr': '1', 'realm_access': {'roles': ['offline_access', 'uma_authorization']}, 'resource_access': {'account': {'roles': ['manage-account', 'manage-account-links', 'view-profile']}}, 'authorization': {'permissions': [{'rsid': '992dfa45-6098-45ac-b62e-da4d2787377f', 'rsname': 'Default Resource'}]}, 'scope': 'profile email', 'email_verified': True, 'preferred_username': '******'}
        >>>

        :param access_token: access token to be used

        :returns: dictionary
        """
        payload = {
            "grant_type": GrantTypes.uma_ticket,
            "audience": config.client.client_id,
        }
        headers = auth_header(access_token, TokenType.bearer)
        log.debug("Retrieving RPT from keycloak")
        response = httpx.post(config.uma2.token_endpoint,
                              data=payload,
                              headers=headers)
        response.raise_for_status()
        log.debug("RPT retrieved successfully")
        return response.json()
Beispiel #5
0
def test_kc_userinfo(mock_post, kc_client, kc_config):
    mock_post.return_value.json = MagicMock()
    token = "token123456789"
    headers = auth_header(token)
    kc_client.userinfo(token)
    mock_post.assert_called_once_with(kc_config.openid.userinfo_endpoint,
                                      headers=headers)
    mock_post.return_value.json.assert_called_once()
Beispiel #6
0
def test_kc_userinfo_failure(mock_post, kc_client, kc_config):
    mock_post.return_value = MagicMock()
    mock_post.return_value.content = "server error"
    mock_post.return_value.raise_for_status = MagicMock(side_effect=HTTPError)
    token = "token123456789"
    headers = auth_header(token)
    with pytest.raises(HTTPError) as ex:
        kc_client.fetch_userinfo(token)
    assert ex.type == HTTPError
    mock_post.assert_called_once_with(kc_config.openid.userinfo_endpoint,
                                      headers=headers)
Beispiel #7
0
def test_kc_logout(mock_post, kc_client, kc_config):
    kc_client.logout("access-token", "refresh-token")
    payload = {
        "client_id": kc_config.client.client_id,
        "client_secret": kc_config.client.client_secret,
        "refresh_token": "refresh-token",
    }
    headers = auth_header("access-token")
    mock_post.assert_called_once_with(kc_config.openid.end_session_endpoint,
                                      data=payload,
                                      headers=headers)
 async def logout(self, access_token: str = None, refresh_token: str = None) -> None:
     access_token = access_token or await self.access_token  # type: ignore
     refresh_token = refresh_token or await self.refresh_token  # type: ignore
     payload = {
         "client_id": config.client.client_id,
         "client_secret": config.client.client_secret,
         "refresh_token": refresh_token,
     }
     headers = auth_header(access_token)
     log.debug("Logging out user from server")
     async with httpx.AsyncClient() as client:
         await client.post(
             config.openid.end_session_endpoint, data=payload, headers=headers
         )
         log.debug("User logged out successfully")
 def logout(self,
            access_token: str = None,
            refresh_token: str = None) -> None:
     access_token = access_token or self.access_token  # type: ignore
     refresh_token = refresh_token or self.refresh_token  # type: ignore
     payload = {
         "client_id": config.client.client_id,
         "client_secret": config.client.client_secret,
         "refresh_token": refresh_token,
     }
     headers = auth_header(access_token)
     log.debug("Logging out user from server")
     response = httpx.post(config.openid.end_session_endpoint,
                           data=payload,
                           headers=headers)
     response.raise_for_status()
     log.debug("User logged out successfully")
    async def find_resources(self, access_token: str = None) -> List:
        """
        fetch resources from keycloak server

        >>> import asyncio
        >>> from keycloak import AsyncClient
        >>> kc= AsyncClient()
        >>> asyncio.run(await kc.find_resources())
        [{'name': 'default Resource', 'type': 'urn:python-client:resources:default', 'owner': {'id': 'd74cc555-d46c-4ef8-8a30-ceb2b91d8823'}, 'ownerManagedAccess': False, 'attributes': {}, '_id': 'bb6a777f-a17b-4555-b035-a6ce12a1fd21', 'uris': ['/*'], 'resource_scopes': []}]
        >>>

        :param access_token: access token to be used
        :returns: list
        """
        access_token = access_token or await self.access_token  # type: ignore
        headers = auth_header(access_token)
        log.debug("Retrieving resources from keycloak")
        async with httpx.AsyncClient() as client:
            response = await client.get(config.uma2.resource_endpoint, headers=headers)
            log.debug("Resources retrieved successfully")
            return [await self.find_resource(x, access_token) for x in response.json()]  # type: ignore
    def fetch_userinfo(self, access_token: str = None) -> Dict:
        """
        method to retrieve userinfo from the keycloak server

        >>>
        >>> from keycloak import Client
        >>> kc = Client()
        >>> kc.fetch_userinfo()
        {'sub': '4c9c2430-b2e7-4f0b-9325-aa81dffe0463', 'email_verified': False, 'preferred_username': '******'}
        >>>

        :param access_token: access token of the client or user
        :returns: dicttionary
        """
        access_token = access_token or self.access_token  # type: ignore
        headers = auth_header(access_token)
        log.debug("Retrieving user info from server")
        response = httpx.post(config.openid.userinfo_endpoint, headers=headers)
        response.raise_for_status()
        log.debug("User info retrieved successfully")
        return response.json()
    async def find_resource(self, resource_id: str, access_token: str = None) -> Dict:
        """
        Method to fetch the details of a resource

        >>> import asyncio
        >>> from keycloak import AsyncClient
        >>> kc= AsyncClient()
        >>> asyncio.run(await kc.find_resource('bb6a777f-a17b-4555-b035-a6ce12a1fd21'))
        {'name': 'default Resource', 'type': 'urn:python-client:resources:default', 'owner': {'id': 'd74cc555-d46c-4ef8-8a30-ceb2b91d8823'}, 'ownerManagedAccess': False, 'attributes': {}, '_id': 'bb6a777f-a17b-4555-b035-a6ce12a1fd21', 'uris': ['/*'], 'resource_scopes': []}
        >>>

        :param resource_id: id of the resource
        :param access_token: access token to be used
        :returns: list
        """
        access_token = access_token or await self.access_token  # type: ignore
        headers = auth_header(access_token)
        endpoint = f"{config.uma2.resource_endpoint}/{resource_id}"
        log.debug("Retrieving resource from keycloak")
        async with httpx.AsyncClient() as client:
            response = await client.get(endpoint, headers=headers)
            log.debug("Resource retrieved successfully")
            return response.json()