Skip to content

Commit eae94e5

Browse files
committed
success return for examples with valid api key and tenant id
1 parent 8e8ecf6 commit eae94e5

File tree

12 files changed

+536
-0
lines changed

12 files changed

+536
-0
lines changed

fastcomments_core/.gitignore

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
env/
12+
build/
13+
develop-eggs/
14+
dist/
15+
downloads/
16+
eggs/
17+
.eggs/
18+
lib/
19+
lib64/
20+
parts/
21+
sdist/
22+
var/
23+
*.egg-info/
24+
.installed.cfg
25+
*.egg
26+
27+
# PyInstaller
28+
# Usually these files are written by a python script from a template
29+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
30+
*.manifest
31+
*.spec
32+
33+
# Installer logs
34+
pip-log.txt
35+
pip-delete-this-directory.txt
36+
37+
# Unit test / coverage reports
38+
htmlcov/
39+
.tox/
40+
.coverage
41+
.coverage.*
42+
.cache
43+
nosetests.xml
44+
coverage.xml
45+
*,cover
46+
.hypothesis/
47+
venv/
48+
.venv/
49+
.python-version
50+
.pytest_cache
51+
52+
# Translations
53+
*.mo
54+
*.pot
55+
56+
# Django stuff:
57+
*.log
58+
59+
# Sphinx documentation
60+
docs/_build/
61+
62+
# PyBuilder
63+
target/
64+
65+
#Ipython Notebook
66+
.ipynb_checkpoints

fastcomments_core/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Fastcomments-python
2+
3+
Utilities to interact with the FastComments python client, particularly for Single Sign-On (SSO) functionality.
4+
5+
# Examples
6+
7+
see `examples/`
8+
9+
run with poetry. For example: `poetry run python examples/secure_sso.py`
10+
11+
# Building
12+
13+
`poetry build`
14+
15+
# Publishing
16+
17+
`poetry publish`
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# from core.fastcomments_client.openapi_client.api import public_api
2+
# from client.openapi_client.configuration import Configuration
3+
4+
# from client.build.lib.openapi_client.api import public_api
5+
# from client.build.lib.openapi_client.configuration import Configuration
6+
from fastcomments_client.api import public_api
7+
from fastcomments_core.sso.fastcomments_sso import FastCommentsSSO
8+
from fastcomments_core.sso.secure_sso_user_data import SecureSSOUserData
9+
10+
11+
def main():
12+
api_key = "your-api-key"
13+
# This should be done server side for security
14+
user_data = SecureSSOUserData(
15+
"user-123",
16+
"email@example.com",
17+
"John Doe",
18+
"Avatar"
19+
)
20+
21+
# Create SSO config with payload
22+
sso = FastCommentsSSO.new_secure(api_key, user_data)
23+
24+
tenant_id = "tenant-123"
25+
url_id = "123"
26+
token = sso.create_token()
27+
28+
# Interact with client
29+
30+
pub = public_api.PublicApi()
31+
res = pub.get_comments_public(tenant_id=tenant_id, url_id=url_id, sso=token)
32+
print(res)
33+
34+
main()
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from fastcomments_client.api import public_api
2+
from fastcomments_core.sso.fastcomments_sso import FastCommentsSSO
3+
from fastcomments_core.sso.simple_sso_user_data import SimpleSSOUserData
4+
5+
6+
def main():
7+
api_key = "your-api-key"
8+
# This should be done server side for security
9+
user_data = SimpleSSOUserData(
10+
"user-123",
11+
"email@example.com",
12+
"Avatar"
13+
)
14+
15+
# Create SSO config with payload
16+
sso = FastCommentsSSO.new_simple(user_data)
17+
18+
tenant_id = "tenant-123"
19+
url_id = "123"
20+
token = sso.create_token()
21+
22+
# Interact with client
23+
pub = public_api.PublicApi()
24+
res = pub.get_comments_public(tenant_id=tenant_id, url_id=url_id, sso=token)
25+
print(res)
26+
27+
main()

fastcomments_core/fastcomments_core/__init__.py

