Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDK-4544] Add orgs in client credentials support #549

Merged
merged 1 commit into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions auth0/authentication/get_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def client_credentials(
self,
audience: str,
grant_type: str = "client_credentials",
organization: str | None = None,
) -> Any:
"""Client credentials grant

Expand All @@ -104,6 +105,9 @@ def client_credentials(

grant_type (str, optional): Denotes the flow you're using. For client credentials use "client_credentials"

organization (str, optional): Optional Organization name or ID. When included, the access token returned
will include the org_id and org_name claims

Returns:
access_token
"""
Expand All @@ -114,6 +118,7 @@ def client_credentials(
"client_id": self.client_id,
"audience": audience,
"grant_type": grant_type,
"organization": organization,
},
)

Expand Down
44 changes: 44 additions & 0 deletions auth0/management/client_grants.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def all(
per_page: int | None = None,
include_totals: bool = False,
client_id: str | None = None,
allow_any_organization: bool | None = None,
):
"""Retrieves all client grants.

Expand All @@ -77,6 +78,8 @@ def all(

client_id (string, optional): The id of a client to filter.

allow_any_organization (bool, optional): Optional filter on allow_any_organization.

See: https://auth0.com/docs/api/management/v2#!/Client_Grants/get_client_grants
"""

Expand All @@ -86,6 +89,7 @@ def all(
"per_page": per_page,
"include_totals": str(include_totals).lower(),
"client_id": client_id,
"allow_any_organization": allow_any_organization,
}

return self.client.get(self._url(), params=params)
Expand Down Expand Up @@ -124,3 +128,43 @@ def update(self, id: str, body: dict[str, Any]) -> dict[str, Any]:
"""

return self.client.patch(self._url(id), data=body)

def get_organizations(
self,
id: str,
page: int | None = None,
per_page: int | None = None,
include_totals: bool = False,
from_param: str | None = None,
take: int | None = None,
):
"""Get the organizations associated to a client grant.

Args:
id (str): Id of client grant.

page (int, optional): The result's page number (zero based). When not set,
the default value is up to the server.

per_page (int, optional): The amount of entries per page. When not set,
the default value is up to the server.

include_totals (bool, optional): True if the query summary is
to be included in the result, False otherwise. Defaults to False.

from_param (str, optional): Id to start retrieving entries. You can
limit the amount of entries using the take parameter.

take (int, optional): The total amount of entries to retrieve when
using the from parameter. When not set, the default value is up to the server.
"""

params = {
"per_page": per_page,
"page": page,
"include_totals": str(include_totals).lower(),
"from": from_param,
"take": take,
}

return self.client.get(self._url(f"{id}/organizations"), params=params)
62 changes: 62 additions & 0 deletions auth0/management/organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,3 +460,65 @@ def delete_organization_invitation(self, id: str, invitation_id: str) -> Any:
"""

return self.client.delete(self._url(id, "invitations", invitation_id))

def get_client_grants(
self,
id: str,
audience: str | None = None,
client_id: str | None = None,
page: int | None = None,
per_page: int | None = None,
include_totals: bool = False,
):
"""Get client grants associated to an organization.

Args:
id (str): Id of organization.

audience (str, optional): URL encoded audience of a Resource Server
to filter.

client_id (string, optional): The id of a client to filter.

page (int, optional): The result's page number (zero based). When not set,
the default value is up to the server.

per_page (int, optional): The amount of entries per page. When not set,
the default value is up to the server.

include_totals (bool, optional): True if the query summary is
to be included in the result, False otherwise. Defaults to False.
"""
params = {
"audience": audience,
"client_id": client_id,
"page": page,
"per_page": per_page,
"include_totals": str(include_totals).lower(),
}

return self.client.get(self._url(id, "client-grants"), params=params)

def add_client_grant(self, id: str, grant_id: str) -> dict[str, Any]:
"""Associate a client grant with an organization.

Args:
id (str): the ID of the organization.

grant_id (string) A Client Grant ID to add to the organization.
"""

return self.client.post(
self._url(id, "client-grants"), data={"grant_id": grant_id}
)

def delete_client_grant(self, id: str, grant_id: str) -> dict[str, Any]:
"""Remove a client grant from an organization.

Args:
id (str): the ID of the organization.

grant_id (string) A Client Grant ID to remove from the organization.
"""

return self.client.delete(self._url(id, "client-grants", grant_id))
22 changes: 22 additions & 0 deletions auth0/test/authentication/test_get_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def test_client_credentials(self, mock_post):
"client_secret": "clsec",
"audience": "aud",
"grant_type": "gt",
"organization": None,
},
)

