Skip to content

Commit

Permalink
[SDK-4544] Add orgs in client credentials support (#549)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamjmcgrath authored Nov 9, 2023
2 parents c00b4ea + 1404403 commit 22c4d4f
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 0 deletions.
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",
)

0 comments on commit 22c4d4f

Please sign in to comment.