mcp-eregistrations-bpa 0.8.5__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.
Potentially problematic release.
This version of mcp-eregistrations-bpa might be problematic. Click here for more details.
- mcp_eregistrations_bpa/__init__.py +121 -0
- mcp_eregistrations_bpa/__main__.py +6 -0
- mcp_eregistrations_bpa/arazzo/__init__.py +21 -0
- mcp_eregistrations_bpa/arazzo/expression.py +379 -0
- mcp_eregistrations_bpa/audit/__init__.py +56 -0
- mcp_eregistrations_bpa/audit/context.py +66 -0
- mcp_eregistrations_bpa/audit/logger.py +236 -0
- mcp_eregistrations_bpa/audit/models.py +131 -0
- mcp_eregistrations_bpa/auth/__init__.py +64 -0
- mcp_eregistrations_bpa/auth/callback.py +391 -0
- mcp_eregistrations_bpa/auth/cas.py +409 -0
- mcp_eregistrations_bpa/auth/oidc.py +252 -0
- mcp_eregistrations_bpa/auth/permissions.py +162 -0
- mcp_eregistrations_bpa/auth/token_manager.py +348 -0
- mcp_eregistrations_bpa/bpa_client/__init__.py +84 -0
- mcp_eregistrations_bpa/bpa_client/client.py +740 -0
- mcp_eregistrations_bpa/bpa_client/endpoints.py +193 -0
- mcp_eregistrations_bpa/bpa_client/errors.py +276 -0
- mcp_eregistrations_bpa/bpa_client/models.py +203 -0
- mcp_eregistrations_bpa/config.py +349 -0
- mcp_eregistrations_bpa/db/__init__.py +21 -0
- mcp_eregistrations_bpa/db/connection.py +64 -0
- mcp_eregistrations_bpa/db/migrations.py +168 -0
- mcp_eregistrations_bpa/exceptions.py +39 -0
- mcp_eregistrations_bpa/py.typed +0 -0
- mcp_eregistrations_bpa/rollback/__init__.py +19 -0
- mcp_eregistrations_bpa/rollback/manager.py +616 -0
- mcp_eregistrations_bpa/server.py +152 -0
- mcp_eregistrations_bpa/tools/__init__.py +372 -0
- mcp_eregistrations_bpa/tools/actions.py +155 -0
- mcp_eregistrations_bpa/tools/analysis.py +352 -0
- mcp_eregistrations_bpa/tools/audit.py +399 -0
- mcp_eregistrations_bpa/tools/behaviours.py +1042 -0
- mcp_eregistrations_bpa/tools/bots.py +627 -0
- mcp_eregistrations_bpa/tools/classifications.py +575 -0
- mcp_eregistrations_bpa/tools/costs.py +765 -0
- mcp_eregistrations_bpa/tools/debug_strategies.py +351 -0
- mcp_eregistrations_bpa/tools/debugger.py +1230 -0
- mcp_eregistrations_bpa/tools/determinants.py +2235 -0
- mcp_eregistrations_bpa/tools/document_requirements.py +670 -0
- mcp_eregistrations_bpa/tools/export.py +899 -0
- mcp_eregistrations_bpa/tools/fields.py +162 -0
- mcp_eregistrations_bpa/tools/form_errors.py +36 -0
- mcp_eregistrations_bpa/tools/formio_helpers.py +971 -0
- mcp_eregistrations_bpa/tools/forms.py +1269 -0
- mcp_eregistrations_bpa/tools/jsonlogic_builder.py +466 -0
- mcp_eregistrations_bpa/tools/large_response.py +163 -0
- mcp_eregistrations_bpa/tools/messages.py +523 -0
- mcp_eregistrations_bpa/tools/notifications.py +241 -0
- mcp_eregistrations_bpa/tools/registration_institutions.py +680 -0
- mcp_eregistrations_bpa/tools/registrations.py +897 -0
- mcp_eregistrations_bpa/tools/role_status.py +447 -0
- mcp_eregistrations_bpa/tools/role_units.py +400 -0
- mcp_eregistrations_bpa/tools/roles.py +1236 -0
- mcp_eregistrations_bpa/tools/rollback.py +335 -0
- mcp_eregistrations_bpa/tools/services.py +674 -0
- mcp_eregistrations_bpa/tools/workflows.py +2487 -0
- mcp_eregistrations_bpa/tools/yaml_transformer.py +991 -0
- mcp_eregistrations_bpa/workflows/__init__.py +28 -0
- mcp_eregistrations_bpa/workflows/loader.py +440 -0
- mcp_eregistrations_bpa/workflows/models.py +336 -0
- mcp_eregistrations_bpa-0.8.5.dist-info/METADATA +965 -0
- mcp_eregistrations_bpa-0.8.5.dist-info/RECORD +66 -0
- mcp_eregistrations_bpa-0.8.5.dist-info/WHEEL +4 -0
- mcp_eregistrations_bpa-0.8.5.dist-info/entry_points.txt +2 -0
- mcp_eregistrations_bpa-0.8.5.dist-info/licenses/LICENSE +86 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Audit logger for tracking BPA operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from mcp_eregistrations_bpa.audit.models import AuditEntry, AuditStatus
|
|
10
|
+
from mcp_eregistrations_bpa.db import get_connection
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AuditEntryNotFoundError(Exception):
|
|
14
|
+
"""Raised when an audit entry is not found."""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AuditEntryImmutableError(Exception):
|
|
20
|
+
"""Raised when attempting to modify a finalized audit entry."""
|
|
21
|
+
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AuditLogger:
|
|
26
|
+
"""Append-only audit logger for BPA operations.
|
|
27
|
+
|
|
28
|
+
This logger implements the audit-before-write pattern:
|
|
29
|
+
1. Call record_pending() BEFORE executing the BPA operation
|
|
30
|
+
2. Call mark_success() or mark_failed() AFTER the operation completes
|
|
31
|
+
|
|
32
|
+
The audit log is append-only (NFR5): entries can only be created and
|
|
33
|
+
their status updated, never deleted or modified otherwise.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, db_path: Path | None = None):
|
|
37
|
+
"""Initialize the audit logger.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
db_path: Optional path to SQLite database. If None, uses default.
|
|
41
|
+
"""
|
|
42
|
+
self._db_path = db_path
|
|
43
|
+
|
|
44
|
+
async def record_pending(
|
|
45
|
+
self,
|
|
46
|
+
user_email: str,
|
|
47
|
+
operation_type: str,
|
|
48
|
+
object_type: str,
|
|
49
|
+
params: dict[str, Any],
|
|
50
|
+
object_id: str | None = None,
|
|
51
|
+
) -> str:
|
|
52
|
+
"""Record a pending operation BEFORE execution.
|
|
53
|
+
|
|
54
|
+
This method MUST be called before executing any BPA write operation.
|
|
55
|
+
It creates an audit entry with status='pending' that will be updated
|
|
56
|
+
after the operation completes.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
user_email: Email of the user performing the operation
|
|
60
|
+
operation_type: Type (create, update, delete, link, unlink)
|
|
61
|
+
object_type: Type (service, registration, field, determinant, action)
|
|
62
|
+
params: Parameters passed to the operation
|
|
63
|
+
object_id: Optional ID of the object being operated on
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
The audit entry ID (UUID string) for use with mark_success/mark_failed
|
|
67
|
+
"""
|
|
68
|
+
entry = AuditEntry.create(
|
|
69
|
+
user_email=user_email,
|
|
70
|
+
operation_type=operation_type,
|
|
71
|
+
object_type=object_type,
|
|
72
|
+
params=params,
|
|
73
|
+
object_id=object_id,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
async with get_connection(self._db_path) as conn:
|
|
77
|
+
await conn.execute(
|
|
78
|
+
"""
|
|
79
|
+
INSERT INTO audit_logs (
|
|
80
|
+
id, timestamp, user_email, operation_type, object_type,
|
|
81
|
+
object_id, params, status, result, rollback_state_id
|
|
82
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
83
|
+
""",
|
|
84
|
+
(
|
|
85
|
+
entry.id,
|
|
86
|
+
entry.timestamp,
|
|
87
|
+
entry.user_email,
|
|
88
|
+
entry.operation_type,
|
|
89
|
+
entry.object_type,
|
|
90
|
+
entry.object_id,
|
|
91
|
+
json.dumps(entry.params),
|
|
92
|
+
entry.status.value,
|
|
93
|
+
None,
|
|
94
|
+
None,
|
|
95
|
+
),
|
|
96
|
+
)
|
|
97
|
+
await conn.commit()
|
|
98
|
+
|
|
99
|
+
return entry.id
|
|
100
|
+
|
|
101
|
+
async def mark_success(self, audit_id: str, result: dict[str, Any]) -> None:
|
|
102
|
+
"""Mark operation as successful with result summary.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
audit_id: The audit entry ID returned by record_pending()
|
|
106
|
+
result: Summary of the operation result (e.g., created object ID)
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
AuditEntryNotFoundError: If audit_id does not exist.
|
|
110
|
+
AuditEntryImmutableError: If entry is not in PENDING status.
|
|
111
|
+
"""
|
|
112
|
+
async with get_connection(self._db_path) as conn:
|
|
113
|
+
# Check current status (append-only enforcement)
|
|
114
|
+
cursor = await conn.execute(
|
|
115
|
+
"SELECT status FROM audit_logs WHERE id = ?",
|
|
116
|
+
(audit_id,),
|
|
117
|
+
)
|
|
118
|
+
row = await cursor.fetchone()
|
|
119
|
+
if row is None:
|
|
120
|
+
raise AuditEntryNotFoundError(f"Audit entry '{audit_id}' not found")
|
|
121
|
+
if row["status"] != AuditStatus.PENDING.value:
|
|
122
|
+
raise AuditEntryImmutableError(
|
|
123
|
+
f"Cannot modify audit entry with status '{row['status']}'. "
|
|
124
|
+
"Only PENDING entries can be updated."
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
await conn.execute(
|
|
128
|
+
"UPDATE audit_logs SET status = ?, result = ? WHERE id = ?",
|
|
129
|
+
(AuditStatus.SUCCESS.value, json.dumps(result), audit_id),
|
|
130
|
+
)
|
|
131
|
+
await conn.commit()
|
|
132
|
+
|
|
133
|
+
async def mark_failed(self, audit_id: str, error_message: str) -> None:
|
|
134
|
+
"""Mark operation as failed with error details.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
audit_id: The audit entry ID returned by record_pending()
|
|
138
|
+
error_message: Description of the error that occurred
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
AuditEntryNotFoundError: If audit_id does not exist.
|
|
142
|
+
AuditEntryImmutableError: If entry is not in PENDING status.
|
|
143
|
+
"""
|
|
144
|
+
async with get_connection(self._db_path) as conn:
|
|
145
|
+
# Check current status (append-only enforcement)
|
|
146
|
+
cursor = await conn.execute(
|
|
147
|
+
"SELECT status FROM audit_logs WHERE id = ?",
|
|
148
|
+
(audit_id,),
|
|
149
|
+
)
|
|
150
|
+
row = await cursor.fetchone()
|
|
151
|
+
if row is None:
|
|
152
|
+
raise AuditEntryNotFoundError(f"Audit entry '{audit_id}' not found")
|
|
153
|
+
if row["status"] != AuditStatus.PENDING.value:
|
|
154
|
+
raise AuditEntryImmutableError(
|
|
155
|
+
f"Cannot modify audit entry with status '{row['status']}'. "
|
|
156
|
+
"Only PENDING entries can be updated."
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
await conn.execute(
|
|
160
|
+
"UPDATE audit_logs SET status = ?, result = ? WHERE id = ?",
|
|
161
|
+
(
|
|
162
|
+
AuditStatus.FAILED.value,
|
|
163
|
+
json.dumps({"error": error_message}),
|
|
164
|
+
audit_id,
|
|
165
|
+
),
|
|
166
|
+
)
|
|
167
|
+
await conn.commit()
|
|
168
|
+
|
|
169
|
+
async def save_rollback_state(
|
|
170
|
+
self,
|
|
171
|
+
audit_id: str,
|
|
172
|
+
object_type: str,
|
|
173
|
+
object_id: str,
|
|
174
|
+
previous_state: dict[str, Any],
|
|
175
|
+
) -> str:
|
|
176
|
+
"""Save rollback state for an audit entry.
|
|
177
|
+
|
|
178
|
+
This enables rollback capability for update/delete operations by
|
|
179
|
+
storing the previous state of the object before changes were made.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
audit_id: The audit entry ID to associate the rollback state with.
|
|
183
|
+
object_type: Type of object (service, registration, etc.).
|
|
184
|
+
object_id: ID of the object.
|
|
185
|
+
previous_state: The object state before the operation.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
The rollback state ID (UUID string).
|
|
189
|
+
"""
|
|
190
|
+
import uuid
|
|
191
|
+
|
|
192
|
+
rollback_state_id = str(uuid.uuid4())
|
|
193
|
+
|
|
194
|
+
async with get_connection(self._db_path) as conn:
|
|
195
|
+
# Insert rollback state
|
|
196
|
+
await conn.execute(
|
|
197
|
+
"""
|
|
198
|
+
INSERT INTO rollback_states (
|
|
199
|
+
id, audit_log_id, object_type, object_id, previous_state
|
|
200
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
201
|
+
""",
|
|
202
|
+
(
|
|
203
|
+
rollback_state_id,
|
|
204
|
+
audit_id,
|
|
205
|
+
object_type,
|
|
206
|
+
object_id,
|
|
207
|
+
json.dumps(previous_state),
|
|
208
|
+
),
|
|
209
|
+
)
|
|
210
|
+
# Update audit entry with rollback_state_id
|
|
211
|
+
await conn.execute(
|
|
212
|
+
"UPDATE audit_logs SET rollback_state_id = ? WHERE id = ?",
|
|
213
|
+
(rollback_state_id, audit_id),
|
|
214
|
+
)
|
|
215
|
+
await conn.commit()
|
|
216
|
+
|
|
217
|
+
return rollback_state_id
|
|
218
|
+
|
|
219
|
+
async def get_entry(self, audit_id: str) -> AuditEntry | None:
|
|
220
|
+
"""Retrieve audit entry by ID.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
audit_id: The audit entry ID to retrieve
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
The AuditEntry if found, None otherwise
|
|
227
|
+
"""
|
|
228
|
+
async with get_connection(self._db_path) as conn:
|
|
229
|
+
cursor = await conn.execute(
|
|
230
|
+
"SELECT * FROM audit_logs WHERE id = ?",
|
|
231
|
+
(audit_id,),
|
|
232
|
+
)
|
|
233
|
+
row = await cursor.fetchone()
|
|
234
|
+
if row is None:
|
|
235
|
+
return None
|
|
236
|
+
return AuditEntry.from_row(dict(row))
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Audit models for tracking BPA operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import uuid
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from datetime import UTC, datetime
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AuditStatus(str, Enum):
|
|
14
|
+
"""Status of an audit log entry."""
|
|
15
|
+
|
|
16
|
+
PENDING = "pending"
|
|
17
|
+
SUCCESS = "success"
|
|
18
|
+
FAILED = "failed"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class OperationType(str, Enum):
|
|
22
|
+
"""Type of operation being audited."""
|
|
23
|
+
|
|
24
|
+
CREATE = "create"
|
|
25
|
+
UPDATE = "update"
|
|
26
|
+
DELETE = "delete"
|
|
27
|
+
LINK = "link"
|
|
28
|
+
UNLINK = "unlink"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ObjectType(str, Enum):
|
|
32
|
+
"""Type of object being operated on."""
|
|
33
|
+
|
|
34
|
+
SERVICE = "service"
|
|
35
|
+
REGISTRATION = "registration"
|
|
36
|
+
FIELD = "field"
|
|
37
|
+
DETERMINANT = "determinant"
|
|
38
|
+
ACTION = "action"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class AuditEntry:
|
|
43
|
+
"""Represents a single audit log entry for a BPA operation.
|
|
44
|
+
|
|
45
|
+
Audit entries are created BEFORE operations execute (with status='pending')
|
|
46
|
+
and updated AFTER completion (with status='success' or 'failed').
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
id: str
|
|
50
|
+
timestamp: str
|
|
51
|
+
user_email: str
|
|
52
|
+
operation_type: str
|
|
53
|
+
object_type: str
|
|
54
|
+
params: dict[str, Any]
|
|
55
|
+
status: AuditStatus = AuditStatus.PENDING
|
|
56
|
+
object_id: str | None = None
|
|
57
|
+
result: dict[str, Any] | None = None
|
|
58
|
+
rollback_state_id: str | None = None
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def create(
|
|
62
|
+
cls,
|
|
63
|
+
user_email: str,
|
|
64
|
+
operation_type: str,
|
|
65
|
+
object_type: str,
|
|
66
|
+
params: dict[str, Any],
|
|
67
|
+
object_id: str | None = None,
|
|
68
|
+
) -> AuditEntry:
|
|
69
|
+
"""Factory method for creating new audit entries.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
user_email: Email of the user performing the operation
|
|
73
|
+
operation_type: Type (create, update, delete, link, unlink)
|
|
74
|
+
object_type: Type (service, registration, field, determinant, action)
|
|
75
|
+
params: Parameters passed to the operation
|
|
76
|
+
object_id: Optional ID of the object being operated on
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
A new AuditEntry with generated UUID and timestamp
|
|
80
|
+
"""
|
|
81
|
+
return cls(
|
|
82
|
+
id=str(uuid.uuid4()),
|
|
83
|
+
timestamp=datetime.now(UTC).isoformat(),
|
|
84
|
+
user_email=user_email,
|
|
85
|
+
operation_type=operation_type,
|
|
86
|
+
object_type=object_type,
|
|
87
|
+
params=params,
|
|
88
|
+
object_id=object_id,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def to_dict(self) -> dict[str, Any]:
|
|
92
|
+
"""Convert to dict for database storage.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Dictionary with all fields, params/result serialized as JSON strings
|
|
96
|
+
"""
|
|
97
|
+
return {
|
|
98
|
+
"id": self.id,
|
|
99
|
+
"timestamp": self.timestamp,
|
|
100
|
+
"user_email": self.user_email,
|
|
101
|
+
"operation_type": self.operation_type,
|
|
102
|
+
"object_type": self.object_type,
|
|
103
|
+
"object_id": self.object_id,
|
|
104
|
+
"params": json.dumps(self.params),
|
|
105
|
+
"status": self.status.value,
|
|
106
|
+
"result": json.dumps(self.result) if self.result else None,
|
|
107
|
+
"rollback_state_id": self.rollback_state_id,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
def from_row(cls, row: dict[str, Any]) -> AuditEntry:
|
|
112
|
+
"""Create from database row.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
row: Dictionary with database column values
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
AuditEntry instance with deserialized data
|
|
119
|
+
"""
|
|
120
|
+
return cls(
|
|
121
|
+
id=row["id"],
|
|
122
|
+
timestamp=row["timestamp"],
|
|
123
|
+
user_email=row["user_email"],
|
|
124
|
+
operation_type=row["operation_type"],
|
|
125
|
+
object_type=row["object_type"],
|
|
126
|
+
object_id=row["object_id"],
|
|
127
|
+
params=json.loads(row["params"]),
|
|
128
|
+
status=AuditStatus(row["status"]),
|
|
129
|
+
result=json.loads(row["result"]) if row["result"] else None,
|
|
130
|
+
rollback_state_id=row["rollback_state_id"],
|
|
131
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Authentication module for Keycloak OIDC and CAS.
|
|
2
|
+
|
|
3
|
+
This module provides authentication for both:
|
|
4
|
+
- Keycloak: OIDC/PKCE authentication (modern BPA systems)
|
|
5
|
+
- CAS: OAuth2 Basic Auth authentication (legacy BPA systems)
|
|
6
|
+
|
|
7
|
+
Both flows include browser-based login, token management, automatic refresh,
|
|
8
|
+
and permission enforcement.
|
|
9
|
+
|
|
10
|
+
Permission enforcement usage in MCP tools:
|
|
11
|
+
@mcp.tool()
|
|
12
|
+
async def service_create(name: str) -> dict[str, object]:
|
|
13
|
+
# Ensure write permission before proceeding
|
|
14
|
+
access_token = await ensure_write_permission()
|
|
15
|
+
# ... use access_token for BPA API call
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from mcp_eregistrations_bpa.auth.callback import CallbackServer
|
|
19
|
+
from mcp_eregistrations_bpa.auth.cas import perform_cas_browser_login
|
|
20
|
+
from mcp_eregistrations_bpa.auth.oidc import (
|
|
21
|
+
OIDCConfig,
|
|
22
|
+
build_authorization_url,
|
|
23
|
+
discover_oidc_config,
|
|
24
|
+
generate_pkce_pair,
|
|
25
|
+
generate_state,
|
|
26
|
+
perform_browser_login,
|
|
27
|
+
)
|
|
28
|
+
from mcp_eregistrations_bpa.auth.permissions import (
|
|
29
|
+
PERMISSION_SERVICE_DESIGNER,
|
|
30
|
+
PERMISSION_VIEWER,
|
|
31
|
+
WRITE_PERMISSIONS,
|
|
32
|
+
check_permission,
|
|
33
|
+
ensure_authenticated,
|
|
34
|
+
ensure_write_permission,
|
|
35
|
+
)
|
|
36
|
+
from mcp_eregistrations_bpa.auth.token_manager import (
|
|
37
|
+
TokenManager,
|
|
38
|
+
TokenResponse,
|
|
39
|
+
exchange_code_for_tokens,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
# OIDC (Keycloak)
|
|
44
|
+
"OIDCConfig",
|
|
45
|
+
"discover_oidc_config",
|
|
46
|
+
"generate_pkce_pair",
|
|
47
|
+
"generate_state",
|
|
48
|
+
"build_authorization_url",
|
|
49
|
+
"perform_browser_login",
|
|
50
|
+
"CallbackServer",
|
|
51
|
+
# CAS (Legacy)
|
|
52
|
+
"perform_cas_browser_login",
|
|
53
|
+
# Token management
|
|
54
|
+
"TokenManager",
|
|
55
|
+
"TokenResponse",
|
|
56
|
+
"exchange_code_for_tokens",
|
|
57
|
+
# Permissions
|
|
58
|
+
"PERMISSION_VIEWER",
|
|
59
|
+
"PERMISSION_SERVICE_DESIGNER",
|
|
60
|
+
"WRITE_PERMISSIONS",
|
|
61
|
+
"ensure_authenticated",
|
|
62
|
+
"ensure_write_permission",
|
|
63
|
+
"check_permission",
|
|
64
|
+
]
|