Whitespace-only changes.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import time
2+
3+
from fastcomments_core.sso.helpers import create_verification_hash
4+
from fastcomments_core.sso.secure_sso_payload import SecureSSOPayload
5+
from fastcomments_core.sso.secure_sso_user_data import SecureSSOUserData
6+
from fastcomments_core.sso.simple_sso_user_data import SimpleSSOUserData
7+
8+
9+
class FastCommentsSSO:
10+
def __init__(self, secure_sso_payload, simple_sso_user_data, login_url: str | None = None, logout_url: str | None = None):
11+
self.secure_sso_payload = secure_sso_payload
12+
self.simple_sso_user_data = simple_sso_user_data
13+
self.login_url = login_url
14+
self.logout_url = logout_url
15+
16+
@classmethod
17+
def new_secure(cls, api_key: str, secure_sso_user_data: SecureSSOUserData):
18+
timestamp = int(time.time())
19+
20+
user_data_str = secure_sso_user_data.as_json_base64()
21+
hash = create_verification_hash(api_key, timestamp, user_data_str)
22+
23+
payload = SecureSSOPayload(user_data_str, hash, timestamp)
24+
return cls(secure_sso_payload = payload, simple_sso_user_data = None)
25+
26+
@classmethod
27+
def new_simple(cls, simple_sso_user_data: SimpleSSOUserData):
28+
return cls(secure_sso_payload = None, simple_sso_user_data = simple_sso_user_data)
29+
30+
@classmethod
31+
def new_secure_with_urls(
32+
cls, secure_sso_payload: SecureSSOPayload,
33+
login_url: str,
34+
logout_url: str,
35+
):
36+
return cls(secure_sso_payload, None, login_url, logout_url)
37+
38+
def create_token(self):
39+
if self.secure_sso_payload:
40+
return self.secure_sso_payload.toJSON()
41+
elif self.simple_sso_user_data:
42+
return self.simple_sso_user_data.toJSON()
43+
else:
44+
raise ValueError("No user data provided")
45+
46+
def reset_token(self):
47+
self.cached_token = None
48+
49+
def prepare_to_send(self):
50+
if self.cached_token:
51+
return self.cached_token
52+
53+
self.cached_token = self.create_token()
54+
return self.cached_token
55+
56+
def set_secure_sso_payload(self, secure_sso_payload: SecureSSOPayload):
57+
self.secure_sso_payload = secure_sso_payload
58+
self.simple_sso_user_data = None
59+
self.reset_token()
60+
61+
def set_simple_sso_user_data(self, simple_sso_user_data: SimpleSSOUserData):
62+
self.simple_sso_user_data = simple_sso_user_data
63+
self.secure_sso_payload = None
64+
self.reset_token()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import hmac
2+
import hashlib
3+
4+
class CreateHashError(Exception):
5+
"""Error occurred during hash creation."""
6+
pass
7+
8+
def create_verification_hash(api_key: str, timestamp: int, user_data_json_base64: str):
9+
try:
10+
# Create message string by concatenating timestamp and base64 data
11+
message_str = f"{timestamp}{user_data_json_base64}"
12+
13+
# Create HMAC using SHA256 hash function
14+
mac = hmac.new(
15+
key=api_key.encode('utf-8'),
16+
msg=message_str.encode('utf-8'),
17+
digestmod=hashlib.sha256
18+
)
19+
20+
# Get digest as bytes then convert to hex
21+
bytes_result = mac.digest()
22+
return get_bytes_as_hex(bytes_result)
23+
24+
except Exception as e:
25+
raise CreateHashError(f"Failed to create verification hash: {str(e)}")
26+
27+
def get_bytes_as_hex(bytes_data: bytes) -> str:
28+
return ''.join(f'{b:02x}' for b in bytes_data)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import json
2+
3+
class SecureSSOPayload:
4+
def __init__(self, user_data_json_base64: str, verification_hash: str, timestamp: int):
5+
self.user_data_json_base64 = user_data_json_base64
6+
self.verification_hash = verification_hash
7+
self.timestamp = timestamp
8+
9+
def toJSON(self):
10+
return json.dumps(
11+
self,
12+
default=lambda o: o.__dict__,
13+
sort_keys=True,
14+
indent=4)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import json
2+
import base64
3+
4+
class SecureSSOUserData:
5+
def __init__(self, user_id: str, email: str, username: str, avatar: str):
6+
self.user_id = user_id
7+
self.email = email
8+
self.username = username
9+
self.avatar = avatar
10+
11+
def toJSON(self):
12+
return json.dumps(
13+
self,
14+
default=lambda o: o.__dict__,
15+
sort_keys=True,
16+
indent=4)
17+
18+
def as_json_base64(self) -> str:
19+
json_str = self.toJSON()
20+
json_bytes = json_str.encode("utf-8")
21+
22+
result = base64.b64encode(json_bytes)
23+
return result.decode("utf-8")
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import json
2+
3+
class SimpleSSOUserData:
4+
def __init__(self, user_id: str, email: str, avatar: str):
5+
self.user_id = user_id
6+
self.email = email
7+
self.avatar = avatar
8+
9+
def toJSON(self):
10+
return json.dumps(
11+
self,
12+
default=lambda o: o.__dict__,
13+
sort_keys=True,
14+
indent=4)

0 commit comments

Comments
 (0)