-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Appended asynchronous SDK version (#33)
* Fix init imports * Refresh token was appended request for access_token export to new method * Codestyle was fixed * Codestyle was fixed * Codestyle was fixed * Codestyle was fixed New method appended to Readme * feat: Appended asynchronous SDK version
- Loading branch information
Showing
6 changed files
with
336 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,7 @@ install_requires = | |
requests | ||
pyjwt | ||
cryptography | ||
aiohttp | ||
python_requires = >=3.6 | ||
test_suite = tests | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
from .main import CasdoorSDK | ||
from .async_main import AsyncCasdoorSDK | ||
from .user import User |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
# Copyright 2021 The Casbin Authors. All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import asyncio | ||
import aiohttp | ||
import jwt | ||
import json | ||
from .user import User | ||
from typing import List | ||
from cryptography import x509 | ||
from cryptography.hazmat.backends import default_backend | ||
|
||
|
||
class AsyncCasdoorSDK: | ||
def __init__( | ||
self, | ||
endpoint: str, | ||
client_id: str, | ||
client_secret: str, | ||
certificate: str, | ||
org_name: str, | ||
application_name: str, | ||
front_endpoint: str = None | ||
): | ||
self.endpoint = endpoint | ||
if front_endpoint: | ||
self.front_endpoint = front_endpoint | ||
else: | ||
self.front_endpoint = endpoint.replace(":8000", ":7001") | ||
self.client_id = client_id | ||
self.client_secret = client_secret | ||
self.certificate = certificate | ||
self.org_name = org_name | ||
self.application_name = application_name | ||
self.grant_type = "authorization_code" | ||
|
||
self.algorithms = ["RS256"] | ||
self._session = aiohttp.ClientSession() | ||
|
||
def __del__(self): | ||
loop = asyncio.get_running_loop() | ||
loop.create_task(self._session.close()) | ||
|
||
@property | ||
def certification(self) -> bytes: | ||
if type(self.certificate) is not str: | ||
raise TypeError('certificate field must be str type') | ||
return self.certificate.encode('utf-8') | ||
|
||
async def get_auth_link(self, redirect_uri: str, response_type: str = "code", scope: str = "read"): | ||
url = self.front_endpoint + "/login/oauth/authorize" | ||
params = { | ||
"client_id": self.client_id, | ||
"response_type": response_type, | ||
"redirect_uri": redirect_uri, | ||
"scope": scope, | ||
"state": self.application_name, | ||
} | ||
async with self._session.request("", url, params=params) as request: | ||
return request.url | ||
|
||
async def get_oauth_token(self, code: str) -> str: | ||
""" | ||
Request the Casdoor server to get access_token. | ||
:param code: the code that sent from Casdoor using redirect url back to your server. | ||
:return: access_token | ||
""" | ||
r = await self.oauth_token_request(code) | ||
access_token = r.get("access_token") | ||
return access_token | ||
|
||
async def oauth_token_request(self, code: str) -> dict: | ||
""" | ||
Request the Casdoor server to get access_token. | ||
:param code: the code that sent from Casdoor using redirect url back to your server. | ||
:return: Response from Casdoor | ||
""" | ||
url = self.endpoint + "/api/login/oauth/access_token" | ||
params = { | ||
"grant_type": self.grant_type, | ||
"client_id": self.client_id, | ||
"client_secret": self.client_secret, | ||
"code": code, | ||
} | ||
async with self._session.post(url, data=params) as response: | ||
return await response.json() | ||
|
||
async def refresh_token_request(self, refresh_token: str, scope: str = "") -> dict: | ||
""" | ||
Request the Casdoor server to get access_token. | ||
:param refresh_token: refresh_token for send to Casdoor | ||
:param scope: OAuth scope | ||
:return: Response from Casdoor | ||
""" | ||
url = self.endpoint + "/api/login/oauth/refresh_token" | ||
params = { | ||
"grant_type": self.grant_type, | ||
"client_id": self.client_id, | ||
"client_secret": self.client_secret, | ||
"scope": scope, | ||
"refresh_token": refresh_token, | ||
} | ||
async with self._session.post(url, data=params) as request: | ||
return await request.json() | ||
|
||
async def refresh_oauth_token(self, refresh_token: str, scope: str = "") -> str: | ||
""" | ||
Request the Casdoor server to get access_token. | ||
:param refresh_token: refresh_token for send to Casdoor | ||
:param scope: OAuth scope | ||
:return: Response from Casdoor | ||
""" | ||
r = await self.refresh_token_request(refresh_token, scope) | ||
access_token = r.get("access_token") | ||
|
||
return access_token | ||
|
||
def parse_jwt_token(self, token: str) -> dict: | ||
""" | ||
Converts the returned access_token to real data using jwt (JSON Web Token) algorithms. | ||
:param token: access_token | ||
:return: the data in dict format | ||
""" | ||
certificate = x509.load_pem_x509_certificate(self.certification, default_backend()) | ||
|
||
return_json = jwt.decode( | ||
token, | ||
certificate.public_key(), | ||
algorithms=self.algorithms, | ||
audience=self.client_id, | ||
) | ||
return return_json | ||
|
||
async def get_users(self) -> List[dict]: | ||
""" | ||
Get the users from Casdoor. | ||
:return: a list of dicts containing user info | ||
""" | ||
url = self.endpoint + "/api/get-users" | ||
params = { | ||
"owner": self.org_name, | ||
"clientId": self.client_id, | ||
"clientSecret": self.client_secret, | ||
} | ||
async with self._session.get(url, params=params) as request: | ||
users = await request.json() | ||
return users | ||
|
||
async def get_user(self, user_id: str) -> dict: | ||
""" | ||
Get the user from Casdoor providing the user_id. | ||
:param user_id: the id of the user | ||
:return: a dict that contains user's info | ||
""" | ||
url = self.endpoint + "/api/get-user" | ||
params = { | ||
"id": f"{self.org_name}/{user_id}", | ||
"clientId": self.client_id, | ||
"clientSecret": self.client_secret, | ||
} | ||
async with self._session.get(url, params=params) as request: | ||
user = await request.json() | ||
return user | ||
|
||
async def modify_user(self, method: str, user: User) -> dict: | ||
url = self.endpoint + f"/api/{method}" | ||
user.owner = self.org_name | ||
params = { | ||
"id": f"{user.owner}/{user.name}", | ||
"clientId": self.client_id, | ||
"clientSecret": self.client_secret, | ||
} | ||
user_info = json.dumps(user.to_dict()) | ||
async with self._session.post(url, params=params, data=user_info) as request: | ||
response = await request.json() | ||
return response | ||
|
||
async def add_user(self, user: User) -> dict: | ||
response = await self.modify_user("add-user", user) | ||
return response | ||
|
||
async def update_user(self, user: User) -> dict: | ||
response = await self.modify_user("update-user", user) | ||
return response | ||
|
||
async def delete_user(self, user: User) -> dict: | ||
response = await self.modify_user("delete-user", user) | ||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# Copyright 2021 The Casbin Authors. All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from src.casdoor.async_main import AsyncCasdoorSDK, User | ||
from unittest import IsolatedAsyncioTestCase | ||
|
||
|
||
class TestOAuth(IsolatedAsyncioTestCase): | ||
""" | ||
You should replace the code content below and | ||
the get_sdk() method's content with your own Casdoor | ||
instance and such if you need to. And running these tests successfully | ||
proves that your connection to Casdoor is good-and-working! | ||
""" | ||
|
||
# server returned authorization code | ||
code = "6d038ac60d4e1f17e742" | ||
|
||
@staticmethod | ||
def get_sdk(): | ||
|
||
sdk = AsyncCasdoorSDK( | ||
endpoint="http://test.casbin.com:8000", | ||
client_id="3267f876b11e7d1cb217", | ||
client_secret="3f0d1f06d28d65309c8f38b505cb9dcfa487754d", | ||
certificate="CasdoorSecret", | ||
org_name="built-in", | ||
application_name="app-built-in" | ||
) | ||
return sdk | ||
|
||
async def test_get_oauth_token(self): | ||
sdk = self.get_sdk() | ||
access_token = await sdk.get_oauth_token(self.code) | ||
self.assertIsInstance(access_token, str) | ||
|
||
async def test_oauth_token_request(self): | ||
sdk = self.get_sdk() | ||
response = await sdk.oauth_token_request(self.code) | ||
self.assertIsInstance(response, dict) | ||
|
||
async def test_refresh_token_request(self): | ||
sdk = self.get_sdk() | ||
response = await sdk.oauth_token_request(self.code) | ||
refresh_token = response.get("refresh_token") | ||
sdk.grant_type = "refresh_token" | ||
response = await sdk.refresh_token_request(refresh_token) | ||
sdk.grant_type = "authorization_code" | ||
self.assertIsInstance(response, dict) | ||
|
||
async def test_get_oauth_refreshed_token(self): | ||
sdk = self.get_sdk() | ||
response = await sdk.oauth_token_request(self.code) | ||
refresh_token = response.get("refresh_token") | ||
sdk.grant_type = "refresh_token" | ||
response = await sdk.refresh_oauth_token(refresh_token) | ||
sdk.grant_type = "authorization_code" | ||
self.assertIsInstance(response, str) | ||
|
||
async def test_parse_jwt_token(self): | ||
sdk = self.get_sdk() | ||
access_token = await sdk.get_oauth_token(self.code) | ||
decoded_msg = sdk.parse_jwt_token(access_token) | ||
self.assertIsInstance(decoded_msg, dict) | ||
|
||
async def test_get_users(self): | ||
sdk = self.get_sdk() | ||
users = await sdk.get_users() | ||
self.assertIsInstance(users, list) | ||
|
||
async def test_get_user(self): | ||
sdk = self.get_sdk() | ||
user = await sdk.get_user("admin") | ||
self.assertIsInstance(user, dict) | ||
|
||
async def test_modify_user(self): | ||
sdk = self.get_sdk() | ||
user = User() | ||
user.name = "test_ffyuanda" | ||
await sdk.delete_user(user) | ||
|
||
response = await sdk.add_user(user) | ||
self.assertEqual(response["data"], "Affected") | ||
|
||
response = await sdk.delete_user(user) | ||
self.assertEqual(response["data"], "Affected") | ||
|
||
response = await sdk.add_user(user) | ||
self.assertEqual(response["data"], "Affected") | ||
|
||
user.phone = "phone" | ||
response = await sdk.update_user(user) | ||
self.assertEqual(response["data"], "Affected") | ||
|
||
self.assertIn("status", response) | ||
self.assertIsInstance(response, dict) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters