Files
I-SecretUpdate/services/secret_service.py
T
2025-12-19 12:58:58 +01:00

156 lines
5.1 KiB
Python

"""
Secret Service
Handles creation and removal of app registration secrets.
"""
from msgraph import GraphServiceClient
from msgraph.generated.models.password_credential import PasswordCredential
from msgraph.generated.applications.item.add_password.add_password_post_request_body import AddPasswordPostRequestBody
from msgraph.generated.applications.item.remove_password.remove_password_post_request_body import RemovePasswordPostRequestBody
from datetime import datetime, timedelta
from typing import Dict, List, Any
import config
class SecretService:
"""Service for managing app registration secrets."""
def __init__(self, graph_client: GraphServiceClient):
"""
Initialize the secret service.
Args:
graph_client: Authenticated Graph service client
"""
self.graph_client = graph_client
async def create_secret(
self,
app_object_id: str,
description: str,
years: int = None
) -> Dict[str, Any]:
"""
Create a new secret for an app registration.
Args:
app_object_id: The object ID of the app registration
description: Display name/description for the secret
years: Number of years until expiration (defaults to config.APP_SECRET_EXPIRATION_YEARS)
Returns:
Dict: Contains secret_text, key_id, and end_datetime
Raises:
Exception: If the API call fails
"""
try:
if years is None:
years = config.APP_SECRET_EXPIRATION_YEARS
# Calculate expiration date
end_date = datetime.now() + timedelta(days=365 * years)
# Create password credential
password_cred = PasswordCredential()
password_cred.display_name = description
password_cred.end_date_time = end_date
# Create request body
request_body = AddPasswordPostRequestBody()
request_body.password_credential = password_cred
# Call Graph API to add password
result = await self.graph_client.applications.by_application_id(
app_object_id
).add_password.post(request_body)
if result:
return {
'secret_text': result.secret_text,
'key_id': str(result.key_id),
'end_datetime': result.end_date_time,
'display_name': result.display_name
}
raise Exception("No result returned from add_password")
except Exception as e:
raise Exception(f"Failed to create secret: {str(e)}")
async def remove_old_secrets(
self,
app_object_id: str,
keep_key_id: str
) -> int:
"""
Remove all secrets except the specified one.
Args:
app_object_id: The object ID of the app registration
keep_key_id: The key ID to keep (newly created secret)
Returns:
int: Number of secrets removed
Raises:
Exception: If the API call fails
"""
try:
removed_count = 0
# Get the app with its password credentials
app = await self.graph_client.applications.by_application_id(app_object_id).get()
if app and app.password_credentials:
for cred in app.password_credentials:
# Remove if it's not the one we want to keep
if str(cred.key_id) != str(keep_key_id):
# Create request body
request_body = RemovePasswordPostRequestBody()
request_body.key_id = cred.key_id
# Call Graph API to remove password
await self.graph_client.applications.by_application_id(
app_object_id
).remove_password.post(request_body)
removed_count += 1
return removed_count
except Exception as e:
raise Exception(f"Failed to remove old secrets: {str(e)}")
async def list_secrets(self, app_object_id: str) -> List[Dict[str, Any]]:
"""
List all secrets for an app registration (metadata only, not the secret values).
Args:
app_object_id: The object ID of the app registration
Returns:
List[Dict]: List of secret metadata
Raises:
Exception: If the API call fails
"""
try:
app = await self.graph_client.applications.by_application_id(app_object_id).get()
secrets = []
if app and app.password_credentials:
for cred in app.password_credentials:
secrets.append({
'key_id': str(cred.key_id),
'display_name': cred.display_name,
'start_datetime': cred.start_date_time,
'end_datetime': cred.end_date_time
})
return secrets
except Exception as e:
raise Exception(f"Failed to list secrets: {str(e)}")