First commit

This commit is contained in:
2025-12-19 12:58:58 +01:00
parent cba66667f3
commit 87865e2c6d
26 changed files with 3343 additions and 0 deletions
View File
+149
View File
@@ -0,0 +1,149 @@
"""
Azure Authentication Module
Handles authentication to Azure services for Key Vault management.
"""
from azure.identity import InteractiveBrowserCredential
from azure.mgmt.resource import SubscriptionClient
from typing import Optional, List, Dict
class AzureAuthenticator:
"""Handles Azure authentication for Key Vault and resource management."""
def __init__(self):
"""
Initialize the Azure authenticator.
"""
self.tenant_id: Optional[str] = None
self.subscription_id: Optional[str] = None
self.credential: Optional[InteractiveBrowserCredential] = None
self.subscriptions: List[Dict[str, str]] = []
def authenticate(self, credential: InteractiveBrowserCredential = None) -> bool:
"""
Authenticate to Azure. Can reuse credential from Graph authenticator.
Args:
credential: Optional credential to reuse (from GraphAuthenticator)
Returns:
bool: True if authentication succeeded, False otherwise
Raises:
Exception: If authentication fails
"""
try:
if credential:
# Reuse credential from Graph authentication
self.credential = credential
else:
# Create new interactive browser credential with "organizations" tenant
# This allows the user to login with any organizational account
# additionally_allowed_tenants="*" allows acquiring tokens for any tenant
self.credential = InteractiveBrowserCredential(
tenant_id="organizations",
additionally_allowed_tenants=["*"]
)
# List all subscriptions (this will use the cached token from Graph auth)
sub_client = SubscriptionClient(self.credential)
subscriptions_list = list(sub_client.subscriptions.list())
# Extract tenant ID from the first subscription
if subscriptions_list:
# Get tenant ID from subscription (format: /subscriptions/{sub-id})
# The tenant info is in the subscription object
first_sub = subscriptions_list[0]
self.tenant_id = first_sub.tenant_id if hasattr(first_sub, 'tenant_id') else None
if self.tenant_id:
print(f"Detected Tenant ID: {self.tenant_id}")
if subscriptions_list:
print(f"Successfully authenticated to Azure. Found {len(subscriptions_list)} subscription(s).")
# Store subscriptions for later selection
self.subscriptions = [
{
'id': sub.subscription_id,
'name': sub.display_name
}
for sub in subscriptions_list
]
return True
return False
except Exception as e:
raise Exception(f"Azure authentication failed: {str(e)}")
def get_credential(self) -> InteractiveBrowserCredential:
"""
Get the credential object.
Returns:
InteractiveBrowserCredential: The credential object
Raises:
Exception: If not authenticated
"""
if not self.credential:
raise Exception("Not authenticated. Call authenticate() first.")
return self.credential
def get_subscriptions(self) -> List[Dict[str, str]]:
"""
Get the list of available subscriptions.
Returns:
List[Dict]: List of subscriptions with 'id' and 'name'
"""
return self.subscriptions
def set_subscription(self, subscription_id: str):
"""
Set the active subscription ID.
Args:
subscription_id: The subscription ID to use
"""
self.subscription_id = subscription_id
def get_subscription_id(self) -> str:
"""
Get the subscription ID.
Returns:
str: The subscription ID
Raises:
Exception: If subscription not set
"""
if not self.subscription_id:
raise Exception("Subscription not set. Call set_subscription() first.")
return self.subscription_id
def get_tenant_id(self) -> str:
"""
Get the tenant ID.
Returns:
str: The tenant ID
Raises:
Exception: If not authenticated
"""
if not self.tenant_id:
raise Exception("Tenant ID not available. Call authenticate() first.")
return self.tenant_id
def is_authenticated(self) -> bool:
"""
Check if currently authenticated.
Returns:
bool: True if authenticated, False otherwise
"""
return self.credential is not None
+105
View File
@@ -0,0 +1,105 @@
"""
Microsoft Graph Authentication Module
Handles authentication to Microsoft Graph API for app registration management.
"""
from azure.identity import InteractiveBrowserCredential
from msgraph import GraphServiceClient
from typing import Optional
import config
class GraphAuthenticator:
"""Handles Microsoft Graph authentication and client creation."""
def __init__(self, client_id: str = None):
"""
Initialize the Graph authenticator.
Args:
client_id: Application Client ID (defaults to config.CLIENT_ID)
"""
self.client_id = client_id or config.CLIENT_ID
self.credential: Optional[InteractiveBrowserCredential] = None
self.client: Optional[GraphServiceClient] = None
async def authenticate(self) -> bool:
"""
Authenticate to Microsoft Graph using interactive browser login.
Returns:
bool: True if authentication succeeded, False otherwise
Raises:
Exception: If authentication fails
"""
try:
# Create interactive browser credential
# Using "organizations" allows login with any organizational account
# additionally_allowed_tenants="*" allows acquiring tokens for any tenant (needed for Key Vault access)
self.credential = InteractiveBrowserCredential(
tenant_id="organizations",
client_id=self.client_id,
additionally_allowed_tenants=["*"]
)
# Define scopes for Microsoft Graph
scopes = ['https://graph.microsoft.com/.default']
# Create Graph service client
self.client = GraphServiceClient(
credentials=self.credential,
scopes=scopes
)
# Authenticate to Graph API FIRST
# This triggers the initial browser auth for Graph scope
# Then Management API will use SSO (single sign-on) from this auth
me = await self.client.me.get()
if me:
print(f"Successfully authenticated as: {me.display_name} ({me.user_principal_name})")
return True
return False
except Exception as e:
raise Exception(f"Graph authentication failed: {str(e)}")
def get_client(self) -> GraphServiceClient:
"""
Get the authenticated Graph service client.
Returns:
GraphServiceClient: The authenticated client
Raises:
Exception: If not authenticated
"""
if not self.client:
raise Exception("Not authenticated. Call authenticate() first.")
return self.client
def get_credential(self) -> InteractiveBrowserCredential:
"""
Get the credential object for reuse in other Azure services.
Returns:
InteractiveBrowserCredential: The credential object
Raises:
Exception: If not authenticated
"""
if not self.credential:
raise Exception("Not authenticated. Call authenticate() first.")
return self.credential
def is_authenticated(self) -> bool:
"""
Check if currently authenticated.
Returns:
bool: True if authenticated, False otherwise
"""
return self.client is not None and self.credential is not None