auth-gateway-serverkit 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- auth_gateway_serverkit/__init__.py +0 -0
- auth_gateway_serverkit/email.py +54 -0
- auth_gateway_serverkit/http_client.py +104 -0
- auth_gateway_serverkit/keycloak/__init__.py +3 -0
- auth_gateway_serverkit/keycloak/authorization_api.py +88 -0
- auth_gateway_serverkit/keycloak/client_api.py +645 -0
- auth_gateway_serverkit/keycloak/config.py +25 -0
- auth_gateway_serverkit/keycloak/initializer.py +106 -0
- auth_gateway_serverkit/keycloak/json_transformer.py +65 -0
- auth_gateway_serverkit/keycloak/roles_api.py +85 -0
- auth_gateway_serverkit/keycloak/user_api.py +268 -0
- auth_gateway_serverkit/logger.py +47 -0
- auth_gateway_serverkit/main.py +2 -0
- auth_gateway_serverkit/middleware/__init__.py +0 -0
- auth_gateway_serverkit/middleware/auth.py +173 -0
- auth_gateway_serverkit/middleware/config.py +36 -0
- auth_gateway_serverkit/middleware/schemas.py +19 -0
- auth_gateway_serverkit/password.py +25 -0
- auth_gateway_serverkit/request_handler.py +90 -0
- auth_gateway_serverkit/string.py +41 -0
- auth_gateway_serverkit-0.0.0.dist-info/METADATA +25 -0
- auth_gateway_serverkit-0.0.0.dist-info/RECORD +24 -0
- auth_gateway_serverkit-0.0.0.dist-info/WHEEL +5 -0
- auth_gateway_serverkit-0.0.0.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
""" Email sending module for user notifications in KalSense."""
|
|
2
|
+
import smtplib
|
|
3
|
+
from email.mime.text import MIMEText
|
|
4
|
+
from email.mime.multipart import MIMEMultipart
|
|
5
|
+
from .logger import init_logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
logger = init_logger('user.email')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def send_password_email(
|
|
12
|
+
app_password,
|
|
13
|
+
app_email,
|
|
14
|
+
first_name,
|
|
15
|
+
user_email,
|
|
16
|
+
user_name,
|
|
17
|
+
generated_password
|
|
18
|
+
):
|
|
19
|
+
try:
|
|
20
|
+
message = MIMEMultipart("alternative")
|
|
21
|
+
message["From"] = app_email
|
|
22
|
+
message["To"] = user_email
|
|
23
|
+
message["Subject"] = "Welcome to KalSense"
|
|
24
|
+
|
|
25
|
+
html = f"""
|
|
26
|
+
<html>
|
|
27
|
+
<body>
|
|
28
|
+
<p>Hi {first_name},<br>
|
|
29
|
+
Welcome! Your new user was created with the following details: <br>
|
|
30
|
+
Your user name is: <b>{user_name}</b><br>
|
|
31
|
+
Your password is: <b>{generated_password}</b><br>
|
|
32
|
+
Please change it upon your first login.<br>
|
|
33
|
+
</p>
|
|
34
|
+
<p>Best Regards,<br>
|
|
35
|
+
<i>IT Services Team</i>
|
|
36
|
+
</p>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
part = MIMEText(html, "html")
|
|
42
|
+
message.attach(part)
|
|
43
|
+
|
|
44
|
+
server = smtplib.SMTP("smtp.gmail.com", 587)
|
|
45
|
+
server.starttls()
|
|
46
|
+
server.login(app_email, app_password)
|
|
47
|
+
text = message.as_string()
|
|
48
|
+
server.sendmail(app_email, user_email, text)
|
|
49
|
+
server.quit()
|
|
50
|
+
logger.info(f"Email sent to {user_email}")
|
|
51
|
+
except smtplib.SMTPException as e:
|
|
52
|
+
logger.error(f"Failed to send email to {user_email}, error: {str(e)}")
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.error(f"Failed to send email to {user_email}, error: {str(e)}")
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
""" http client utilities for making asynchronous HTTP requests using httpx. """
|
|
2
|
+
import httpx
|
|
3
|
+
from typing import Optional, Dict
|
|
4
|
+
from .logger import init_logger
|
|
5
|
+
|
|
6
|
+
logger = init_logger("utils.requests")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def post(
|
|
10
|
+
url: str,
|
|
11
|
+
json: Optional[dict] = None,
|
|
12
|
+
data: Optional[dict] = None,
|
|
13
|
+
files: Optional[dict] = None,
|
|
14
|
+
headers: Optional[Dict[str, str]] = None,
|
|
15
|
+
timeout=20,
|
|
16
|
+
connect=5
|
|
17
|
+
) -> dict:
|
|
18
|
+
try:
|
|
19
|
+
timeout = httpx.Timeout(timeout, connect=connect)
|
|
20
|
+
async with httpx.AsyncClient(timeout=timeout, headers=headers) as client:
|
|
21
|
+
if json is not None:
|
|
22
|
+
response = await client.post(url, json=json)
|
|
23
|
+
elif files is not None or data is not None:
|
|
24
|
+
response = await client.post(url, data=data, files=files)
|
|
25
|
+
else:
|
|
26
|
+
response = await client.post(url, data=data)
|
|
27
|
+
response.raise_for_status()
|
|
28
|
+
return response.json()
|
|
29
|
+
except httpx.HTTPStatusError as e:
|
|
30
|
+
logger.error(f"HTTP error: {e.response.status_code} - {e.response.text} - URL: {url}")
|
|
31
|
+
raise
|
|
32
|
+
except Exception as e:
|
|
33
|
+
logger.error(f"Request error: {e} - URL: {url}")
|
|
34
|
+
raise
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def get(
|
|
38
|
+
url: str,
|
|
39
|
+
params: dict = None,
|
|
40
|
+
headers: Optional[Dict[str, str]] = None,
|
|
41
|
+
timeout=20,
|
|
42
|
+
connect=5
|
|
43
|
+
) -> dict:
|
|
44
|
+
try:
|
|
45
|
+
timeout = httpx.Timeout(timeout, connect=connect)
|
|
46
|
+
async with httpx.AsyncClient(headers=headers) as client:
|
|
47
|
+
response = await client.get(url, params=params, timeout=timeout)
|
|
48
|
+
response.raise_for_status()
|
|
49
|
+
return response.json()
|
|
50
|
+
except httpx.HTTPStatusError as e:
|
|
51
|
+
logger.error(f"HTTP error: {e.response.status_code} - {e.response.text}")
|
|
52
|
+
raise
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.error(f"Request error: {e}")
|
|
55
|
+
raise
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def delete(
|
|
59
|
+
url: str,
|
|
60
|
+
params: Optional[dict] = None,
|
|
61
|
+
headers: Optional[Dict[str, str]] = None,
|
|
62
|
+
timeout=20,
|
|
63
|
+
connect=5
|
|
64
|
+
) -> dict:
|
|
65
|
+
try:
|
|
66
|
+
timeout = httpx.Timeout(timeout, connect=connect)
|
|
67
|
+
async with httpx.AsyncClient(timeout=timeout, headers=headers) as client:
|
|
68
|
+
response = await client.delete(url, params=params)
|
|
69
|
+
response.raise_for_status()
|
|
70
|
+
return response.json()
|
|
71
|
+
except httpx.HTTPStatusError as e:
|
|
72
|
+
logger.error(f"HTTP error: {e.response.status_code} - {e.response.text} - URL: {url}")
|
|
73
|
+
raise
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error(f"Request error: {e} - URL: {url}")
|
|
76
|
+
raise
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def put(
|
|
80
|
+
url: str,
|
|
81
|
+
json: Optional[dict] = None,
|
|
82
|
+
data: Optional[dict] = None,
|
|
83
|
+
files: Optional[dict] = None,
|
|
84
|
+
headers: Optional[Dict[str, str]] = None,
|
|
85
|
+
timeout=20,
|
|
86
|
+
connect=5
|
|
87
|
+
) -> dict:
|
|
88
|
+
try:
|
|
89
|
+
timeout = httpx.Timeout(timeout, connect=connect)
|
|
90
|
+
async with httpx.AsyncClient(timeout=timeout, headers=headers) as client:
|
|
91
|
+
if json is not None:
|
|
92
|
+
response = await client.put(url, json=json)
|
|
93
|
+
elif files is not None or data is not None:
|
|
94
|
+
response = await client.put(url, data=data, files=files)
|
|
95
|
+
else:
|
|
96
|
+
response = await client.put(url, data=data)
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
return response.json()
|
|
99
|
+
except httpx.HTTPStatusError as e:
|
|
100
|
+
logger.error(f"HTTP error: {e.response.status_code} - {e.response.text} - URL: {url}")
|
|
101
|
+
raise
|
|
102
|
+
except Exception as e:
|
|
103
|
+
logger.error(f"Request error: {e} - URL: {url}")
|
|
104
|
+
raise
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import aiohttp
|
|
2
|
+
from typing import Dict, Optional
|
|
3
|
+
from .config import settings
|
|
4
|
+
from .client_api import get_admin_token, get_client_uuid
|
|
5
|
+
from ..logger import init_logger
|
|
6
|
+
from .json_transformer import KeycloakConfigTransformer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = init_logger("serverkit.keycloak.authorization")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class KeycloakAuthorizationAPI:
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self.base_url = f"{settings.SERVER_URL}/admin/realms/{settings.REALM}"
|
|
16
|
+
|
|
17
|
+
async def update_authorization_config(self, authorization_config: Dict) -> bool:
|
|
18
|
+
"""Update complete authorization configuration via API"""
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
# Get admin token and client UUID
|
|
22
|
+
admin_token = await get_admin_token()
|
|
23
|
+
if not admin_token:
|
|
24
|
+
logger.error("Failed to get admin token")
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
client_uuid = await get_client_uuid(admin_token)
|
|
28
|
+
if not client_uuid:
|
|
29
|
+
logger.error("Failed to get client UUID")
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
# Update authorization settings
|
|
33
|
+
url = f"{self.base_url}/clients/{client_uuid}/authz/resource-server"
|
|
34
|
+
headers = {
|
|
35
|
+
'Authorization': f'Bearer {admin_token}',
|
|
36
|
+
'Content-Type': 'application/json'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async with aiohttp.ClientSession() as session:
|
|
40
|
+
async with session.put(url, json=authorization_config, headers=headers) as response:
|
|
41
|
+
if response.status == 200:
|
|
42
|
+
logger.info("Authorization configuration updated successfully")
|
|
43
|
+
return True
|
|
44
|
+
else:
|
|
45
|
+
error_text = await response.text()
|
|
46
|
+
logger.error(f"Failed to update authorization config: {error_text}")
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
except Exception as e:
|
|
50
|
+
logger.error(f"Error updating authorization configuration: {e}")
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
async def sync_configuration(self, config_file_path: str) -> bool:
|
|
54
|
+
"""Load, transform, and sync configuration"""
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
# Transform configuration
|
|
58
|
+
transformer = KeycloakConfigTransformer()
|
|
59
|
+
keycloak_config = transformer.load_and_transform(config_file_path)
|
|
60
|
+
|
|
61
|
+
# Update via API
|
|
62
|
+
return await self.update_authorization_config(keycloak_config)
|
|
63
|
+
|
|
64
|
+
except Exception as e:
|
|
65
|
+
logger.error(f"Error syncing configuration: {e}")
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
async def get_current_authorization_config(self) -> Optional[Dict]:
|
|
69
|
+
"""Get current authorization configuration"""
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
admin_token = await get_admin_token()
|
|
73
|
+
client_uuid = await get_client_uuid(admin_token)
|
|
74
|
+
|
|
75
|
+
url = f"{self.base_url}/clients/{client_uuid}/authz/resource-server"
|
|
76
|
+
headers = {'Authorization': f'Bearer {admin_token}'}
|
|
77
|
+
|
|
78
|
+
async with aiohttp.ClientSession() as session:
|
|
79
|
+
async with session.get(url, headers=headers) as response:
|
|
80
|
+
if response.status == 200:
|
|
81
|
+
return await response.json()
|
|
82
|
+
else:
|
|
83
|
+
logger.error(f"Failed to get current config: {response.status}")
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
logger.error(f"Error getting current configuration: {e}")
|
|
88
|
+
return None
|