Expand All @@ -133,11 +134,32 @@ def test_client_credentials_with_client_assertion(self, mock_post):
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"audience": "aud",
"grant_type": "gt",
"organization": None,
},
)

self.assertTrue(fnmatch(kwargs["data"]["client_assertion"], "*.*.*"))

@mock.patch("auth0.rest.RestClient.post")
def test_client_credentials_with_organization(self, mock_post):
g = GetToken("my.domain.com", "cid", client_secret="clsec")

g.client_credentials("aud", organization="my-org")

args, kwargs = mock_post.call_args

self.assertEqual(args[0], "https://my.domain.com/oauth/token")
self.assertEqual(
kwargs["data"],
{
"client_id": "cid",
"grant_type": "client_credentials",
"client_secret": "clsec",
"audience": "aud",
"organization": "my-org",
},
)

@mock.patch("auth0.rest.RestClient.post")
def test_login(self, mock_post):
g = GetToken("my.domain.com", "cid", client_secret="clsec")
Expand Down
45 changes: 45 additions & 0 deletions auth0/test/management/test_client_grants.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def test_all(self, mock_rc):
"per_page": None,
"include_totals": "false",
"client_id": None,
"allow_any_organization": None,
},
)

Expand All @@ -50,6 +51,7 @@ def test_all(self, mock_rc):
"per_page": None,
"include_totals": "false",
"client_id": None,
"allow_any_organization": None,
},
)

Expand All @@ -67,6 +69,7 @@ def test_all(self, mock_rc):
"per_page": 23,
"include_totals": "true",
"client_id": None,
"allow_any_organization": None,
},
)

Expand All @@ -84,6 +87,25 @@ def test_all(self, mock_rc):
"per_page": None,
"include_totals": "false",
"client_id": "exampleid",
"allow_any_organization": None,
},
)

# With allow any organization
c.all(allow_any_organization=True)

args, kwargs = mock_instance.get.call_args

self.assertEqual("https://domain/api/v2/client-grants", args[0])
self.assertEqual(
kwargs["params"],
{
"audience": None,
"page": None,
"per_page": None,
"include_totals": "false",
"client_id": None,
"allow_any_organization": True,
},
)

Expand Down Expand Up @@ -120,3 +142,26 @@ def test_update(self, mock_rc):

self.assertEqual("https://domain/api/v2/client-grants/this-id", args[0])
self.assertEqual(kwargs["data"], {"a": "b", "c": "d"})

@mock.patch("auth0.management.client_grants.RestClient")
def test_get_organizations(self, mock_rc):
mock_instance = mock_rc.return_value

c = ClientGrants(domain="domain", token="jwttoken")
c.get_organizations("cgid")

args, kwargs = mock_instance.get.call_args

self.assertEqual(
"https://domain/api/v2/client-grants/cgid/organizations", args[0]
)
self.assertEqual(
kwargs["params"],
{
"page": None,
"per_page": None,
"include_totals": "false",
"from": None,
"take": None,
},
)
41 changes: 41 additions & 0 deletions auth0/test/management/test_organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,3 +479,44 @@ def test_delete_organization_invitation(self, mock_rc):
mock_instance.delete.assert_called_with(
"https://domain/api/v2/organizations/test-org/invitations/test-inv"
)

@mock.patch("auth0.management.organizations.RestClient")
def test_get_client_grants(self, mock_rc):
mock_instance = mock_rc.return_value

c = Organizations(domain="domain", token="jwttoken")
c.get_client_grants("test-org")

mock_instance.get.assert_called_with(
"https://domain/api/v2/organizations/test-org/client-grants",
params={
"audience": None,
"client_id": None,
"page": None,
"per_page": None,
"include_totals": "false",
},
)

@mock.patch("auth0.management.organizations.RestClient")
def test_add_client_grant(self, mock_rc):
mock_instance = mock_rc.return_value

c = Organizations(domain="domain", token="jwttoken")
c.add_client_grant("test-org", "test-cg")

mock_instance.post.assert_called_with(
"https://domain/api/v2/organizations/test-org/client-grants",
data={"grant_id": "test-cg"},
)

@mock.patch("auth0.management.organizations.RestClient")
def test_delete_client_grant(self, mock_rc):
mock_instance = mock_rc.return_value

c = Organizations(domain="domain", token="jwttoken")
c.delete_client_grant("test-org", "test-cg")

mock_instance.delete.assert_called_with(
"https://domain/api/v2/organizations/test-org/client-grants/test-cg",
)