authix-python-sdk 1.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.
- authix/__init__.py +23 -0
- authix/api.py +81 -0
- authix/cli.py +252 -0
- authix/client.py +110 -0
- authix/decorators.py +109 -0
- authix/exceptions.py +42 -0
- authix/handlers.py +570 -0
- authix/middleware.py +237 -0
- authix_python_sdk-1.0.0.dist-info/METADATA +666 -0
- authix_python_sdk-1.0.0.dist-info/RECORD +14 -0
- authix_python_sdk-1.0.0.dist-info/WHEEL +5 -0
- authix_python_sdk-1.0.0.dist-info/entry_points.txt +2 -0
- authix_python_sdk-1.0.0.dist-info/licenses/LICENSE +21 -0
- authix_python_sdk-1.0.0.dist-info/top_level.txt +1 -0
authix/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authix Python SDK
|
|
3
|
+
Official Python SDK for the Authix authentication service
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__version__ = "1.0.0"
|
|
7
|
+
__author__ = "Authix Team"
|
|
8
|
+
__email__ = "support@authix.com"
|
|
9
|
+
|
|
10
|
+
from .api import AuthixAPI
|
|
11
|
+
from .client import APIClient
|
|
12
|
+
from .handlers import AuthHandler, AppsHandler, LicensesHandler, UsersHandler, SecurityHandler, WebhooksHandler
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"AuthixAPI",
|
|
16
|
+
"APIClient",
|
|
17
|
+
"AuthHandler",
|
|
18
|
+
"AppsHandler",
|
|
19
|
+
"LicensesHandler",
|
|
20
|
+
"UsersHandler",
|
|
21
|
+
"SecurityHandler",
|
|
22
|
+
"WebhooksHandler",
|
|
23
|
+
]
|
authix/api.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authix API - Main API interface
|
|
3
|
+
Official Python SDK for the Authix authentication service
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import asyncio
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from .client import APIClient
|
|
11
|
+
from .handlers import AuthHandler, AppsHandler, LicensesHandler, UsersHandler, SecurityHandler, WebhooksHandler
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AuthixAPI:
|
|
15
|
+
"""
|
|
16
|
+
Main Authix API client that provides access to all Authix services.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
import asyncio
|
|
20
|
+
from authix import AuthixAPI
|
|
21
|
+
|
|
22
|
+
async def main():
|
|
23
|
+
# Initialize with your API key
|
|
24
|
+
api = AuthixAPI(api_key="your_api_key_here")
|
|
25
|
+
|
|
26
|
+
# Register a new user
|
|
27
|
+
result = await api.auth.register(
|
|
28
|
+
username="testuser",
|
|
29
|
+
password="SecurePass123!",
|
|
30
|
+
license_key="AX-XXXX-XXXX-XXXX",
|
|
31
|
+
hwid="device_hash_here",
|
|
32
|
+
app_id="your_app_id",
|
|
33
|
+
app_name="Your App Name"
|
|
34
|
+
)
|
|
35
|
+
print("Registration result:", result)
|
|
36
|
+
|
|
37
|
+
# Login user
|
|
38
|
+
login_result = await api.auth.login(
|
|
39
|
+
username="testuser",
|
|
40
|
+
password="SecurePass123!",
|
|
41
|
+
hwid="device_hash_here",
|
|
42
|
+
app_id="your_app_id",
|
|
43
|
+
app_name="Your App Name"
|
|
44
|
+
)
|
|
45
|
+
print("Login result:", login_result)
|
|
46
|
+
|
|
47
|
+
asyncio.run(main())
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self,
|
|
51
|
+
api_key: Optional[str] = None,
|
|
52
|
+
server_url: Optional[str] = None,
|
|
53
|
+
timeout: int = 30):
|
|
54
|
+
"""
|
|
55
|
+
Initialize Authix API client
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
api_key: Your Authix API key (can be set via AUTHIX_API_KEY env var)
|
|
59
|
+
server_url: Authix server URL (can be set via AUTHIX_SERVER env var)
|
|
60
|
+
timeout: Request timeout in seconds
|
|
61
|
+
"""
|
|
62
|
+
# Get configuration from parameters or environment
|
|
63
|
+
self.api_key = api_key or os.getenv("AUTHIX_API_KEY")
|
|
64
|
+
self.server_url = server_url or os.getenv("AUTHIX_SERVER", "https://getauthix.online")
|
|
65
|
+
|
|
66
|
+
if not self.api_key:
|
|
67
|
+
raise RuntimeError("API key is required. Set it via parameter or AUTHIX_API_KEY environment variable")
|
|
68
|
+
|
|
69
|
+
# Initialize the HTTP client
|
|
70
|
+
self.client = APIClient(server_url=self.server_url, api_key=self.api_key, timeout=timeout)
|
|
71
|
+
|
|
72
|
+
# Initialize handlers
|
|
73
|
+
self.auth = AuthHandler(self.client)
|
|
74
|
+
self.apps = AppsHandler(self.client)
|
|
75
|
+
self.licenses = LicensesHandler(self.client)
|
|
76
|
+
self.users = UsersHandler(self.client)
|
|
77
|
+
self.security = SecurityHandler(self.client)
|
|
78
|
+
self.webhooks = WebhooksHandler(self.client)
|
|
79
|
+
|
|
80
|
+
def __repr__(self):
|
|
81
|
+
return f"AuthixAPI(server_url='{self.server_url}', api_key='...{self.api_key[-8:]}')"
|
authix/cli.py
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authix CLI
|
|
3
|
+
Command-line interface for Authix operations
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import asyncio
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
from typing import Dict, Any
|
|
12
|
+
|
|
13
|
+
from .api import AuthixAPI
|
|
14
|
+
from . import __version__
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def load_config() -> Dict[str, str]:
|
|
18
|
+
"""Load configuration from environment variables"""
|
|
19
|
+
config = {
|
|
20
|
+
'api_key': os.getenv('AUTHIX_API_KEY'),
|
|
21
|
+
'server_url': os.getenv('AUTHIX_SERVER', 'https://getauthix.online')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if not config['api_key']:
|
|
25
|
+
print("Error: AUTHIX_API_KEY must be set")
|
|
26
|
+
sys.exit(1)
|
|
27
|
+
|
|
28
|
+
return config
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def register_command(args):
|
|
32
|
+
"""Register a new user"""
|
|
33
|
+
config = load_config()
|
|
34
|
+
api = AuthixAPI(**config)
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
result = await api.auth.register(
|
|
38
|
+
username=args.username,
|
|
39
|
+
password=args.password,
|
|
40
|
+
license_key=args.license_key,
|
|
41
|
+
hwid=args.hwid,
|
|
42
|
+
app_id=args.app_id,
|
|
43
|
+
app_name=args.app_name
|
|
44
|
+
)
|
|
45
|
+
print(json.dumps(result, indent=2))
|
|
46
|
+
except Exception as e:
|
|
47
|
+
print(f"Error: {e}")
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
finally:
|
|
50
|
+
await api.client.close()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def login_command(args):
|
|
54
|
+
"""Login user"""
|
|
55
|
+
config = load_config()
|
|
56
|
+
api = AuthixAPI(**config)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
result = await api.auth.login(
|
|
60
|
+
username=args.username,
|
|
61
|
+
password=args.password,
|
|
62
|
+
hwid=args.hwid,
|
|
63
|
+
app_id=args.app_id,
|
|
64
|
+
app_name=args.app_name
|
|
65
|
+
)
|
|
66
|
+
print(json.dumps(result, indent=2))
|
|
67
|
+
except Exception as e:
|
|
68
|
+
print(f"Error: {e}")
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
finally:
|
|
71
|
+
await api.client.close()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def apps_command(args):
|
|
75
|
+
"""List applications"""
|
|
76
|
+
config = load_config()
|
|
77
|
+
api = AuthixAPI(**config)
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
apps = await api.apps.get_apps()
|
|
81
|
+
print(json.dumps(apps, indent=2))
|
|
82
|
+
except Exception as e:
|
|
83
|
+
print(f"Error: {e}")
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
finally:
|
|
86
|
+
await api.client.close()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def create_app_command(args):
|
|
90
|
+
"""Create new application"""
|
|
91
|
+
config = load_config()
|
|
92
|
+
api = AuthixAPI(**config)
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
data = {'name': args.name}
|
|
96
|
+
if args.description:
|
|
97
|
+
data['description'] = args.description
|
|
98
|
+
|
|
99
|
+
app = await api.apps.create_app(**data)
|
|
100
|
+
print(json.dumps(app, indent=2))
|
|
101
|
+
except Exception as e:
|
|
102
|
+
print(f"Error: {e}")
|
|
103
|
+
sys.exit(1)
|
|
104
|
+
finally:
|
|
105
|
+
await api.client.close()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
async def create_license_command(args):
|
|
109
|
+
"""Create license"""
|
|
110
|
+
config = load_config()
|
|
111
|
+
api = AuthixAPI(**config)
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
license_data = await api.licenses.create_license(
|
|
115
|
+
app_id=args.app_id,
|
|
116
|
+
days=args.days,
|
|
117
|
+
max_hwids=args.max_hwids
|
|
118
|
+
)
|
|
119
|
+
print(json.dumps(license_data, indent=2))
|
|
120
|
+
except Exception as e:
|
|
121
|
+
print(f"Error: {e}")
|
|
122
|
+
sys.exit(1)
|
|
123
|
+
finally:
|
|
124
|
+
await api.client.close()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
async def ban_hwid_command(args):
|
|
128
|
+
"""Ban HWID"""
|
|
129
|
+
config = load_config()
|
|
130
|
+
api = AuthixAPI(**config)
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
result = await api.security.ban_hwid(args.app_id, args.hwid)
|
|
134
|
+
print(json.dumps(result, indent=2))
|
|
135
|
+
except Exception as e:
|
|
136
|
+
print(f"Error: {e}")
|
|
137
|
+
sys.exit(1)
|
|
138
|
+
finally:
|
|
139
|
+
await api.client.close()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
async def create_webhook_command(args):
|
|
143
|
+
"""Create webhook"""
|
|
144
|
+
config = load_config()
|
|
145
|
+
api = AuthixAPI(**config)
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
webhook = await api.webhooks.create_webhook(
|
|
149
|
+
app_id=args.app_id,
|
|
150
|
+
url=args.url,
|
|
151
|
+
events=args.events,
|
|
152
|
+
secret=args.secret
|
|
153
|
+
)
|
|
154
|
+
print(json.dumps(webhook, indent=2))
|
|
155
|
+
except Exception as e:
|
|
156
|
+
print(f"Error: {e}")
|
|
157
|
+
sys.exit(1)
|
|
158
|
+
finally:
|
|
159
|
+
await api.client.close()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
async def health_command(args):
|
|
163
|
+
"""Check server health"""
|
|
164
|
+
config = load_config()
|
|
165
|
+
api = AuthixAPI(**config)
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
# Try to get apps as a health check
|
|
169
|
+
await api.apps.get_apps()
|
|
170
|
+
print("✅ Authix server is online and accessible")
|
|
171
|
+
except Exception as e:
|
|
172
|
+
print(f"❌ Server health check failed: {e}")
|
|
173
|
+
sys.exit(1)
|
|
174
|
+
finally:
|
|
175
|
+
await api.client.close()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def main():
|
|
179
|
+
"""Main CLI entry point"""
|
|
180
|
+
parser = argparse.ArgumentParser(
|
|
181
|
+
description='Authix CLI - Command-line interface for Authix',
|
|
182
|
+
prog='authix-cli'
|
|
183
|
+
)
|
|
184
|
+
parser.add_argument('--version', action='version', version=f'Authix CLI {__version__}')
|
|
185
|
+
|
|
186
|
+
subparsers = parser.add_subparsers(dest='command', help='Available commands')
|
|
187
|
+
|
|
188
|
+
# Auth commands
|
|
189
|
+
register_parser = subparsers.add_parser('register', help='Register a new user')
|
|
190
|
+
register_parser.add_argument('username', help='Username')
|
|
191
|
+
register_parser.add_argument('password', help='Password')
|
|
192
|
+
register_parser.add_argument('license_key', help='License key')
|
|
193
|
+
register_parser.add_argument('hwid', help='Hardware ID')
|
|
194
|
+
register_parser.add_argument('app_id', help='Application ID')
|
|
195
|
+
register_parser.add_argument('app_name', help='Application name')
|
|
196
|
+
register_parser.set_defaults(func=register_command)
|
|
197
|
+
|
|
198
|
+
login_parser = subparsers.add_parser('login', help='Login user')
|
|
199
|
+
login_parser.add_argument('username', help='Username')
|
|
200
|
+
login_parser.add_argument('password', help='Password')
|
|
201
|
+
login_parser.add_argument('hwid', help='Hardware ID')
|
|
202
|
+
login_parser.add_argument('app_id', help='Application ID')
|
|
203
|
+
login_parser.add_argument('app_name', help='Application name')
|
|
204
|
+
login_parser.set_defaults(func=login_command)
|
|
205
|
+
|
|
206
|
+
# App commands
|
|
207
|
+
apps_parser = subparsers.add_parser('apps', help='List all applications')
|
|
208
|
+
apps_parser.set_defaults(func=apps_command)
|
|
209
|
+
|
|
210
|
+
create_app_parser = subparsers.add_parser('create-app', help='Create a new application')
|
|
211
|
+
create_app_parser.add_argument('name', help='Application name')
|
|
212
|
+
create_app_parser.add_argument('--description', help='Application description')
|
|
213
|
+
create_app_parser.set_defaults(func=create_app_command)
|
|
214
|
+
|
|
215
|
+
# License commands
|
|
216
|
+
license_parser = subparsers.add_parser('create-license', help='Create a new license')
|
|
217
|
+
license_parser.add_argument('app_id', help='Application ID')
|
|
218
|
+
license_parser.add_argument('--days', type=int, default=30, help='License validity in days')
|
|
219
|
+
license_parser.add_argument('--max-hwids', type=int, default=3, help='Maximum allowed hardware IDs')
|
|
220
|
+
license_parser.set_defaults(func=create_license_command)
|
|
221
|
+
|
|
222
|
+
# Security commands
|
|
223
|
+
ban_hwid_parser = subparsers.add_parser('ban-hwid', help='Ban hardware ID')
|
|
224
|
+
ban_hwid_parser.add_argument('app_id', help='Application ID')
|
|
225
|
+
ban_hwid_parser.add_argument('hwid', help='Hardware ID to ban')
|
|
226
|
+
ban_hwid_parser.set_defaults(func=ban_hwid_command)
|
|
227
|
+
|
|
228
|
+
# Webhook commands
|
|
229
|
+
webhook_parser = subparsers.add_parser('create-webhook', help='Create webhook')
|
|
230
|
+
webhook_parser.add_argument('app_id', help='Application ID')
|
|
231
|
+
webhook_parser.add_argument('url', help='Webhook URL')
|
|
232
|
+
webhook_parser.add_argument('--events', nargs='+', required=True, help='Events to subscribe to')
|
|
233
|
+
webhook_parser.add_argument('--secret', help='Webhook secret')
|
|
234
|
+
webhook_parser.set_defaults(func=create_webhook_command)
|
|
235
|
+
|
|
236
|
+
# Health check
|
|
237
|
+
health_parser = subparsers.add_parser('health', help='Check server health')
|
|
238
|
+
health_parser.set_defaults(func=health_command)
|
|
239
|
+
|
|
240
|
+
# Parse arguments
|
|
241
|
+
args = parser.parse_args()
|
|
242
|
+
|
|
243
|
+
if not args.command:
|
|
244
|
+
parser.print_help()
|
|
245
|
+
sys.exit(1)
|
|
246
|
+
|
|
247
|
+
# Execute command
|
|
248
|
+
asyncio.run(args.func(args))
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
if __name__ == '__main__':
|
|
252
|
+
main()
|
authix/client.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authix HTTP Client
|
|
3
|
+
HTTP client for making requests to Authix API
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import asyncio
|
|
8
|
+
import aiohttp
|
|
9
|
+
from typing import Dict, Any, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class APIClient:
|
|
13
|
+
"""
|
|
14
|
+
HTTP client for Authix API requests
|
|
15
|
+
|
|
16
|
+
This class handles all HTTP communication with the Authix server.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, server_url: str, api_key: str, timeout: int = 30):
|
|
20
|
+
"""
|
|
21
|
+
Initialize API client
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
server_url: Base URL of Authix server
|
|
25
|
+
api_key: API key for authentication
|
|
26
|
+
timeout: Request timeout in seconds
|
|
27
|
+
"""
|
|
28
|
+
self.server_url = server_url.rstrip('/')
|
|
29
|
+
self.api_key = api_key
|
|
30
|
+
self.timeout = aiohttp.ClientTimeout(total=timeout)
|
|
31
|
+
self._session = None
|
|
32
|
+
|
|
33
|
+
async def _get_session(self) -> aiohttp.ClientSession:
|
|
34
|
+
"""Get or create aiohttp session"""
|
|
35
|
+
if self._session is None or self._session.closed:
|
|
36
|
+
headers = {
|
|
37
|
+
'Authorization': f'Bearer {self.api_key}',
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
'User-Agent': f'Authix-Python-SDK/1.0.0'
|
|
40
|
+
}
|
|
41
|
+
self._session = aiohttp.ClientSession(
|
|
42
|
+
headers=headers,
|
|
43
|
+
timeout=self.timeout
|
|
44
|
+
)
|
|
45
|
+
return self._session
|
|
46
|
+
|
|
47
|
+
async def request(self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
48
|
+
"""
|
|
49
|
+
Make HTTP request to Authix API
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
method: HTTP method (GET, POST, PUT, DELETE)
|
|
53
|
+
endpoint: API endpoint
|
|
54
|
+
data: Request data for POST/PUT requests
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Response data as dictionary
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
RuntimeError: If API request fails
|
|
61
|
+
"""
|
|
62
|
+
url = f"{self.server_url}/api/v1{endpoint}"
|
|
63
|
+
session = await self._get_session()
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
async with session.request(method, url, json=data) as response:
|
|
67
|
+
response_data = await response.json()
|
|
68
|
+
|
|
69
|
+
if response.status >= 400:
|
|
70
|
+
error_msg = response_data.get('error', f'HTTP {response.status}')
|
|
71
|
+
raise RuntimeError(error_msg)
|
|
72
|
+
|
|
73
|
+
return response_data
|
|
74
|
+
|
|
75
|
+
except aiohttp.ClientError as e:
|
|
76
|
+
raise RuntimeError(f"Network error: {str(e)}")
|
|
77
|
+
except json.JSONDecodeError:
|
|
78
|
+
raise RuntimeError("Invalid JSON response from server")
|
|
79
|
+
|
|
80
|
+
async def get(self, endpoint: str) -> Dict[str, Any]:
|
|
81
|
+
"""Make GET request"""
|
|
82
|
+
return await self.request('GET', endpoint)
|
|
83
|
+
|
|
84
|
+
async def post(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
85
|
+
"""Make POST request"""
|
|
86
|
+
return await self.request('POST', endpoint, data)
|
|
87
|
+
|
|
88
|
+
async def put(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
89
|
+
"""Make PUT request"""
|
|
90
|
+
return await self.request('PUT', endpoint, data)
|
|
91
|
+
|
|
92
|
+
async def delete(self, endpoint: str) -> Dict[str, Any]:
|
|
93
|
+
"""Make DELETE request"""
|
|
94
|
+
return await self.request('DELETE', endpoint)
|
|
95
|
+
|
|
96
|
+
async def close(self):
|
|
97
|
+
"""Close the HTTP session"""
|
|
98
|
+
if self._session and not self._session.closed:
|
|
99
|
+
await self._session.close()
|
|
100
|
+
|
|
101
|
+
def __del__(self):
|
|
102
|
+
"""Cleanup when client is destroyed"""
|
|
103
|
+
if self._session and not self._session.closed:
|
|
104
|
+
# Schedule closing for the next event loop iteration
|
|
105
|
+
try:
|
|
106
|
+
loop = asyncio.get_event_loop()
|
|
107
|
+
if loop.is_running():
|
|
108
|
+
loop.create_task(self.close())
|
|
109
|
+
except RuntimeError:
|
|
110
|
+
pass
|
authix/decorators.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SecureAuth Decorators
|
|
3
|
+
Authentication and authorization decorators for Flask
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from flask import g, request, redirect, url_for, abort, jsonify
|
|
7
|
+
from functools import wraps
|
|
8
|
+
from typing import Callable, Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def require_auth(f: Callable) -> Callable:
|
|
12
|
+
"""Decorator to require authentication"""
|
|
13
|
+
@wraps(f)
|
|
14
|
+
def decorated_function(*args, **kwargs):
|
|
15
|
+
if not hasattr(g, 'current_user') or not g.current_user:
|
|
16
|
+
if request.path.startswith('/api/'):
|
|
17
|
+
return jsonify({'success': False, 'error': 'Authentication required'}), 401
|
|
18
|
+
else:
|
|
19
|
+
return redirect(url_for('login'))
|
|
20
|
+
return f(*args, **kwargs)
|
|
21
|
+
return decorated_function
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def require_role(*required_roles: str) -> Callable:
|
|
25
|
+
"""Decorator to require specific role(s)"""
|
|
26
|
+
def decorator(f: Callable) -> Callable:
|
|
27
|
+
@wraps(f)
|
|
28
|
+
def decorated_function(*args, **kwargs):
|
|
29
|
+
if not hasattr(g, 'current_user') or not g.current_user:
|
|
30
|
+
if request.path.startswith('/api/'):
|
|
31
|
+
return jsonify({'success': False, 'error': 'Authentication required'}), 401
|
|
32
|
+
else:
|
|
33
|
+
return redirect(url_for('login'))
|
|
34
|
+
|
|
35
|
+
user_role = g.current_user.get('role')
|
|
36
|
+
if user_role not in required_roles:
|
|
37
|
+
if request.path.startswith('/api/'):
|
|
38
|
+
return jsonify({'success': False, 'error': 'Insufficient permissions'}), 403
|
|
39
|
+
else:
|
|
40
|
+
abort(403)
|
|
41
|
+
|
|
42
|
+
return f(*args, **kwargs)
|
|
43
|
+
return decorated_function
|
|
44
|
+
return decorator
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def require_verification(f: Callable) -> Callable:
|
|
48
|
+
"""Decorator to require user verification"""
|
|
49
|
+
@wraps(f)
|
|
50
|
+
def decorated_function(*args, **kwargs):
|
|
51
|
+
if not hasattr(g, 'current_user') or not g.current_user:
|
|
52
|
+
if request.path.startswith('/api/'):
|
|
53
|
+
return jsonify({'success': False, 'error': 'Authentication required'}), 401
|
|
54
|
+
else:
|
|
55
|
+
return redirect(url_for('login'))
|
|
56
|
+
|
|
57
|
+
if not g.current_user.get('is_verified', False):
|
|
58
|
+
if request.path.startswith('/api/'):
|
|
59
|
+
return jsonify({'success': False, 'error': 'Account not verified'}), 403
|
|
60
|
+
else:
|
|
61
|
+
abort(403)
|
|
62
|
+
|
|
63
|
+
return f(*args, **kwargs)
|
|
64
|
+
return decorated_function
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def require_permission(permission: str) -> Callable:
|
|
68
|
+
"""Decorator to require specific permission"""
|
|
69
|
+
def decorator(f: Callable) -> Callable:
|
|
70
|
+
@wraps(f)
|
|
71
|
+
def decorated_function(*args, **kwargs):
|
|
72
|
+
if not hasattr(g, 'current_user') or not g.current_user:
|
|
73
|
+
if request.path.startswith('/api/'):
|
|
74
|
+
return jsonify({'success': False, 'error': 'Authentication required'}), 401
|
|
75
|
+
else:
|
|
76
|
+
return redirect(url_for('login'))
|
|
77
|
+
|
|
78
|
+
# Check permission (this would need to be implemented in the API)
|
|
79
|
+
# For now, check if user is admin
|
|
80
|
+
user_role = g.current_user.get('role')
|
|
81
|
+
if user_role != 'admin':
|
|
82
|
+
if request.path.startswith('/api/'):
|
|
83
|
+
return jsonify({'success': False, 'error': 'Insufficient permissions'}), 403
|
|
84
|
+
else:
|
|
85
|
+
abort(403)
|
|
86
|
+
|
|
87
|
+
return f(*args, **kwargs)
|
|
88
|
+
return decorated_function
|
|
89
|
+
return decorator
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def optional_auth(f: Callable) -> Callable:
|
|
93
|
+
"""Decorator that makes authentication optional"""
|
|
94
|
+
@wraps(f)
|
|
95
|
+
def decorated_function(*args, **kwargs):
|
|
96
|
+
# User may or may not be authenticated
|
|
97
|
+
return f(*args, **kwargs)
|
|
98
|
+
return decorated_function
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def rate_limit(limit: int, window: int = 60) -> Callable:
|
|
102
|
+
"""Decorator for rate limiting"""
|
|
103
|
+
def decorator(f: Callable) -> Callable:
|
|
104
|
+
@wraps(f)
|
|
105
|
+
def decorated_function(*args, **kwargs):
|
|
106
|
+
# This is a placeholder - implement actual rate limiting
|
|
107
|
+
return f(*args, **kwargs)
|
|
108
|
+
return decorated_function
|
|
109
|
+
return decorator
|
authix/exceptions.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SecureAuth Exceptions
|
|
3
|
+
Custom exceptions for SecureAuth SDK
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SecureAuthError(Exception):
|
|
8
|
+
"""Base exception for SecureAuth errors"""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SecureAuthException(SecureAuthError):
|
|
13
|
+
"""Exception for SecureAuth API errors"""
|
|
14
|
+
def __init__(self, message: str, status_code: int = None, response_data: dict = None):
|
|
15
|
+
super().__init__(message)
|
|
16
|
+
self.status_code = status_code
|
|
17
|
+
self.response_data = response_data or {}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AuthenticationError(SecureAuthError):
|
|
21
|
+
"""Exception for authentication failures"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AuthorizationError(SecureAuthError):
|
|
26
|
+
"""Exception for authorization failures"""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ConfigurationError(SecureAuthError):
|
|
31
|
+
"""Exception for configuration errors"""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NetworkError(SecureAuthError):
|
|
36
|
+
"""Exception for network-related errors"""
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ValidationError(SecureAuthError):
|
|
41
|
+
"""Exception for validation errors"""
|
|
42
|
+
pass
|