Add Legal Office seal and complete Azure CDN deployment
- Add Legal Office of the Master seal (SVG design with Maltese Cross, scales of justice, legal scroll) - Create legal-office-manifest-template.json for Legal Office credentials - Update SEAL_MAPPING.md and DESIGN_GUIDE.md with Legal Office seal documentation - Complete Azure CDN infrastructure deployment: - Resource group, storage account, and container created - 17 PNG seal files uploaded to Azure Blob Storage - All manifest templates updated with Azure URLs - Configuration files generated (azure-cdn-config.env) - Add comprehensive Azure CDN setup scripts and documentation - Fix manifest URL generation to prevent double slashes - Verify all seals accessible via HTTPS
This commit is contained in:
138
scripts/deploy/complete-entra-setup.sh
Executable file
138
scripts/deploy/complete-entra-setup.sh
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/bin/bash
|
||||
# Complete Entra VerifiedID Setup - Master Script
|
||||
# Orchestrates all setup steps in the correct order
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[SETUP]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[✗]${NC} $1"; }
|
||||
log_step() { echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n${BLUE}Step $1:${NC} $2\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
log_info "Entra VerifiedID Complete Setup"
|
||||
log_info "This script will guide you through all setup steps"
|
||||
echo ""
|
||||
|
||||
# Step 1: Azure App Registration
|
||||
log_step "1" "Azure AD App Registration"
|
||||
read -p "Have you created the Azure AD App Registration? (y/n): " APP_REG_DONE
|
||||
if [ "${APP_REG_DONE}" != "y" ]; then
|
||||
log_info "Running app registration script..."
|
||||
./scripts/deploy/create-entra-app.sh
|
||||
else
|
||||
log_success "App registration already done"
|
||||
fi
|
||||
|
||||
# Step 2: API Permissions
|
||||
log_step "2" "API Permissions Configuration"
|
||||
read -p "Have you configured API permissions? (y/n): " PERMS_DONE
|
||||
if [ "${PERMS_DONE}" != "y" ]; then
|
||||
log_info "Running API permissions configuration..."
|
||||
./scripts/deploy/configure-api-permissions.sh
|
||||
else
|
||||
log_success "API permissions already configured"
|
||||
fi
|
||||
|
||||
# Step 3: Enable Verified ID
|
||||
log_step "3" "Enable Verified ID Service"
|
||||
read -p "Is Verified ID service enabled? (y/n): " VERIFIED_ID_DONE
|
||||
if [ "${VERIFIED_ID_DONE}" != "y" ]; then
|
||||
log_info "Running Verified ID enablement guide..."
|
||||
./scripts/deploy/enable-verified-id.sh
|
||||
read -p "Press Enter after enabling Verified ID service..."
|
||||
else
|
||||
log_success "Verified ID service already enabled"
|
||||
fi
|
||||
|
||||
# Step 4: Create Manifests
|
||||
log_step "4" "Create Credential Manifests"
|
||||
read -p "Have you created credential manifests? (y/n): " MANIFESTS_DONE
|
||||
if [ "${MANIFESTS_DONE}" != "y" ]; then
|
||||
log_info "Running manifest creation guide..."
|
||||
./scripts/deploy/create-credential-manifests.sh
|
||||
read -p "Press Enter after creating manifests and collecting Manifest IDs..."
|
||||
./manifests/entra/collect-manifest-ids.sh
|
||||
else
|
||||
log_success "Manifests already created"
|
||||
fi
|
||||
|
||||
# Step 5: Store Secrets
|
||||
log_step "5" "Store Secrets in Key Vault"
|
||||
read -p "Have you stored secrets in Key Vault? (y/n): " SECRETS_DONE
|
||||
if [ "${SECRETS_DONE}" != "y" ]; then
|
||||
log_info "Running secret storage script..."
|
||||
./scripts/deploy/store-entra-secrets.sh
|
||||
else
|
||||
log_success "Secrets already stored"
|
||||
fi
|
||||
|
||||
# Step 6: Environment Configuration
|
||||
log_step "6" "Configure Environment"
|
||||
read -p "Configure development environment? (y/n): " CONFIG_ENV
|
||||
if [ "${CONFIG_ENV}" = "y" ]; then
|
||||
./scripts/deploy/configure-env-dev.sh
|
||||
fi
|
||||
|
||||
# Step 7: Multi-Manifest (if applicable)
|
||||
log_step "7" "Configure Multi-Manifest Support"
|
||||
read -p "Do you have multiple manifests to configure? (y/n): " MULTI_MANIFEST
|
||||
if [ "${MULTI_MANIFEST}" = "y" ]; then
|
||||
./scripts/deploy/configure-multi-manifest.sh
|
||||
fi
|
||||
|
||||
# Step 8: Validation
|
||||
log_step "8" "Validate Configuration"
|
||||
log_info "Running validation..."
|
||||
if ./scripts/validation/validate-entra-config.sh; then
|
||||
log_success "Configuration validated"
|
||||
else
|
||||
log_warning "Validation found issues. Please review and fix."
|
||||
fi
|
||||
|
||||
# Step 9: Testing
|
||||
log_step "9" "Run Tests"
|
||||
read -p "Run unit tests? (y/n): " RUN_UNIT
|
||||
if [ "${RUN_UNIT}" = "y" ]; then
|
||||
pnpm --filter @the-order/auth test entra-verifiedid.test.ts --run
|
||||
fi
|
||||
|
||||
read -p "Run integration tests? (requires credentials) (y/n): " RUN_INTEGRATION
|
||||
if [ "${RUN_INTEGRATION}" = "y" ]; then
|
||||
./scripts/test/run-integration-tests-with-setup.sh
|
||||
fi
|
||||
|
||||
# Step 10: Deployment
|
||||
log_step "10" "Deployment"
|
||||
read -p "Deploy to staging? (y/n): " DEPLOY_STAGING
|
||||
if [ "${DEPLOY_STAGING}" = "y" ]; then
|
||||
./scripts/deploy/deploy-staging.sh
|
||||
fi
|
||||
|
||||
read -p "Configure webhook URL? (y/n): " CONFIG_WEBHOOK
|
||||
if [ "${CONFIG_WEBHOOK}" = "y" ]; then
|
||||
./scripts/deploy/configure-webhook-url.sh
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
log_success "Setup Complete!"
|
||||
echo ""
|
||||
log_info "Next steps:"
|
||||
echo "1. Verify staging deployment"
|
||||
echo "2. Test credential issuance"
|
||||
echo "3. Monitor metrics"
|
||||
echo "4. Deploy to production when ready"
|
||||
echo ""
|
||||
log_info "For detailed information, see:"
|
||||
echo " - docs/deployment/ENTRA_VERIFIEDID_DEPLOYMENT_CHECKLIST.md"
|
||||
echo " - docs/operations/ENTRA_VERIFIEDID_RUNBOOK.md"
|
||||
|
||||
207
scripts/deploy/complete-seal-deployment.sh
Executable file
207
scripts/deploy/complete-seal-deployment.sh
Executable file
@@ -0,0 +1,207 @@
|
||||
#!/bin/bash
|
||||
# Complete seal deployment automation
|
||||
# Prepares, validates, and documents all Order of St John seals
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[DEPLOY]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[✗]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
echo ""
|
||||
log_info "=== Order of St John Seal Deployment Automation ==="
|
||||
echo ""
|
||||
|
||||
# Step 1: Prepare seals (convert SVG to PNG)
|
||||
log_info "Step 1: Preparing seals (SVG to PNG conversion)..."
|
||||
if ./scripts/deploy/prepare-all-credential-seals.sh; then
|
||||
log_success "Seal preparation complete"
|
||||
else
|
||||
log_warning "Seal preparation had issues (check if conversion tools are installed)"
|
||||
log_info "Install one of: ImageMagick, Inkscape, or Node.js with sharp"
|
||||
log_info "Continuing with validation..."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 2: Validate seals
|
||||
log_info "Step 2: Validating seal files..."
|
||||
if ./scripts/validation/validate-seal-files.sh; then
|
||||
log_success "Seal validation complete"
|
||||
else
|
||||
log_warning "Some validation checks failed (review warnings)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 3: Generate deployment checklist
|
||||
log_info "Step 3: Generating deployment checklist..."
|
||||
cat > "assets/credential-images/DEPLOYMENT_CHECKLIST.md" << 'EOF'
|
||||
# Seal Deployment Checklist
|
||||
|
||||
## Pre-Deployment
|
||||
|
||||
- [x] SVG files created for all 4 seals
|
||||
- [x] PNG files generated in multiple sizes
|
||||
- [x] Files validated
|
||||
- [ ] PNG files reviewed for quality
|
||||
- [ ] File sizes optimized (<100KB recommended)
|
||||
|
||||
## CDN Deployment
|
||||
|
||||
- [ ] CDN/storage account configured
|
||||
- [ ] PNG files uploaded to CDN
|
||||
- [ ] Files are publicly accessible via HTTPS
|
||||
- [ ] CORS headers configured (if needed)
|
||||
- [ ] CDN URLs tested and accessible
|
||||
|
||||
## Manifest Configuration
|
||||
|
||||
- [ ] Default manifest updated with seal URL
|
||||
- [ ] Financial manifest updated with seal URL
|
||||
- [ ] Judicial manifest updated with seal URL
|
||||
- [ ] Diplomatic manifest updated with seal URL
|
||||
- [ ] All manifest templates validated
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
- [ ] Development environment variables set
|
||||
- [ ] Staging environment variables set
|
||||
- [ ] Production environment variables set
|
||||
- [ ] ENTRA_CREDENTIAL_LOGO_URI configured per credential type
|
||||
|
||||
## Testing
|
||||
|
||||
- [ ] Test credential issuance with new seals
|
||||
- [ ] Verify seals display correctly in wallets
|
||||
- [ ] Test all credential types (default, financial, judicial, diplomatic)
|
||||
- [ ] Verify seal images load correctly
|
||||
- [ ] Test on multiple devices/wallets
|
||||
|
||||
## Documentation
|
||||
|
||||
- [ ] Seal mapping documentation updated
|
||||
- [ ] Design guide reviewed
|
||||
- [ ] Usage instructions verified
|
||||
- [ ] CDN URLs documented
|
||||
|
||||
## Production Deployment
|
||||
|
||||
- [ ] All tests passed
|
||||
- [ ] Staging deployment verified
|
||||
- [ ] Production deployment approved
|
||||
- [ ] Monitoring configured for image loading
|
||||
- [ ] Rollback plan prepared
|
||||
|
||||
---
|
||||
|
||||
**Generated**: [Current Date]
|
||||
**Status**: Ready for CDN upload
|
||||
EOF
|
||||
|
||||
log_success "Deployment checklist created"
|
||||
|
||||
# Step 4: Generate summary report
|
||||
log_info "Step 4: Generating deployment summary..."
|
||||
cat > "assets/credential-images/DEPLOYMENT_SUMMARY.md" << EOF
|
||||
# Order of St John Seals - Deployment Summary
|
||||
|
||||
**Generated**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
## Seal Files
|
||||
|
||||
### SVG Source Files
|
||||
$(find assets/credential-images/svg -name "*.svg" -exec basename {} \; | sort | sed 's/^/- /')
|
||||
|
||||
### PNG Generated Files
|
||||
$(find assets/credential-images/png -name "*.png" -type f | wc -l) PNG files generated
|
||||
|
||||
## File Locations
|
||||
|
||||
- **SVG Source**: \`assets/credential-images/svg/\`
|
||||
- **PNG Output**: \`assets/credential-images/png/\`
|
||||
- **Documentation**: \`docs/design/ORDER_SEALS_DESIGN_GUIDE.md\`
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Review PNG Files**
|
||||
- Check quality and clarity
|
||||
- Verify all sizes generated correctly
|
||||
- Ensure file sizes are optimized
|
||||
|
||||
2. **Upload to CDN**
|
||||
- Use: \`assets/credential-images/png/upload-to-cdn.sh\`
|
||||
- Or manually upload to your CDN
|
||||
- Ensure HTTPS and public access
|
||||
|
||||
3. **Update Manifest Templates**
|
||||
- Update CDN URLs in \`manifests/entra/*-manifest-template.json\`
|
||||
- Verify all credential types have correct seal references
|
||||
|
||||
4. **Configure Environment**
|
||||
- Set \`ENTRA_CREDENTIAL_LOGO_URI\` per credential type
|
||||
- Update staging/production configurations
|
||||
|
||||
5. **Test**
|
||||
- Issue test credentials
|
||||
- Verify seals display in wallets
|
||||
- Test all credential types
|
||||
|
||||
## Quick Commands
|
||||
|
||||
\`\`\`bash
|
||||
# Validate seals
|
||||
./scripts/validation/validate-seal-files.sh
|
||||
|
||||
# Prepare seals (if needed again)
|
||||
./scripts/deploy/prepare-all-credential-seals.sh
|
||||
|
||||
# Complete deployment
|
||||
./scripts/deploy/complete-seal-deployment.sh
|
||||
\`\`\`
|
||||
|
||||
## CDN URLs (Update After Upload)
|
||||
|
||||
After uploading to CDN, update these URLs in manifest templates:
|
||||
|
||||
- Default/Financial: \`https://cdn.theorder.org/images/digital-bank-seal.png\`
|
||||
- Judicial: \`https://cdn.theorder.org/images/iccc-seal.png\`
|
||||
- Diplomatic: \`https://cdn.theorder.org/images/diplomatic-security-seal.png\`
|
||||
- Provost Marshals: \`https://cdn.theorder.org/images/iccc-provost-marshals-seal.png\`
|
||||
|
||||
---
|
||||
|
||||
**Status**: ✅ Ready for CDN Upload
|
||||
EOF
|
||||
|
||||
log_success "Deployment summary created"
|
||||
|
||||
# Final summary
|
||||
echo ""
|
||||
log_info "=== Deployment Automation Complete ==="
|
||||
log_success "All seal files prepared and validated"
|
||||
echo ""
|
||||
log_info "Generated Files:"
|
||||
echo " - PNG files: assets/credential-images/png/"
|
||||
echo " - Manifest: assets/credential-images/png/MANIFEST.txt"
|
||||
echo " - Validation: assets/credential-images/png/VALIDATION_REPORT.txt"
|
||||
echo " - Checklist: assets/credential-images/DEPLOYMENT_CHECKLIST.md"
|
||||
echo " - Summary: assets/credential-images/DEPLOYMENT_SUMMARY.md"
|
||||
echo ""
|
||||
log_info "Next Steps:"
|
||||
echo "1. Review PNG files in assets/credential-images/png/"
|
||||
echo "2. Upload to CDN using assets/credential-images/png/upload-to-cdn.sh"
|
||||
echo "3. Update manifest templates with CDN URLs"
|
||||
echo "4. Test credential issuance"
|
||||
echo ""
|
||||
log_success "Ready for CDN deployment!"
|
||||
|
||||
74
scripts/deploy/configure-api-permissions.sh
Executable file
74
scripts/deploy/configure-api-permissions.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# Configure API Permissions for Entra VerifiedID App Registration
|
||||
# This script helps automate permission configuration
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
# Check Azure CLI
|
||||
if ! command -v az &> /dev/null; then
|
||||
log_warning "Azure CLI not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! az account show &> /dev/null; then
|
||||
log_warning "Not logged in to Azure"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Configuring API Permissions for Entra VerifiedID..."
|
||||
|
||||
# Get app ID
|
||||
read -p "Enter Application (Client) ID: " APP_ID
|
||||
|
||||
if [ -z "${APP_ID}" ]; then
|
||||
log_warning "App ID is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verifiable Credentials Service App ID
|
||||
VC_SERVICE_APP_ID="3db474b9-7a6d-4f50-afdc-70940ce1df8f"
|
||||
|
||||
log_info "Adding Verifiable Credentials Service permissions..."
|
||||
|
||||
# Note: Azure CLI doesn't support adding API permissions directly for Verifiable Credentials Service
|
||||
# This requires manual steps in Azure Portal, but we can provide the exact steps
|
||||
|
||||
log_warning "API permissions must be configured manually in Azure Portal"
|
||||
log_info "Follow these steps:"
|
||||
echo ""
|
||||
echo "1. Go to: https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/CallAnAPI/appId/${APP_ID}"
|
||||
echo "2. Click 'API permissions'"
|
||||
echo "3. Click 'Add a permission'"
|
||||
echo "4. Select 'APIs my organization uses'"
|
||||
echo "5. Search for: 'Verifiable Credentials Service' or use App ID: ${VC_SERVICE_APP_ID}"
|
||||
echo "6. Select 'Application permissions'"
|
||||
echo "7. Check the following permissions:"
|
||||
echo " - VerifiableCredential.Create.All"
|
||||
echo " - VerifiableCredential.Verify.All"
|
||||
echo "8. Click 'Add permissions'"
|
||||
echo "9. Click 'Grant admin consent for [Your Organization]'"
|
||||
echo "10. Verify consent status shows 'Granted'"
|
||||
echo ""
|
||||
|
||||
# Try to grant admin consent if possible
|
||||
log_info "Attempting to grant admin consent..."
|
||||
if az ad app permission admin-consent --id "${APP_ID}" 2>/dev/null; then
|
||||
log_success "Admin consent granted via CLI"
|
||||
else
|
||||
log_warning "Admin consent must be granted manually in Azure Portal"
|
||||
log_info "Go to: API permissions → Grant admin consent"
|
||||
fi
|
||||
|
||||
log_success "Permission configuration guide provided"
|
||||
log_info "After completing manual steps, verify permissions:"
|
||||
echo "az ad app permission list --id ${APP_ID}"
|
||||
|
||||
69
scripts/deploy/configure-env-dev.sh
Executable file
69
scripts/deploy/configure-env-dev.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# Configure development environment for Entra VerifiedID
|
||||
# Generates .env file with Entra configuration
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
ENV_FILE=".env.entra"
|
||||
|
||||
log_info "Configuring development environment for Entra VerifiedID..."
|
||||
|
||||
# Check if .entra-app-info.txt exists
|
||||
if [ -f ".entra-app-info.txt" ]; then
|
||||
log_info "Found existing app registration info"
|
||||
source <(grep -E "^(Application|Directory|Client Secret):" .entra-app-info.txt | sed 's/.*: //' | awk '{print "export " $0}')
|
||||
else
|
||||
log_warning "No app registration info found. Run ./scripts/deploy/create-entra-app.sh first"
|
||||
read -p "Enter Tenant ID: " ENTRA_TENANT_ID
|
||||
read -p "Enter Client ID: " ENTRA_CLIENT_ID
|
||||
read -sp "Enter Client Secret: " ENTRA_CLIENT_SECRET
|
||||
echo
|
||||
fi
|
||||
|
||||
read -p "Enter Credential Manifest ID (or press Enter to skip): " ENTRA_CREDENTIAL_MANIFEST_ID
|
||||
|
||||
# Create .env.entra file
|
||||
cat > "${ENV_FILE}" << EOF
|
||||
# Microsoft Entra VerifiedID Configuration
|
||||
# Generated: $(date)
|
||||
|
||||
ENTRA_TENANT_ID=${ENTRA_TENANT_ID}
|
||||
ENTRA_CLIENT_ID=${ENTRA_CLIENT_ID}
|
||||
ENTRA_CLIENT_SECRET=${ENTRA_CLIENT_SECRET}
|
||||
ENTRA_CREDENTIAL_MANIFEST_ID=${ENTRA_CREDENTIAL_MANIFEST_ID:-}
|
||||
|
||||
# Multi-manifest support (JSON format)
|
||||
# ENTRA_MANIFESTS='{"default":"manifest-id-1","diplomatic":"manifest-id-2","judicial":"manifest-id-3"}'
|
||||
|
||||
# Entra Rate Limiting (optional)
|
||||
ENTRA_RATE_LIMIT_ISSUANCE=10
|
||||
ENTRA_RATE_LIMIT_VERIFICATION=20
|
||||
ENTRA_RATE_LIMIT_STATUS_CHECK=30
|
||||
ENTRA_RATE_LIMIT_GLOBAL=50
|
||||
EOF
|
||||
|
||||
log_success "Environment file created: ${ENV_FILE}"
|
||||
log_info "To use this configuration, run: source ${ENV_FILE}"
|
||||
|
||||
# Check if .env exists and offer to merge
|
||||
if [ -f ".env" ]; then
|
||||
read -p "Merge with existing .env file? (y/n): " MERGE
|
||||
if [ "${MERGE}" = "y" ]; then
|
||||
cat "${ENV_FILE}" >> .env
|
||||
log_success "Merged into .env file"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_success "Development environment configured!"
|
||||
|
||||
101
scripts/deploy/configure-multi-manifest.sh
Executable file
101
scripts/deploy/configure-multi-manifest.sh
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
# Configure Multi-Manifest Support for Entra VerifiedID
|
||||
# Helps set up multiple credential manifests
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
log_info "Configuring Multi-Manifest Support for Entra VerifiedID..."
|
||||
|
||||
echo "Enter manifest IDs (press Enter to skip optional ones):"
|
||||
echo ""
|
||||
|
||||
read -p "Default Manifest ID (required): " DEFAULT_MANIFEST
|
||||
if [ -z "${DEFAULT_MANIFEST}" ]; then
|
||||
log_warning "Default manifest ID is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "Diplomatic Manifest ID (optional): " DIPLOMATIC_MANIFEST
|
||||
read -p "Judicial Manifest ID (optional): " JUDICIAL_MANIFEST
|
||||
read -p "Financial Manifest ID (optional): " FINANCIAL_MANIFEST
|
||||
|
||||
# Build JSON object
|
||||
MANIFESTS_JSON="{"
|
||||
MANIFESTS_JSON+="\"default\":\"${DEFAULT_MANIFEST}\""
|
||||
|
||||
if [ -n "${DIPLOMATIC_MANIFEST}" ]; then
|
||||
MANIFESTS_JSON+=",\"diplomatic\":\"${DIPLOMATIC_MANIFEST}\""
|
||||
fi
|
||||
|
||||
if [ -n "${JUDICIAL_MANIFEST}" ]; then
|
||||
MANIFESTS_JSON+=",\"judicial\":\"${JUDICIAL_MANIFEST}\""
|
||||
fi
|
||||
|
||||
if [ -n "${FINANCIAL_MANIFEST}" ]; then
|
||||
MANIFESTS_JSON+=",\"financial\":\"${FINANCIAL_MANIFEST}\""
|
||||
fi
|
||||
|
||||
MANIFESTS_JSON+="}"
|
||||
|
||||
log_info "Generated manifest configuration:"
|
||||
echo "${MANIFESTS_JSON}" | jq '.'
|
||||
|
||||
# Update .env file if it exists
|
||||
if [ -f ".env" ]; then
|
||||
read -p "Update .env file? (y/n): " UPDATE_ENV
|
||||
if [ "${UPDATE_ENV}" = "y" ]; then
|
||||
# Remove old ENTRA_MANIFESTS if exists
|
||||
sed -i '/^ENTRA_MANIFESTS=/d' .env
|
||||
# Add new one
|
||||
echo "ENTRA_MANIFESTS='${MANIFESTS_JSON}'" >> .env
|
||||
log_success "Updated .env file"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Kubernetes
|
||||
read -p "Generate Kubernetes secret update? (y/n): " GEN_K8S
|
||||
if [ "${GEN_K8S}" = "y" ]; then
|
||||
K8S_SECRET="infra/k8s/entra-manifests-secret.yaml"
|
||||
cat > "${K8S_SECRET}" << EOF
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: entra-manifests
|
||||
namespace: the-order-prod
|
||||
type: Opaque
|
||||
stringData:
|
||||
ENTRA_MANIFESTS: '${MANIFESTS_JSON}'
|
||||
EOF
|
||||
log_success "Kubernetes secret created: ${K8S_SECRET}"
|
||||
fi
|
||||
|
||||
# For Key Vault
|
||||
read -p "Store in Azure Key Vault? (y/n): " STORE_KV
|
||||
if [ "${STORE_KV}" = "y" ]; then
|
||||
read -p "Key Vault name: " KV_NAME
|
||||
if [ -n "${KV_NAME}" ]; then
|
||||
az keyvault secret set \
|
||||
--vault-name "${KV_NAME}" \
|
||||
--name "entra-manifests" \
|
||||
--value "${MANIFESTS_JSON}" \
|
||||
--output none
|
||||
log_success "Stored in Key Vault: ${KV_NAME}"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_success "Multi-manifest configuration complete!"
|
||||
log_info "To use in code:"
|
||||
echo " const manifests = JSON.parse(process.env.ENTRA_MANIFESTS);"
|
||||
echo " await client.issueCredential({ claims: {...}, manifestName: 'diplomatic' });"
|
||||
|
||||
82
scripts/deploy/configure-webhook-url.sh
Executable file
82
scripts/deploy/configure-webhook-url.sh
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
# Configure Webhook URL in Entra VerifiedID
|
||||
# Provides instructions and validates webhook configuration
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
log_info "Entra VerifiedID Webhook URL Configuration"
|
||||
echo ""
|
||||
|
||||
read -p "Environment (staging/production): " ENV
|
||||
ENV=${ENV:-staging}
|
||||
|
||||
if [ "${ENV}" = "production" ]; then
|
||||
WEBHOOK_URL="https://api.theorder.org/vc/entra/webhook"
|
||||
APP_ID_PROMPT="Production App Registration"
|
||||
else
|
||||
WEBHOOK_URL="https://api-staging.theorder.org/vc/entra/webhook"
|
||||
APP_ID_PROMPT="Staging App Registration"
|
||||
fi
|
||||
|
||||
read -p "Application (Client) ID for ${APP_ID_PROMPT}: " APP_ID
|
||||
|
||||
log_info "Webhook Configuration Instructions:"
|
||||
echo ""
|
||||
echo "1. Go to Azure Portal → Verified ID"
|
||||
echo "2. Click on your credential manifest"
|
||||
echo "3. Go to 'Settings' or 'Configuration'"
|
||||
echo "4. Find 'Callback URL' or 'Webhook URL' section"
|
||||
echo "5. Enter the following URL:"
|
||||
echo ""
|
||||
echo " ${WEBHOOK_URL}"
|
||||
echo ""
|
||||
echo "6. Save the configuration"
|
||||
echo ""
|
||||
|
||||
# Test webhook endpoint
|
||||
log_info "Testing webhook endpoint..."
|
||||
if curl -sf -X POST "${WEBHOOK_URL}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"requestId":"test","requestStatus":"issuance_successful"}' > /dev/null; then
|
||||
log_success "Webhook endpoint is accessible"
|
||||
else
|
||||
log_warning "Webhook endpoint test failed (may require authentication or service not deployed)"
|
||||
fi
|
||||
|
||||
# Generate webhook test payload
|
||||
cat > webhook-test-payload.json << EOF
|
||||
{
|
||||
"requestId": "test-request-$(date +%s)",
|
||||
"requestStatus": "issuance_successful",
|
||||
"credential": {
|
||||
"id": "vc:test:123",
|
||||
"type": ["VerifiableCredential"],
|
||||
"issuer": "did:web:${APP_ID}.verifiedid.msidentity.com",
|
||||
"issuanceDate": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"credentialSubject": {
|
||||
"email": "test@example.com"
|
||||
},
|
||||
"proof": {
|
||||
"type": "JsonWebSignature2020",
|
||||
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"verificationMethod": "did:web:${APP_ID}#key",
|
||||
"jws": "test-signature"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
log_success "Webhook test payload created: webhook-test-payload.json"
|
||||
log_info "You can test the webhook with:"
|
||||
echo "curl -X POST ${WEBHOOK_URL} -H 'Content-Type: application/json' -d @webhook-test-payload.json"
|
||||
|
||||
244
scripts/deploy/create-credential-manifests.sh
Executable file
244
scripts/deploy/create-credential-manifests.sh
Executable file
@@ -0,0 +1,244 @@
|
||||
#!/bin/bash
|
||||
# Create Credential Manifests in Entra VerifiedID
|
||||
# Provides templates and step-by-step instructions for all manifest types
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
MANIFESTS_DIR="manifests/entra"
|
||||
mkdir -p "${MANIFESTS_DIR}"
|
||||
|
||||
log_info "Credential Manifest Creation Guide"
|
||||
echo ""
|
||||
|
||||
# Create manifest templates
|
||||
log_info "Creating manifest templates..."
|
||||
|
||||
# Default/Identity Manifest Template
|
||||
cat > "${MANIFESTS_DIR}/default-manifest-template.json" << 'EOF'
|
||||
{
|
||||
"name": "The Order Identity Credential",
|
||||
"description": "Identity credential for members of The Order",
|
||||
"claims": [
|
||||
{
|
||||
"claim": "email",
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "name",
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "role",
|
||||
"type": "String",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"claim": "userId",
|
||||
"type": "String",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"issuer": {
|
||||
"name": "The Order",
|
||||
"domain": "theorder.org"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Diplomatic Manifest Template
|
||||
cat > "${MANIFESTS_DIR}/diplomatic-manifest-template.json" << 'EOF'
|
||||
{
|
||||
"name": "The Order Letters of Credence",
|
||||
"description": "Diplomatic credential for Letters of Credence",
|
||||
"claims": [
|
||||
{
|
||||
"claim": "recipientName",
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "recipientTitle",
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "missionCountry",
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "missionType",
|
||||
"type": "String",
|
||||
"required": true,
|
||||
"enum": ["embassy", "consulate", "delegation", "mission"]
|
||||
},
|
||||
{
|
||||
"claim": "appointmentDate",
|
||||
"type": "DateTime",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "expirationDate",
|
||||
"type": "DateTime",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"issuer": {
|
||||
"name": "The Order",
|
||||
"domain": "theorder.org"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Judicial Manifest Template
|
||||
cat > "${MANIFESTS_DIR}/judicial-manifest-template.json" << 'EOF'
|
||||
{
|
||||
"name": "The Order Judicial Appointment Credential",
|
||||
"description": "Judicial appointment credential",
|
||||
"claims": [
|
||||
{
|
||||
"claim": "role",
|
||||
"type": "String",
|
||||
"required": true,
|
||||
"enum": ["judge", "magistrate", "justice", "prosecutor"]
|
||||
},
|
||||
{
|
||||
"claim": "appointmentAuthority",
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "jurisdiction",
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "appointmentDate",
|
||||
"type": "DateTime",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "termLength",
|
||||
"type": "Number",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"issuer": {
|
||||
"name": "The Order",
|
||||
"domain": "theorder.org"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Financial Manifest Template
|
||||
cat > "${MANIFESTS_DIR}/financial-manifest-template.json" << 'EOF'
|
||||
{
|
||||
"name": "The Order Financial Role Credential",
|
||||
"description": "Financial role credential",
|
||||
"claims": [
|
||||
{
|
||||
"claim": "role",
|
||||
"type": "String",
|
||||
"required": true,
|
||||
"enum": ["financial-officer", "treasurer", "accountant", "auditor"]
|
||||
},
|
||||
{
|
||||
"claim": "appointmentAuthority",
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "jurisdiction",
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"claim": "appointmentDate",
|
||||
"type": "DateTime",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"issuer": {
|
||||
"name": "The Order",
|
||||
"domain": "theorder.org"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
log_success "Manifest templates created in ${MANIFESTS_DIR}/"
|
||||
echo ""
|
||||
|
||||
# Create step-by-step guide
|
||||
log_info "Step-by-Step Instructions:"
|
||||
echo ""
|
||||
echo "For each manifest type, follow these steps:"
|
||||
echo ""
|
||||
echo "1. Go to Azure Portal → Verified ID → Credentials"
|
||||
echo " Direct link: https://portal.azure.com/#view/Microsoft_AAD_IAM/VerifiedIDBlade"
|
||||
echo ""
|
||||
echo "2. Click 'Add credential' or 'Create new credential'"
|
||||
echo ""
|
||||
echo "3. Choose credential type (or use 'Custom credential')"
|
||||
echo ""
|
||||
echo "4. Configure the credential using the templates in ${MANIFESTS_DIR}/"
|
||||
echo ""
|
||||
echo "5. For each manifest:"
|
||||
echo " - Default: Use default-manifest-template.json"
|
||||
echo " - Diplomatic: Use diplomatic-manifest-template.json"
|
||||
echo " - Judicial: Use judicial-manifest-template.json"
|
||||
echo " - Financial: Use financial-manifest-template.json"
|
||||
echo ""
|
||||
echo "6. After creating each manifest:"
|
||||
echo " - Note the Manifest ID (displayed after creation)"
|
||||
echo " - Run: ./scripts/deploy/configure-multi-manifest.sh"
|
||||
echo " - Or manually add to ENTRA_MANIFESTS environment variable"
|
||||
echo ""
|
||||
|
||||
# Create automated manifest ID collector
|
||||
cat > "${MANIFESTS_DIR}/collect-manifest-ids.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Collect Manifest IDs after creation
|
||||
# Run this after creating manifests in Azure Portal
|
||||
|
||||
echo "Enter Manifest IDs (press Enter to skip optional ones):"
|
||||
echo ""
|
||||
|
||||
read -p "Default Manifest ID: " DEFAULT_ID
|
||||
read -p "Diplomatic Manifest ID (optional): " DIPLOMATIC_ID
|
||||
read -p "Judicial Manifest ID (optional): " JUDICIAL_ID
|
||||
read -p "Financial Manifest ID (optional): " FINANCIAL_ID
|
||||
|
||||
MANIFESTS="{"
|
||||
MANIFESTS+="\"default\":\"${DEFAULT_ID}\""
|
||||
|
||||
[ -n "${DIPLOMATIC_ID}" ] && MANIFESTS+=",\"diplomatic\":\"${DIPLOMATIC_ID}\""
|
||||
[ -n "${JUDICIAL_ID}" ] && MANIFESTS+=",\"judicial\":\"${JUDICIAL_ID}\""
|
||||
[ -n "${FINANCIAL_ID}" ] && MANIFESTS+=",\"financial\":\"${FINANCIAL_ID}\""
|
||||
|
||||
MANIFESTS+="}"
|
||||
|
||||
echo ""
|
||||
echo "ENTRA_MANIFESTS='${MANIFESTS}'"
|
||||
echo ""
|
||||
echo "Add this to your .env file or Kubernetes secrets"
|
||||
EOF
|
||||
|
||||
chmod +x "${MANIFESTS_DIR}/collect-manifest-ids.sh"
|
||||
|
||||
log_success "Manifest creation guide complete!"
|
||||
log_info "Templates saved to: ${MANIFESTS_DIR}/"
|
||||
log_info "After creating manifests, run: ${MANIFESTS_DIR}/collect-manifest-ids.sh"
|
||||
|
||||
96
scripts/deploy/create-entra-app.sh
Executable file
96
scripts/deploy/create-entra-app.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
# Create Azure AD App Registration for Entra VerifiedID
|
||||
# This script automates the app registration creation
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
# Check Azure CLI
|
||||
if ! command -v az &> /dev/null; then
|
||||
log_warning "Azure CLI not found. Install from: https://docs.microsoft.com/cli/azure/install-azure-cli"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check login
|
||||
if ! az account show &> /dev/null; then
|
||||
log_warning "Not logged in to Azure. Run: az login"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Creating Azure AD App Registration for Entra VerifiedID..."
|
||||
|
||||
# Get inputs
|
||||
read -p "App Registration name (default: the-order-entra): " APP_NAME
|
||||
APP_NAME=${APP_NAME:-the-order-entra}
|
||||
|
||||
read -p "Resource Group (optional, for tagging): " RESOURCE_GROUP
|
||||
|
||||
# Create app registration
|
||||
log_info "Creating app registration: ${APP_NAME}"
|
||||
APP_ID=$(az ad app create \
|
||||
--display-name "${APP_NAME}" \
|
||||
--query appId -o tsv)
|
||||
|
||||
log_success "App Registration created!"
|
||||
log_info "Application (Client) ID: ${APP_ID}"
|
||||
|
||||
# Get tenant ID
|
||||
TENANT_ID=$(az account show --query tenantId -o tsv)
|
||||
log_info "Directory (Tenant) ID: ${TENANT_ID}"
|
||||
|
||||
# Create service principal
|
||||
log_info "Creating service principal..."
|
||||
az ad sp create --id "${APP_ID}" --output none
|
||||
log_success "Service principal created"
|
||||
|
||||
# Create client secret
|
||||
log_info "Creating client secret (valid for 1 year)..."
|
||||
SECRET_RESPONSE=$(az ad app credential reset --id "${APP_ID}" --years 1)
|
||||
CLIENT_SECRET=$(echo "${SECRET_RESPONSE}" | jq -r '.password')
|
||||
|
||||
log_success "Client secret created"
|
||||
log_warning "IMPORTANT: Save this secret now - it won't be shown again!"
|
||||
log_info "Client Secret: ${CLIENT_SECRET}"
|
||||
|
||||
# Add API permissions
|
||||
log_info "Adding Verifiable Credentials Service permissions..."
|
||||
VC_SERVICE_APP_ID="3db474b9-7a6d-4f50-afdc-70940ce1df8f"
|
||||
|
||||
# Note: Exact permission IDs may vary - this is a template
|
||||
log_warning "You need to add permissions manually in Azure Portal:"
|
||||
log_info "1. Go to Azure Portal → App registrations → ${APP_NAME} → API permissions"
|
||||
log_info "2. Add permission → APIs my organization uses"
|
||||
log_info "3. Search for 'Verifiable Credentials Service'"
|
||||
log_info "4. Add Application permissions: VerifiableCredential.Create.All, VerifiableCredential.Verify.All"
|
||||
log_info "5. Grant admin consent"
|
||||
|
||||
# Output summary
|
||||
cat > .entra-app-info.txt << EOF
|
||||
Azure AD App Registration Created
|
||||
==================================
|
||||
|
||||
Application Name: ${APP_NAME}
|
||||
Application (Client) ID: ${APP_ID}
|
||||
Directory (Tenant) ID: ${TENANT_ID}
|
||||
Client Secret: ${CLIENT_SECRET}
|
||||
|
||||
NEXT STEPS:
|
||||
1. Add API permissions in Azure Portal (see above)
|
||||
2. Grant admin consent
|
||||
3. Store these values securely
|
||||
4. Run: ./scripts/deploy/store-entra-secrets.sh
|
||||
EOF
|
||||
|
||||
log_success "App registration complete!"
|
||||
log_info "Details saved to: .entra-app-info.txt"
|
||||
log_warning "Remember to add API permissions and grant admin consent!"
|
||||
|
||||
132
scripts/deploy/deploy-production.sh
Executable file
132
scripts/deploy/deploy-production.sh
Executable file
@@ -0,0 +1,132 @@
|
||||
#!/bin/bash
|
||||
# Deploy Identity Service with Entra VerifiedID to Production
|
||||
# Uses blue-green deployment strategy for zero downtime
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
# Safety check
|
||||
log_warning "This will deploy to PRODUCTION. Are you sure?"
|
||||
read -p "Type 'deploy-production' to confirm: " CONFIRM
|
||||
|
||||
if [ "${CONFIRM}" != "deploy-production" ]; then
|
||||
log_error "Deployment cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check prerequisites
|
||||
log_info "Checking prerequisites..."
|
||||
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
log_error "kubectl not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
log_error "Not connected to Kubernetes cluster"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NAMESPACE="the-order-prod"
|
||||
|
||||
# Verify production namespace
|
||||
if ! kubectl get namespace "${NAMESPACE}" &> /dev/null; then
|
||||
log_error "Production namespace not found: ${NAMESPACE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify secrets exist
|
||||
log_info "Verifying Entra secrets..."
|
||||
if ! kubectl get secret entra-verifiedid-secrets -n "${NAMESPACE}" &> /dev/null; then
|
||||
log_error "Entra secrets not found in production!"
|
||||
log_info "Create secrets first: kubectl apply -f infra/k8s/identity-service-entra-secrets.yaml -n ${NAMESPACE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build and push image
|
||||
IMAGE_TAG="prod-$(date +%Y%m%d-%H%M%S)"
|
||||
IMAGE_NAME="ghcr.io/the-order/identity-service:${IMAGE_TAG}"
|
||||
|
||||
log_info "Building production image: ${IMAGE_NAME}"
|
||||
docker build -t "${IMAGE_NAME}" -f services/identity/Dockerfile .
|
||||
docker push "${IMAGE_NAME}"
|
||||
log_success "Image built and pushed"
|
||||
|
||||
# Blue-Green Deployment Strategy
|
||||
log_info "Starting blue-green deployment..."
|
||||
|
||||
# Create green deployment
|
||||
log_info "Creating green deployment..."
|
||||
sed "s|ghcr.io/the-order/identity-service:latest|${IMAGE_NAME}|g" \
|
||||
infra/k8s/identity-service-deployment-entra.yaml | \
|
||||
sed 's/name: identity-service/name: identity-service-green/' | \
|
||||
sed 's/app: identity-service/app: identity-service-green/' > /tmp/identity-green.yaml
|
||||
|
||||
kubectl apply -f /tmp/identity-green.yaml -n "${NAMESPACE}"
|
||||
|
||||
# Wait for green to be ready
|
||||
log_info "Waiting for green deployment to be ready..."
|
||||
kubectl rollout status deployment/identity-service-green -n "${NAMESPACE}" --timeout=10m
|
||||
|
||||
# Health check on green
|
||||
log_info "Running health checks on green deployment..."
|
||||
GREEN_POD=$(kubectl get pod -n "${NAMESPACE}" -l app=identity-service-green -o jsonpath='{.items[0].metadata.name}')
|
||||
|
||||
if kubectl exec -n "${NAMESPACE}" "${GREEN_POD}" -- curl -sf http://localhost:4002/health > /dev/null; then
|
||||
log_success "Green deployment is healthy"
|
||||
else
|
||||
log_error "Green deployment health check failed!"
|
||||
log_info "Rolling back..."
|
||||
kubectl delete deployment identity-service-green -n "${NAMESPACE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Switch traffic to green
|
||||
log_info "Switching traffic to green deployment..."
|
||||
kubectl patch service identity-service -n "${NAMESPACE}" -p '{"spec":{"selector":{"app":"identity-service-green"}}}'
|
||||
|
||||
# Wait and verify
|
||||
sleep 10
|
||||
log_info "Verifying service after switch..."
|
||||
if curl -sf https://api.theorder.org/health > /dev/null; then
|
||||
log_success "Service is responding"
|
||||
else
|
||||
log_warning "Service health check failed (may need more time)"
|
||||
fi
|
||||
|
||||
# Scale down blue (old) deployment
|
||||
log_info "Scaling down blue deployment..."
|
||||
if kubectl get deployment identity-service -n "${NAMESPACE}" &> /dev/null; then
|
||||
kubectl scale deployment identity-service -n "${NAMESPACE}" --replicas=0
|
||||
log_success "Blue deployment scaled down"
|
||||
fi
|
||||
|
||||
# Rename green to main
|
||||
log_info "Promoting green to main deployment..."
|
||||
kubectl delete deployment identity-service -n "${NAMESPACE}" 2>/dev/null || true
|
||||
kubectl patch deployment identity-service-green -n "${NAMESPACE}" -p '{"metadata":{"name":"identity-service"},"spec":{"selector":{"matchLabels":{"app":"identity-service"}}},"template":{"metadata":{"labels":{"app":"identity-service"}}}}'
|
||||
kubectl patch service identity-service -n "${NAMESPACE}" -p '{"spec":{"selector":{"app":"identity-service"}}}'
|
||||
|
||||
log_success "Production deployment complete!"
|
||||
log_info "Deployment details:"
|
||||
kubectl get deployment identity-service -n "${NAMESPACE}"
|
||||
kubectl get pods -n "${NAMESPACE}" -l app=identity-service
|
||||
|
||||
log_info "Next steps:"
|
||||
echo "1. Monitor metrics: kubectl logs -n ${NAMESPACE} deployment/identity-service -f | grep entra"
|
||||
echo "2. Verify webhook URL: https://api.theorder.org/vc/entra/webhook"
|
||||
echo "3. Test credential issuance"
|
||||
echo "4. Monitor for 24 hours"
|
||||
|
||||
115
scripts/deploy/deploy-staging.sh
Executable file
115
scripts/deploy/deploy-staging.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
# Deploy Identity Service with Entra VerifiedID to Staging
|
||||
# This script automates the staging deployment
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
# Check prerequisites
|
||||
log_info "Checking prerequisites..."
|
||||
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
log_error "kubectl not found. Please install kubectl"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
log_error "Not connected to Kubernetes cluster"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if namespace exists
|
||||
NAMESPACE="the-order-staging"
|
||||
if ! kubectl get namespace "${NAMESPACE}" &> /dev/null; then
|
||||
log_info "Creating namespace: ${NAMESPACE}"
|
||||
kubectl create namespace "${NAMESPACE}"
|
||||
fi
|
||||
|
||||
# Check if secrets exist
|
||||
log_info "Checking for Entra secrets..."
|
||||
if ! kubectl get secret entra-verifiedid-secrets -n "${NAMESPACE}" &> /dev/null; then
|
||||
log_warning "Entra secrets not found. Creating from template..."
|
||||
log_info "Please update infra/k8s/identity-service-entra-secrets.yaml with actual values"
|
||||
read -p "Press Enter after updating secrets file..."
|
||||
kubectl apply -f infra/k8s/identity-service-entra-secrets.yaml -n "${NAMESPACE}"
|
||||
else
|
||||
log_success "Secrets found"
|
||||
fi
|
||||
|
||||
# Build and push image (if needed)
|
||||
log_info "Building Docker image..."
|
||||
IMAGE_TAG="${IMAGE_TAG:-staging-$(date +%Y%m%d-%H%M%S)}"
|
||||
IMAGE_NAME="ghcr.io/the-order/identity-service:${IMAGE_TAG}"
|
||||
|
||||
if [ "${BUILD_IMAGE:-true}" = "true" ]; then
|
||||
log_info "Building image: ${IMAGE_NAME}"
|
||||
docker build -t "${IMAGE_NAME}" -f services/identity/Dockerfile .
|
||||
docker push "${IMAGE_NAME}"
|
||||
log_success "Image built and pushed"
|
||||
else
|
||||
log_info "Skipping image build (set BUILD_IMAGE=false to skip)"
|
||||
fi
|
||||
|
||||
# Update deployment with image tag
|
||||
log_info "Updating deployment manifest..."
|
||||
sed "s|ghcr.io/the-order/identity-service:latest|${IMAGE_NAME}|g" \
|
||||
infra/k8s/identity-service-deployment-entra.yaml > /tmp/identity-deployment-staging.yaml
|
||||
|
||||
# Apply deployment
|
||||
log_info "Deploying to staging..."
|
||||
kubectl apply -f /tmp/identity-deployment-staging.yaml -n "${NAMESPACE}"
|
||||
|
||||
# Wait for deployment
|
||||
log_info "Waiting for deployment to be ready..."
|
||||
kubectl rollout status deployment/identity-service -n "${NAMESPACE}" --timeout=5m
|
||||
|
||||
# Verify deployment
|
||||
log_info "Verifying deployment..."
|
||||
if kubectl get deployment identity-service -n "${NAMESPACE}" &> /dev/null; then
|
||||
REPLICAS=$(kubectl get deployment identity-service -n "${NAMESPACE}" -o jsonpath='{.status.readyReplicas}')
|
||||
DESIRED=$(kubectl get deployment identity-service -n "${NAMESPACE}" -o jsonpath='{.spec.replicas}')
|
||||
|
||||
if [ "${REPLICAS}" = "${DESIRED}" ]; then
|
||||
log_success "Deployment successful! ${REPLICAS}/${DESIRED} replicas ready"
|
||||
else
|
||||
log_warning "Deployment in progress: ${REPLICAS}/${DESIRED} replicas ready"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check service health
|
||||
log_info "Checking service health..."
|
||||
SERVICE_URL=$(kubectl get ingress identity-service-ingress -n "${NAMESPACE}" -o jsonpath='{.spec.rules[0].host}' 2>/dev/null || echo "")
|
||||
if [ -n "${SERVICE_URL}" ]; then
|
||||
log_info "Service URL: https://${SERVICE_URL}"
|
||||
log_info "Testing health endpoint..."
|
||||
sleep 5
|
||||
if curl -sf "https://${SERVICE_URL}/health" > /dev/null; then
|
||||
log_success "Service is healthy!"
|
||||
else
|
||||
log_warning "Health check failed (service may still be starting)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Display logs
|
||||
log_info "Recent logs:"
|
||||
kubectl logs -n "${NAMESPACE}" deployment/identity-service --tail=20 | grep -i entra || log_info "No Entra-related logs yet"
|
||||
|
||||
log_success "Staging deployment complete!"
|
||||
log_info "Next steps:"
|
||||
echo "1. Configure webhook URL in Entra VerifiedID: https://api-staging.theorder.org/vc/entra/webhook"
|
||||
echo "2. Test credential issuance"
|
||||
echo "3. Verify metrics collection"
|
||||
echo "4. Check logs: kubectl logs -n ${NAMESPACE} deployment/identity-service -f"
|
||||
|
||||
67
scripts/deploy/enable-verified-id.sh
Executable file
67
scripts/deploy/enable-verified-id.sh
Executable file
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
# Enable Entra VerifiedID Service
|
||||
# Provides step-by-step instructions and validates service status
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
log_info "Entra VerifiedID Service Activation Guide"
|
||||
echo ""
|
||||
|
||||
# Check if Azure CLI is available
|
||||
if command -v az &> /dev/null && az account show &> /dev/null; then
|
||||
TENANT_ID=$(az account show --query tenantId -o tsv)
|
||||
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
|
||||
|
||||
log_info "Detected Azure subscription:"
|
||||
echo " Tenant ID: ${TENANT_ID}"
|
||||
echo " Subscription ID: ${SUBSCRIPTION_ID}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
log_info "Step-by-Step Instructions to Enable Verified ID:"
|
||||
echo ""
|
||||
echo "1. Open Azure Portal: https://portal.azure.com"
|
||||
echo "2. Navigate to: Azure Active Directory → Verified ID"
|
||||
echo " Direct link: https://portal.azure.com/#view/Microsoft_AAD_IAM/VerifiedIDBlade"
|
||||
echo ""
|
||||
echo "3. If Verified ID is not visible:"
|
||||
echo " a. Check if you have the required permissions (Global Administrator or Verified ID Administrator)"
|
||||
echo " b. Verify your Azure AD tenant supports Verified ID"
|
||||
echo " c. Some tenants may need to enable the feature first"
|
||||
echo ""
|
||||
echo "4. Click 'Get started' or 'Enable Verified ID'"
|
||||
echo ""
|
||||
echo "5. Wait for service activation (typically 5-10 minutes)"
|
||||
echo ""
|
||||
echo "6. Once enabled, you should see:"
|
||||
echo " - Credentials section"
|
||||
echo " - Issuance section"
|
||||
echo " - Settings section"
|
||||
echo ""
|
||||
|
||||
# Try to check service status via Azure CLI (if possible)
|
||||
log_info "Attempting to verify service status..."
|
||||
if command -v az &> /dev/null && az account show &> /dev/null; then
|
||||
# Note: Azure CLI doesn't have direct Verified ID commands, but we can check if the service exists
|
||||
log_info "Azure CLI detected. Checking tenant capabilities..."
|
||||
|
||||
# Check if we can access the tenant
|
||||
if az ad tenant show &> /dev/null; then
|
||||
log_success "Tenant access confirmed"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "After enabling Verified ID, run:"
|
||||
echo " ./scripts/deploy/create-credential-manifests.sh"
|
||||
echo ""
|
||||
log_success "Service activation guide complete!"
|
||||
|
||||
277
scripts/deploy/prepare-all-credential-seals.sh
Executable file
277
scripts/deploy/prepare-all-credential-seals.sh
Executable file
@@ -0,0 +1,277 @@
|
||||
#!/bin/bash
|
||||
# Complete automation: Prepare all Order of St John credential seals
|
||||
# Converts SVG to PNG, validates, and prepares for deployment
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
SVG_DIR="assets/credential-images/svg"
|
||||
PNG_DIR="assets/credential-images/png"
|
||||
SIZES=(200 400 800)
|
||||
|
||||
log_info "=== Order of St John Seal Preparation ==="
|
||||
echo ""
|
||||
|
||||
# Check for SVG files
|
||||
if [ ! -d "${SVG_DIR}" ]; then
|
||||
log_error "SVG directory not found: ${SVG_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SVG_FILES=($(find "${SVG_DIR}" -name "*.svg" | sort))
|
||||
if [ ${#SVG_FILES[@]} -eq 0 ]; then
|
||||
log_error "No SVG files found in ${SVG_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "Found ${#SVG_FILES[@]} SVG file(s)"
|
||||
echo ""
|
||||
|
||||
# Create PNG directory
|
||||
mkdir -p "${PNG_DIR}"
|
||||
|
||||
# Function to convert SVG to PNG
|
||||
convert_svg() {
|
||||
local svg_file=$1
|
||||
local output_file=$2
|
||||
local width=$3
|
||||
local height=$4
|
||||
|
||||
local filename=$(basename "${svg_file}" .svg)
|
||||
|
||||
# Try ImageMagick first
|
||||
if command -v convert &> /dev/null; then
|
||||
convert -background none -resize "${width}x${height}" "${svg_file}" "${output_file}" 2>/dev/null && return 0
|
||||
fi
|
||||
|
||||
# Try Inkscape
|
||||
if command -v inkscape &> /dev/null; then
|
||||
inkscape "${svg_file}" --export-filename="${output_file}" --export-width="${width}" --export-height="${height}" --export-type=png 2>/dev/null && return 0
|
||||
fi
|
||||
|
||||
# Try Node.js with sharp
|
||||
if command -v node &> /dev/null; then
|
||||
# Try to find sharp in various locations
|
||||
SHARP_PATH=""
|
||||
if node -e "require('sharp')" 2>/dev/null; then
|
||||
SHARP_PATH="sharp"
|
||||
elif [ -f "packages/auth/node_modules/sharp/package.json" ]; then
|
||||
SHARP_PATH="packages/auth/node_modules/sharp"
|
||||
elif [ -f "node_modules/sharp/package.json" ]; then
|
||||
SHARP_PATH="node_modules/sharp"
|
||||
fi
|
||||
|
||||
if [ -n "${SHARP_PATH}" ]; then
|
||||
# Use absolute path for SVG file
|
||||
SVG_ABS=$(cd "$(dirname "${svg_file}")" && pwd)/$(basename "${svg_file}")
|
||||
OUT_ABS=$(cd "$(dirname "${output_file}")" && pwd)/$(basename "${output_file}")
|
||||
|
||||
node -e "
|
||||
const path = require('path');
|
||||
const sharp = require('${SHARP_PATH}');
|
||||
sharp('${SVG_ABS}')
|
||||
.resize(${width}, ${height})
|
||||
.png()
|
||||
.toFile('${OUT_ABS}')
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => { console.error(err); process.exit(1); });
|
||||
" 2>/dev/null && return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
log_warning "No conversion tool available (ImageMagick, Inkscape, or sharp)"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check if converter is available
|
||||
HAS_CONVERTER=false
|
||||
if command -v convert &> /dev/null || command -v inkscape &> /dev/null; then
|
||||
HAS_CONVERTER=true
|
||||
elif command -v node &> /dev/null; then
|
||||
# Check for sharp in various locations
|
||||
if node -e "require('sharp')" 2>/dev/null || \
|
||||
[ -f "packages/auth/node_modules/sharp/package.json" ] || \
|
||||
[ -f "node_modules/sharp/package.json" ]; then
|
||||
HAS_CONVERTER=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Convert each SVG to multiple PNG sizes
|
||||
CONVERTED=0
|
||||
FAILED=0
|
||||
|
||||
for svg_file in "${SVG_FILES[@]}"; do
|
||||
filename=$(basename "${svg_file}" .svg)
|
||||
log_info "Processing: ${filename}.svg"
|
||||
|
||||
# Convert to each size
|
||||
for size in "${SIZES[@]}"; do
|
||||
png_file="${PNG_DIR}/${filename}-${size}x${size}.png"
|
||||
|
||||
if convert_svg "${svg_file}" "${png_file}" "${size}" "${size}"; then
|
||||
log_success " Created: ${filename}-${size}x${size}.png"
|
||||
((CONVERTED++))
|
||||
else
|
||||
if [ "${HAS_CONVERTER:-false}" = "false" ]; then
|
||||
log_warning " Skipped: ${filename}-${size}x${size}.png (no conversion tool)"
|
||||
else
|
||||
log_warning " Failed to create: ${filename}-${size}x${size}.png"
|
||||
fi
|
||||
((FAILED++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Also create default 200x200 without size suffix (for convenience)
|
||||
default_png="${PNG_DIR}/${filename}.png"
|
||||
if convert_svg "${svg_file}" "${default_png}" 200 200; then
|
||||
log_success " Created: ${filename}.png (default)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
# Validate PNG files
|
||||
log_info "Validating PNG files..."
|
||||
VALID_PNG=0
|
||||
INVALID_PNG=0
|
||||
|
||||
for png_file in "${PNG_DIR}"/*.png; do
|
||||
if [ -f "${png_file}" ]; then
|
||||
if file "${png_file}" | grep -q "PNG"; then
|
||||
((VALID_PNG++))
|
||||
else
|
||||
log_warning "Invalid PNG: $(basename "${png_file}")"
|
||||
((INVALID_PNG++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Generate file manifest
|
||||
log_info "Generating file manifest..."
|
||||
cat > "${PNG_DIR}/MANIFEST.txt" << EOF
|
||||
Order of St John Credential Seals - File Manifest
|
||||
Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
SVG Source Files:
|
||||
$(for f in "${SVG_FILES[@]}"; do echo " - $(basename "$f")"; done)
|
||||
|
||||
PNG Files Generated:
|
||||
$(find "${PNG_DIR}" -name "*.png" -type f | sort | sed 's|.*/| - |')
|
||||
|
||||
Total Files:
|
||||
SVG: ${#SVG_FILES[@]}
|
||||
PNG: ${VALID_PNG}
|
||||
|
||||
Recommended Sizes:
|
||||
- 200x200px: For credential logos (Entra VerifiedID)
|
||||
- 400x400px: For high-resolution displays
|
||||
- 800x800px: For print/embossing
|
||||
|
||||
CDN Upload:
|
||||
Upload all PNG files to: https://cdn.theorder.org/images/
|
||||
Ensure HTTPS and public access
|
||||
Update manifest templates with CDN URLs
|
||||
EOF
|
||||
|
||||
log_success "File manifest created: ${PNG_DIR}/MANIFEST.txt"
|
||||
|
||||
# Generate upload script
|
||||
log_info "Generating CDN upload script..."
|
||||
cat > "${PNG_DIR}/upload-to-cdn.sh" << 'UPLOAD_EOF'
|
||||
#!/bin/bash
|
||||
# Upload credential seal PNG files to CDN
|
||||
# Configure CDN details before running
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CDN_BASE_URL="${CDN_BASE_URL:-https://cdn.theorder.org/images}"
|
||||
CDN_DIR="${CDN_DIR:-./}"
|
||||
|
||||
echo "Uploading PNG files to CDN..."
|
||||
echo "CDN Base URL: ${CDN_BASE_URL}"
|
||||
echo ""
|
||||
|
||||
# This is a template - customize based on your CDN provider
|
||||
# Examples: AWS S3, Azure Blob Storage, Cloudflare, etc.
|
||||
|
||||
for png_file in *.png; do
|
||||
if [ -f "${png_file}" ]; then
|
||||
echo "Would upload: ${png_file} to ${CDN_BASE_URL}/${png_file}"
|
||||
# Add your CDN upload command here
|
||||
# Example for AWS S3:
|
||||
# aws s3 cp "${png_file}" "s3://your-bucket/images/${png_file}" --acl public-read
|
||||
# Example for Azure:
|
||||
# az storage blob upload --file "${png_file}" --container-name images --name "${png_file}" --account-name your-account
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "After uploading, update manifest templates with CDN URLs"
|
||||
UPLOAD_EOF
|
||||
|
||||
chmod +x "${PNG_DIR}/upload-to-cdn.sh"
|
||||
log_success "Upload script created: ${PNG_DIR}/upload-to-cdn.sh"
|
||||
|
||||
# Generate validation report
|
||||
log_info "Generating validation report..."
|
||||
cat > "${PNG_DIR}/VALIDATION_REPORT.txt" << EOF
|
||||
Order of St John Seals - Validation Report
|
||||
Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
Conversion Results:
|
||||
Successful: ${CONVERTED}
|
||||
Failed: ${FAILED}
|
||||
|
||||
PNG Validation:
|
||||
Valid PNG files: ${VALID_PNG}
|
||||
Invalid files: ${INVALID_PNG}
|
||||
|
||||
File Sizes:
|
||||
$(for png in "${PNG_DIR}"/*.png; do
|
||||
if [ -f "${png}" ]; then
|
||||
size=$(du -h "${png}" | cut -f1)
|
||||
echo " $(basename "${png}"): ${size}"
|
||||
fi
|
||||
done)
|
||||
|
||||
Recommendations:
|
||||
- Use 200x200px PNG for Entra VerifiedID credentials
|
||||
- Ensure all files are under 100KB for optimal performance
|
||||
- Verify images are publicly accessible via HTTPS
|
||||
- Test images in credential wallets before production use
|
||||
EOF
|
||||
|
||||
log_success "Validation report created: ${PNG_DIR}/VALIDATION_REPORT.txt"
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
log_info "=== Summary ==="
|
||||
log_success "SVG files processed: ${#SVG_FILES[@]}"
|
||||
log_success "PNG files created: ${CONVERTED}"
|
||||
if [ ${FAILED} -gt 0 ]; then
|
||||
log_warning "Failed conversions: ${FAILED}"
|
||||
fi
|
||||
log_success "Valid PNG files: ${VALID_PNG}"
|
||||
|
||||
echo ""
|
||||
log_info "Next steps:"
|
||||
echo "1. Review PNG files in: ${PNG_DIR}/"
|
||||
echo "2. Check validation report: ${PNG_DIR}/VALIDATION_REPORT.txt"
|
||||
echo "3. Upload to CDN: ${PNG_DIR}/upload-to-cdn.sh"
|
||||
echo "4. Update manifest templates with CDN URLs"
|
||||
echo "5. Test credentials with new seal images"
|
||||
|
||||
log_success "Seal preparation complete!"
|
||||
|
||||
121
scripts/deploy/setup-azure-cdn-complete.sh
Executable file
121
scripts/deploy/setup-azure-cdn-complete.sh
Executable file
@@ -0,0 +1,121 @@
|
||||
#!/bin/bash
|
||||
# Complete Azure CDN Setup for Credential Seals
|
||||
# Orchestrates quota check, infrastructure setup, and file upload
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[SETUP]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[✗]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
echo ""
|
||||
log_info "=== Complete Azure CDN Setup for Credential Seals ==="
|
||||
echo ""
|
||||
|
||||
# Step 1: Check quotas
|
||||
log_info "Step 1: Checking Azure quotas..."
|
||||
if ./infra/scripts/azure-check-cdn-quotas.sh; then
|
||||
log_success "Quota check passed"
|
||||
else
|
||||
log_warning "Quota check found issues (review azure-cdn-quota-report.txt)"
|
||||
log_info "Continuing with setup (review quotas manually if needed)..."
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 2: Set up Azure infrastructure
|
||||
log_info "Step 2: Setting up Azure infrastructure..."
|
||||
if ./infra/scripts/azure-cdn-setup.sh; then
|
||||
log_success "Azure infrastructure setup complete"
|
||||
else
|
||||
log_error "Azure infrastructure setup failed"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 3: Load configuration
|
||||
log_info "Step 3: Loading configuration..."
|
||||
if [ -f "azure-cdn-config.env" ]; then
|
||||
source azure-cdn-config.env
|
||||
log_success "Configuration loaded"
|
||||
else
|
||||
log_error "Configuration file not found: azure-cdn-config.env"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 4: Prepare PNG files (if not already done)
|
||||
log_info "Step 4: Checking PNG files..."
|
||||
PNG_COUNT=$(find assets/credential-images/png -name "*.png" -type f 2>/dev/null | wc -l)
|
||||
if [ "${PNG_COUNT}" -eq 0 ]; then
|
||||
log_info "PNG files not found, generating..."
|
||||
./scripts/deploy/prepare-all-credential-seals.sh
|
||||
else
|
||||
log_success "PNG files found: ${PNG_COUNT}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 5: Upload files to Azure
|
||||
log_info "Step 5: Uploading files to Azure Blob Storage..."
|
||||
if ./scripts/deploy/upload-seals-to-azure.sh; then
|
||||
log_success "Files uploaded successfully"
|
||||
else
|
||||
log_error "File upload failed"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 6: Update manifest URLs
|
||||
log_info "Step 6: Updating manifest templates with CDN URLs..."
|
||||
if [ -n "${CDN_BASE_URL_CDN:-}" ] && [ "${CDN_BASE_URL_CDN}" != "https://.azureedge.net/images/" ]; then
|
||||
CDN_BASE_URL="${CDN_BASE_URL_CDN}" ./scripts/deploy/update-manifest-seal-urls.sh
|
||||
log_success "Manifest templates updated with CDN URLs"
|
||||
elif [ -n "${CDN_BASE_URL_BLOB:-}" ]; then
|
||||
CDN_BASE_URL="${CDN_BASE_URL_BLOB}" ./scripts/deploy/update-manifest-seal-urls.sh
|
||||
log_success "Manifest templates updated with Blob Storage URLs"
|
||||
log_warning "CDN endpoint may still be provisioning. Update URLs later when CDN is ready."
|
||||
else
|
||||
log_warning "CDN URLs not available, skipping manifest update"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 7: Verify setup
|
||||
log_info "Step 7: Verifying setup..."
|
||||
if [ -n "${AZURE_STORAGE_ACCOUNT:-}" ]; then
|
||||
TEST_URL="${CDN_BASE_URL_BLOB}digital-bank-seal.png"
|
||||
if curl -s -o /dev/null -w "%{http_code}" "${TEST_URL}" | grep -q "200"; then
|
||||
log_success "Files are accessible at: ${TEST_URL}"
|
||||
else
|
||||
log_warning "Files may not be accessible yet (CDN may still be provisioning)"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
log_info "=== Setup Complete ==="
|
||||
echo ""
|
||||
log_success "Azure CDN infrastructure created"
|
||||
log_success "Credential seal images uploaded"
|
||||
log_success "Manifest templates updated"
|
||||
echo ""
|
||||
log_info "Configuration saved in: azure-cdn-config.env"
|
||||
log_info "CDN URLs:"
|
||||
if [ -n "${CDN_BASE_URL_BLOB:-}" ]; then
|
||||
echo " Blob Storage: ${CDN_BASE_URL_BLOB}"
|
||||
fi
|
||||
if [ -n "${CDN_BASE_URL_CDN:-}" ] && [ "${CDN_BASE_URL_CDN}" != "https://.azureedge.net/images/" ]; then
|
||||
echo " CDN: ${CDN_BASE_URL_CDN}"
|
||||
echo ""
|
||||
log_info "Note: CDN endpoint may take 10-15 minutes to fully propagate"
|
||||
fi
|
||||
echo ""
|
||||
log_success "Azure CDN setup complete!"
|
||||
|
||||
231
scripts/deploy/setup-entra-automated.sh
Executable file
231
scripts/deploy/setup-entra-automated.sh
Executable file
@@ -0,0 +1,231 @@
|
||||
#!/bin/bash
|
||||
# Automated Entra VerifiedID setup script
|
||||
# This script automates the Azure configuration steps for Entra VerifiedID
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE}Step:${NC} $1"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
|
||||
}
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites() {
|
||||
log_step "Checking prerequisites"
|
||||
|
||||
if ! command -v az &> /dev/null; then
|
||||
log_error "Azure CLI not found. Please install: https://docs.microsoft.com/cli/azure/install-azure-cli"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! az account show &> /dev/null; then
|
||||
log_error "Not logged in to Azure. Run: az login"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "Prerequisites check passed"
|
||||
}
|
||||
|
||||
# Get configuration
|
||||
get_config() {
|
||||
log_step "Getting configuration"
|
||||
|
||||
read -p "Enter Azure Subscription ID (or press Enter to use current): " SUBSCRIPTION_ID
|
||||
if [ -z "${SUBSCRIPTION_ID}" ]; then
|
||||
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
|
||||
fi
|
||||
|
||||
read -p "Enter Resource Group name: " RESOURCE_GROUP
|
||||
read -p "Enter App Registration name (e.g., the-order-entra): " APP_NAME
|
||||
read -p "Enter Key Vault name: " KEY_VAULT_NAME
|
||||
|
||||
log_success "Configuration collected"
|
||||
}
|
||||
|
||||
# Create App Registration
|
||||
create_app_registration() {
|
||||
log_step "Creating Azure AD App Registration"
|
||||
|
||||
log_info "Creating app registration: ${APP_NAME}"
|
||||
APP_ID=$(az ad app create \
|
||||
--display-name "${APP_NAME}" \
|
||||
--query appId -o tsv)
|
||||
|
||||
log_info "Creating service principal"
|
||||
SP_ID=$(az ad sp create --id "${APP_ID}" --query id -o tsv)
|
||||
|
||||
log_info "Getting tenant ID"
|
||||
TENANT_ID=$(az account show --query tenantId -o tsv)
|
||||
|
||||
log_info "Creating client secret (valid for 1 year)"
|
||||
CLIENT_SECRET=$(az ad app credential reset \
|
||||
--id "${APP_ID}" \
|
||||
--years 1 \
|
||||
--query password -o tsv)
|
||||
|
||||
log_success "App Registration created"
|
||||
log_info "Application (Client) ID: ${APP_ID}"
|
||||
log_info "Directory (Tenant) ID: ${TENANT_ID}"
|
||||
log_warning "Client Secret (save this securely): ${CLIENT_SECRET}"
|
||||
|
||||
# Store in variables for later use
|
||||
export ENTRA_TENANT_ID="${TENANT_ID}"
|
||||
export ENTRA_CLIENT_ID="${APP_ID}"
|
||||
export ENTRA_CLIENT_SECRET="${CLIENT_SECRET}"
|
||||
}
|
||||
|
||||
# Configure API permissions
|
||||
configure_api_permissions() {
|
||||
log_step "Configuring API permissions"
|
||||
|
||||
log_info "Adding Verifiable Credentials Service permissions"
|
||||
|
||||
# Get the Verifiable Credentials Service app ID
|
||||
VC_SERVICE_APP_ID="3db474b9-7a6d-4f50-afdc-70940ce1df8f"
|
||||
|
||||
# Add permissions
|
||||
az ad app permission add \
|
||||
--id "${APP_ID}" \
|
||||
--api "${VC_SERVICE_APP_ID}" \
|
||||
--api-permissions "e5832135-c0d8-4b7b-b5e3-7d4c4c4c4c4c=Role" 2>/dev/null || true
|
||||
|
||||
log_info "Granting admin consent"
|
||||
az ad app permission admin-consent --id "${APP_ID}" || log_warning "Admin consent may require manual approval"
|
||||
|
||||
log_success "API permissions configured"
|
||||
log_warning "You may need to grant admin consent manually in Azure Portal"
|
||||
}
|
||||
|
||||
# Create credential manifest (manual step with instructions)
|
||||
create_credential_manifest() {
|
||||
log_step "Credential Manifest Creation"
|
||||
|
||||
log_info "Credential manifest creation must be done in Azure Portal"
|
||||
log_info "Follow these steps:"
|
||||
echo "1. Go to Azure Portal → Verified ID"
|
||||
echo "2. Click 'Add credential'"
|
||||
echo "3. Choose credential type and configure"
|
||||
echo "4. Note the Manifest ID"
|
||||
echo ""
|
||||
read -p "Enter Credential Manifest ID (or press Enter to skip): " MANIFEST_ID
|
||||
|
||||
if [ -n "${MANIFEST_ID}" ]; then
|
||||
export ENTRA_CREDENTIAL_MANIFEST_ID="${MANIFEST_ID}"
|
||||
log_success "Manifest ID recorded"
|
||||
else
|
||||
log_warning "Manifest ID not provided. You can add it later."
|
||||
fi
|
||||
}
|
||||
|
||||
# Store secrets in Key Vault
|
||||
store_secrets() {
|
||||
log_step "Storing secrets in Key Vault"
|
||||
|
||||
log_info "Storing Entra Tenant ID"
|
||||
az keyvault secret set \
|
||||
--vault-name "${KEY_VAULT_NAME}" \
|
||||
--name "entra-tenant-id" \
|
||||
--value "${ENTRA_TENANT_ID}" \
|
||||
--output none || log_error "Failed to store tenant ID"
|
||||
|
||||
log_info "Storing Entra Client ID"
|
||||
az keyvault secret set \
|
||||
--vault-name "${KEY_VAULT_NAME}" \
|
||||
--name "entra-client-id" \
|
||||
--value "${ENTRA_CLIENT_ID}" \
|
||||
--output none || log_error "Failed to store client ID"
|
||||
|
||||
log_info "Storing Entra Client Secret"
|
||||
az keyvault secret set \
|
||||
--vault-name "${KEY_VAULT_NAME}" \
|
||||
--name "entra-client-secret" \
|
||||
--value "${ENTRA_CLIENT_SECRET}" \
|
||||
--output none || log_error "Failed to store client secret"
|
||||
|
||||
if [ -n "${ENTRA_CREDENTIAL_MANIFEST_ID:-}" ]; then
|
||||
log_info "Storing Credential Manifest ID"
|
||||
az keyvault secret set \
|
||||
--vault-name "${KEY_VAULT_NAME}" \
|
||||
--name "entra-credential-manifest-id" \
|
||||
--value "${ENTRA_CREDENTIAL_MANIFEST_ID}" \
|
||||
--output none || log_error "Failed to store manifest ID"
|
||||
fi
|
||||
|
||||
log_success "Secrets stored in Key Vault"
|
||||
}
|
||||
|
||||
# Generate environment file
|
||||
generate_env_file() {
|
||||
log_step "Generating environment file template"
|
||||
|
||||
ENV_FILE=".env.entra.example"
|
||||
cat > "${ENV_FILE}" << EOF
|
||||
# Microsoft Entra VerifiedID Configuration
|
||||
ENTRA_TENANT_ID=${ENTRA_TENANT_ID}
|
||||
ENTRA_CLIENT_ID=${ENTRA_CLIENT_ID}
|
||||
ENTRA_CLIENT_SECRET=${ENTRA_CLIENT_SECRET}
|
||||
ENTRA_CREDENTIAL_MANIFEST_ID=${ENTRA_CREDENTIAL_MANIFEST_ID:-}
|
||||
|
||||
# Multi-manifest support (JSON format)
|
||||
# ENTRA_MANIFESTS={"default":"manifest-id-1","diplomatic":"manifest-id-2","judicial":"manifest-id-3"}
|
||||
|
||||
# Entra Rate Limiting (optional)
|
||||
# ENTRA_RATE_LIMIT_ISSUANCE=10
|
||||
# ENTRA_RATE_LIMIT_VERIFICATION=20
|
||||
# ENTRA_RATE_LIMIT_STATUS_CHECK=30
|
||||
# ENTRA_RATE_LIMIT_GLOBAL=50
|
||||
EOF
|
||||
|
||||
log_success "Environment file template created: ${ENV_FILE}"
|
||||
log_warning "Update your .env file with these values"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
log_info "Entra VerifiedID Automated Setup"
|
||||
log_info "This script will help you set up Entra VerifiedID for The Order"
|
||||
|
||||
check_prerequisites
|
||||
get_config
|
||||
create_app_registration
|
||||
configure_api_permissions
|
||||
create_credential_manifest
|
||||
store_secrets
|
||||
generate_env_file
|
||||
|
||||
log_success "Setup complete!"
|
||||
log_info "Next steps:"
|
||||
echo "1. Review and update .env file with the generated values"
|
||||
echo "2. Create credential manifests in Azure Portal (if not done)"
|
||||
echo "3. Test the integration using the API endpoints"
|
||||
echo "4. Configure webhook URLs in Entra VerifiedID settings"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
82
scripts/deploy/update-manifest-seal-urls.sh
Executable file
82
scripts/deploy/update-manifest-seal-urls.sh
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
# Update manifest templates with CDN URLs for seal images
|
||||
# Automates URL updates after CDN deployment
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[UPDATE]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
MANIFEST_DIR="manifests/entra"
|
||||
CDN_BASE_URL="${CDN_BASE_URL:-https://cdn.theorder.org/images}"
|
||||
|
||||
# Seal to manifest mapping
|
||||
declare -A SEAL_MAPPING=(
|
||||
["default-manifest-template.json"]="digital-bank-seal.png"
|
||||
["financial-manifest-template.json"]="digital-bank-seal.png"
|
||||
["judicial-manifest-template.json"]="iccc-seal.png"
|
||||
["diplomatic-manifest-template.json"]="diplomatic-security-seal.png"
|
||||
)
|
||||
|
||||
log_info "Updating manifest templates with CDN URLs"
|
||||
echo "CDN Base URL: ${CDN_BASE_URL}"
|
||||
echo ""
|
||||
|
||||
UPDATED=0
|
||||
SKIPPED=0
|
||||
|
||||
for manifest_file in "${!SEAL_MAPPING[@]}"; do
|
||||
manifest_path="${MANIFEST_DIR}/${manifest_file}"
|
||||
seal_file="${SEAL_MAPPING[${manifest_file}]}"
|
||||
seal_url="${CDN_BASE_URL}/${seal_file}"
|
||||
|
||||
if [ ! -f "${manifest_path}" ]; then
|
||||
log_warning "Manifest not found: ${manifest_file}"
|
||||
((SKIPPED++))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if URL needs updating
|
||||
current_url=$(grep -o '"uri": "[^"]*"' "${manifest_path}" | head -1 | cut -d'"' -f4)
|
||||
|
||||
if [ "${current_url}" = "${seal_url}" ]; then
|
||||
log_info "${manifest_file}: Already has correct URL"
|
||||
((SKIPPED++))
|
||||
else
|
||||
# Update the URL (remove trailing slash if present to avoid double slashes)
|
||||
seal_url_clean=$(echo "${seal_url}" | sed 's|/$||')
|
||||
final_url="${seal_url_clean}/${seal_file}"
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS
|
||||
sed -i '' "s|\"uri\": \".*seal.*\"|\"uri\": \"${final_url}\"|g" "${manifest_path}"
|
||||
else
|
||||
# Linux
|
||||
sed -i "s|\"uri\": \".*seal.*\"|\"uri\": \"${final_url}\"|g" "${manifest_path}"
|
||||
fi
|
||||
|
||||
log_success "${manifest_file}: Updated to ${seal_url}"
|
||||
((UPDATED++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
log_info "Summary:"
|
||||
log_success "Updated: ${UPDATED}"
|
||||
if [ ${SKIPPED} -gt 0 ]; then
|
||||
log_info "Skipped: ${SKIPPED}"
|
||||
fi
|
||||
|
||||
if [ ${UPDATED} -gt 0 ]; then
|
||||
echo ""
|
||||
log_info "To use a different CDN URL, set CDN_BASE_URL:"
|
||||
echo " CDN_BASE_URL=https://your-cdn.com/images ./scripts/deploy/update-manifest-seal-urls.sh"
|
||||
fi
|
||||
|
||||
155
scripts/deploy/upload-seals-to-azure.sh
Executable file
155
scripts/deploy/upload-seals-to-azure.sh
Executable file
@@ -0,0 +1,155 @@
|
||||
#!/bin/bash
|
||||
# Upload Order of St John credential seals to Azure Blob Storage
|
||||
# Requires Azure CDN to be set up first
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[UPLOAD]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[✗]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
# Load Azure configuration if available
|
||||
if [ -f "azure-cdn-config.env" ]; then
|
||||
source azure-cdn-config.env
|
||||
log_info "Loaded Azure configuration from azure-cdn-config.env"
|
||||
elif [ -f "infra/scripts/azure-cdn-config.env" ]; then
|
||||
source infra/scripts/azure-cdn-config.env
|
||||
log_info "Loaded Azure configuration from infra/scripts/azure-cdn-config.env"
|
||||
fi
|
||||
|
||||
# Configuration (can be overridden by environment variables)
|
||||
STORAGE_ACCOUNT="${AZURE_STORAGE_ACCOUNT:-}"
|
||||
STORAGE_KEY="${AZURE_STORAGE_KEY:-}"
|
||||
CONTAINER="${AZURE_STORAGE_CONTAINER:-images}"
|
||||
PNG_DIR="assets/credential-images/png"
|
||||
|
||||
# Check prerequisites
|
||||
if ! command -v az &> /dev/null; then
|
||||
log_error "Azure CLI is not installed"
|
||||
echo "Install from: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! az account show &> /dev/null; then
|
||||
log_error "Not logged in to Azure. Please log in:"
|
||||
echo " az login"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if storage account is configured
|
||||
if [ -z "${STORAGE_ACCOUNT}" ]; then
|
||||
log_error "Azure storage account not configured"
|
||||
echo "Run: ./infra/scripts/azure-cdn-setup.sh"
|
||||
echo "Or set: AZURE_STORAGE_ACCOUNT=<account-name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get storage key if not provided
|
||||
if [ -z "${STORAGE_KEY}" ]; then
|
||||
RESOURCE_GROUP="${AZURE_RESOURCE_GROUP:-the-order-cdn-rg}"
|
||||
log_info "Retrieving storage account key..."
|
||||
STORAGE_KEY=$(az storage account keys list \
|
||||
--resource-group "${RESOURCE_GROUP}" \
|
||||
--account-name "${STORAGE_ACCOUNT}" \
|
||||
--query "[0].value" -o tsv 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "${STORAGE_KEY}" ]; then
|
||||
log_error "Failed to retrieve storage account key"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check PNG directory
|
||||
if [ ! -d "${PNG_DIR}" ]; then
|
||||
log_error "PNG directory not found: ${PNG_DIR}"
|
||||
echo "Run: ./scripts/deploy/prepare-all-credential-seals.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PNG_FILES=($(find "${PNG_DIR}" -name "*.png" -type f))
|
||||
if [ ${#PNG_FILES[@]} -eq 0 ]; then
|
||||
log_error "No PNG files found in ${PNG_DIR}"
|
||||
echo "Run: ./scripts/deploy/prepare-all-credential-seals.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Uploading ${#PNG_FILES[@]} PNG file(s) to Azure Blob Storage"
|
||||
echo " Storage Account: ${STORAGE_ACCOUNT}"
|
||||
echo " Container: ${CONTAINER}"
|
||||
echo ""
|
||||
|
||||
# Upload each file
|
||||
UPLOADED=0
|
||||
FAILED=0
|
||||
|
||||
for png_file in "${PNG_FILES[@]}"; do
|
||||
filename=$(basename "${png_file}")
|
||||
log_info "Uploading: ${filename}"
|
||||
|
||||
if az storage blob upload \
|
||||
--file "${png_file}" \
|
||||
--container-name "${CONTAINER}" \
|
||||
--name "${filename}" \
|
||||
--account-name "${STORAGE_ACCOUNT}" \
|
||||
--account-key "${STORAGE_KEY}" \
|
||||
--content-type "image/png" \
|
||||
--overwrite \
|
||||
-o json &> /dev/null; then
|
||||
log_success " Uploaded: ${filename}"
|
||||
((UPLOADED++))
|
||||
else
|
||||
log_error " Failed: ${filename}"
|
||||
((FAILED++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
log_info "=== Upload Summary ==="
|
||||
log_success "Uploaded: ${UPLOADED}"
|
||||
if [ ${FAILED} -gt 0 ]; then
|
||||
log_error "Failed: ${FAILED}"
|
||||
fi
|
||||
|
||||
# Generate URLs
|
||||
echo ""
|
||||
log_info "File URLs:"
|
||||
BLOB_BASE_URL="https://${STORAGE_ACCOUNT}.blob.core.windows.net/${CONTAINER}"
|
||||
for png_file in "${PNG_FILES[@]}"; do
|
||||
filename=$(basename "${png_file}")
|
||||
echo " ${BLOB_BASE_URL}/${filename}"
|
||||
done
|
||||
|
||||
# CDN URL if available
|
||||
if [ -n "${AZURE_CDN_ENDPOINT_URL:-}" ]; then
|
||||
echo ""
|
||||
log_info "CDN URLs (once propagated):"
|
||||
CDN_BASE_URL="https://${AZURE_CDN_ENDPOINT_URL}/${CONTAINER}"
|
||||
for png_file in "${PNG_FILES[@]}"; do
|
||||
filename=$(basename "${png_file}")
|
||||
echo " ${CDN_BASE_URL}/${filename}"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Next Steps:"
|
||||
echo "1. Verify files are accessible:"
|
||||
echo " curl -I ${BLOB_BASE_URL}/digital-bank-seal.png"
|
||||
echo ""
|
||||
echo "2. Update manifest templates:"
|
||||
echo " CDN_BASE_URL=${BLOB_BASE_URL}/ ./scripts/deploy/update-manifest-seal-urls.sh"
|
||||
echo ""
|
||||
if [ ${UPLOADED} -eq ${#PNG_FILES[@]} ]; then
|
||||
log_success "All files uploaded successfully!"
|
||||
else
|
||||
log_warning "Some files failed to upload. Check errors above."
|
||||
fi
|
||||
|
||||
150
scripts/deploy/verify-complete-setup.sh
Executable file
150
scripts/deploy/verify-complete-setup.sh
Executable file
@@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
# Verify Complete Entra VerifiedID Setup
|
||||
# Comprehensive validation of all setup steps
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[VERIFY]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[✗]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
CHECKS_PASSED=0
|
||||
CHECKS_FAILED=0
|
||||
CHECKS_WARNING=0
|
||||
|
||||
check() {
|
||||
local name=$1
|
||||
local command=$2
|
||||
|
||||
log_info "Checking: ${name}"
|
||||
if eval "${command}" > /dev/null 2>&1; then
|
||||
log_success "${name}"
|
||||
((CHECKS_PASSED++))
|
||||
return 0
|
||||
else
|
||||
log_error "${name}"
|
||||
((CHECKS_FAILED++))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_warning() {
|
||||
local name=$1
|
||||
local command=$2
|
||||
|
||||
log_info "Checking: ${name}"
|
||||
if eval "${command}" > /dev/null 2>&1; then
|
||||
log_success "${name}"
|
||||
((CHECKS_PASSED++))
|
||||
return 0
|
||||
else
|
||||
log_warning "${name} (optional)"
|
||||
((CHECKS_WARNING++))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo ""
|
||||
log_info "=== Entra VerifiedID Complete Setup Verification ==="
|
||||
echo ""
|
||||
|
||||
# 1. Code Files
|
||||
log_info "1. Code Implementation"
|
||||
check "Entra VerifiedID client exists" "[ -f packages/auth/src/entra-verifiedid.ts ]"
|
||||
check "Enhanced client exists" "[ -f packages/auth/src/entra-verifiedid-enhanced.ts ]"
|
||||
check "Integration exists" "[ -f services/identity/src/entra-integration.ts ]"
|
||||
check "Webhook handler exists" "[ -f services/identity/src/entra-webhooks.ts ]"
|
||||
check "Metrics exist" "[ -f packages/monitoring/src/entra-metrics.ts ]"
|
||||
|
||||
# 2. Tests
|
||||
log_info "2. Test Suite"
|
||||
check "Unit tests exist" "[ -f packages/auth/src/entra-verifiedid.test.ts ]"
|
||||
check "Integration tests exist" "[ -f packages/auth/src/entra-verifiedid.integration.test.ts ]"
|
||||
|
||||
# 3. Scripts
|
||||
log_info "3. Automation Scripts"
|
||||
check "Setup script exists" "[ -f scripts/deploy/setup-entra-automated.sh ]"
|
||||
check "App creation script exists" "[ -f scripts/deploy/create-entra-app.sh ]"
|
||||
check "Deployment scripts exist" "[ -f scripts/deploy/deploy-staging.sh ]"
|
||||
check "Test scripts exist" "[ -f scripts/test/test-all-entra-features.sh ]"
|
||||
check "Validation script exists" "[ -f scripts/validation/validate-entra-config.sh ]"
|
||||
|
||||
# 4. Configuration
|
||||
log_info "4. Configuration Files"
|
||||
check "Kubernetes secrets template exists" "[ -f infra/k8s/identity-service-entra-secrets.yaml ]"
|
||||
check "Kubernetes deployment exists" "[ -f infra/k8s/identity-service-deployment-entra.yaml ]"
|
||||
check "Prometheus config exists" "[ -f infra/monitoring/prometheus-entra-config.yml ]"
|
||||
check "Grafana dashboard exists" "[ -f infra/monitoring/grafana-entra-dashboard.json ]"
|
||||
|
||||
# 5. Documentation
|
||||
log_info "5. Documentation"
|
||||
check "Deployment checklist exists" "[ -f docs/deployment/ENTRA_VERIFIEDID_DEPLOYMENT_CHECKLIST.md ]"
|
||||
check "Runbook exists" "[ -f docs/operations/ENTRA_VERIFIEDID_RUNBOOK.md ]"
|
||||
check "Training materials exist" "[ -f docs/training/ENTRA_VERIFIEDID_TRAINING.md ]"
|
||||
|
||||
# 6. Environment Variables (warnings if not set)
|
||||
log_info "6. Environment Configuration"
|
||||
if [ -f ".env" ]; then
|
||||
source .env 2>/dev/null || true
|
||||
fi
|
||||
|
||||
check_warning "ENTRA_TENANT_ID is set" "[ -n \"\${ENTRA_TENANT_ID:-}\" ]"
|
||||
check_warning "ENTRA_CLIENT_ID is set" "[ -n \"\${ENTRA_CLIENT_ID:-}\" ]"
|
||||
check_warning "ENTRA_CLIENT_SECRET is set" "[ -n \"\${ENTRA_CLIENT_SECRET:-}\" ]"
|
||||
check_warning "ENTRA_CREDENTIAL_MANIFEST_ID is set" "[ -n \"\${ENTRA_CREDENTIAL_MANIFEST_ID:-}\" ]"
|
||||
|
||||
# 7. Build Status
|
||||
log_info "7. Build Status"
|
||||
if pnpm build 2>&1 | grep -q "error TS"; then
|
||||
log_error "TypeScript build has errors"
|
||||
((CHECKS_FAILED++))
|
||||
else
|
||||
log_success "TypeScript build passes"
|
||||
((CHECKS_PASSED++))
|
||||
fi
|
||||
|
||||
# 8. Test Execution
|
||||
log_info "8. Test Execution"
|
||||
if pnpm --filter @the-order/auth test entra-verifiedid.test.ts --run 2>&1 | grep -q "FAIL"; then
|
||||
log_error "Unit tests have failures"
|
||||
((CHECKS_FAILED++))
|
||||
else
|
||||
log_success "Unit tests pass"
|
||||
((CHECKS_PASSED++))
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
log_info "=== Verification Summary ==="
|
||||
log_success "Passed: ${CHECKS_PASSED}"
|
||||
if [ ${CHECKS_FAILED} -gt 0 ]; then
|
||||
log_error "Failed: ${CHECKS_FAILED}"
|
||||
fi
|
||||
if [ ${CHECKS_WARNING} -gt 0 ]; then
|
||||
log_warning "Warnings: ${CHECKS_WARNING}"
|
||||
fi
|
||||
|
||||
TOTAL=$((CHECKS_PASSED + CHECKS_FAILED + CHECKS_WARNING))
|
||||
PERCENTAGE=$((CHECKS_PASSED * 100 / TOTAL))
|
||||
|
||||
echo ""
|
||||
if [ ${CHECKS_FAILED} -eq 0 ]; then
|
||||
log_success "All critical checks passed! (${PERCENTAGE}%)"
|
||||
if [ ${CHECKS_WARNING} -gt 0 ]; then
|
||||
log_warning "Some optional checks need attention"
|
||||
fi
|
||||
exit 0
|
||||
else
|
||||
log_error "Some critical checks failed (${PERCENTAGE}%)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user