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:
93
scripts/ci/validate-entra-deployment.sh
Executable file
93
scripts/ci/validate-entra-deployment.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
# CI/CD Validation Script for Entra VerifiedID Deployment
|
||||
# Validates code, tests, and configuration before deployment
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[CI]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[PASS]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[FAIL]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
ERRORS=0
|
||||
|
||||
log_info "Running Entra VerifiedID Deployment Validation..."
|
||||
|
||||
# 1. TypeScript compilation
|
||||
log_info "1. Checking TypeScript compilation..."
|
||||
if pnpm build 2>&1 | grep -q "error TS"; then
|
||||
log_error "TypeScript compilation errors found"
|
||||
((ERRORS++))
|
||||
else
|
||||
log_success "TypeScript compilation passed"
|
||||
fi
|
||||
|
||||
# 2. Linting
|
||||
log_info "2. Running linter..."
|
||||
if pnpm lint 2>&1 | grep -q "✖.*error"; then
|
||||
log_error "Linting errors found"
|
||||
((ERRORS++))
|
||||
else
|
||||
log_success "Linting passed"
|
||||
fi
|
||||
|
||||
# 3. Unit tests
|
||||
log_info "3. Running unit tests..."
|
||||
if pnpm --filter @the-order/auth test entra-verifiedid.test.ts --run 2>&1 | grep -q "FAIL"; then
|
||||
log_error "Unit tests failed"
|
||||
((ERRORS++))
|
||||
else
|
||||
log_success "Unit tests passed"
|
||||
fi
|
||||
|
||||
# 4. Check required files
|
||||
log_info "4. Checking required files..."
|
||||
REQUIRED_FILES=(
|
||||
"packages/auth/src/entra-verifiedid.ts"
|
||||
"packages/auth/src/entra-verifiedid-enhanced.ts"
|
||||
"services/identity/src/entra-integration.ts"
|
||||
"services/identity/src/entra-webhooks.ts"
|
||||
"infra/k8s/identity-service-entra-secrets.yaml"
|
||||
"infra/k8s/identity-service-deployment-entra.yaml"
|
||||
)
|
||||
|
||||
for file in "${REQUIRED_FILES[@]}"; do
|
||||
if [ ! -f "${file}" ]; then
|
||||
log_error "Required file missing: ${file}"
|
||||
((ERRORS++))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${ERRORS} -eq 0 ]; then
|
||||
log_success "All required files present"
|
||||
fi
|
||||
|
||||
# 5. Validate Kubernetes manifests
|
||||
log_info "5. Validating Kubernetes manifests..."
|
||||
if command -v kubectl &> /dev/null; then
|
||||
if kubectl apply --dry-run=client -f infra/k8s/identity-service-deployment-entra.yaml &> /dev/null; then
|
||||
log_success "Kubernetes manifests are valid"
|
||||
else
|
||||
log_error "Kubernetes manifest validation failed"
|
||||
((ERRORS++))
|
||||
fi
|
||||
else
|
||||
log_info "kubectl not available, skipping manifest validation"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
if [ ${ERRORS} -eq 0 ]; then
|
||||
log_success "All validations passed! Ready for deployment."
|
||||
exit 0
|
||||
else
|
||||
log_error "${ERRORS} validation error(s) found. Fix before deploying."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
34
scripts/fix-lint-errors.sh
Executable file
34
scripts/fix-lint-errors.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Script to systematically fix common lint errors
|
||||
# Adds return types to React components and fixes type issues
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
echo "Fixing lint errors systematically..."
|
||||
|
||||
# Fix all page components - add return types
|
||||
find apps/portal-internal/src/app apps/portal-public/src/app -name "page.tsx" -type f | while read -r file; do
|
||||
if ! grep -q "export default function.*JSX.Element" "$file"; then
|
||||
# Add return type to default export function
|
||||
sed -i 's/export default function \([^(]*\)() {/export default function \1(): JSX.Element {/g' "$file"
|
||||
echo "Fixed: $file"
|
||||
fi
|
||||
done
|
||||
|
||||
# Fix layout files
|
||||
find apps/portal-internal/src/app apps/portal-public/src/app -name "layout.tsx" -type f | while read -r file; do
|
||||
if ! grep -q "export default function.*JSX.Element" "$file"; then
|
||||
sed -i 's/export default function RootLayout({/export default function RootLayout({/g' "$file"
|
||||
sed -i 's/}: {/}: {/g' "$file"
|
||||
sed -i 's/children: ReactNode;$/children: ReactNode;\n}): JSX.Element {/g' "$file"
|
||||
echo "Fixed: $file"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Lint error fixes applied. Run 'pnpm lint' to verify."
|
||||
|
||||
202
scripts/test/generate-test-data.sh
Executable file
202
scripts/test/generate-test-data.sh
Executable file
@@ -0,0 +1,202 @@
|
||||
#!/bin/bash
|
||||
# Generate test data for Entra VerifiedID testing
|
||||
# Creates sample credentials, test payloads, and validation data
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
TEST_DATA_DIR="test-data/entra"
|
||||
mkdir -p "${TEST_DATA_DIR}"
|
||||
|
||||
log_info "Generating test data for Entra VerifiedID..."
|
||||
|
||||
# Sample credential issuance request
|
||||
cat > "${TEST_DATA_DIR}/issuance-request.json" << 'EOF'
|
||||
{
|
||||
"claims": {
|
||||
"email": "test@example.com",
|
||||
"name": "Test User",
|
||||
"role": "member",
|
||||
"userId": "user-123"
|
||||
},
|
||||
"pin": "1234",
|
||||
"callbackUrl": "https://api.theorder.org/vc/entra/webhook"
|
||||
}
|
||||
EOF
|
||||
|
||||
# Multi-manifest issuance requests
|
||||
cat > "${TEST_DATA_DIR}/issuance-request-diplomatic.json" << 'EOF'
|
||||
{
|
||||
"claims": {
|
||||
"recipientName": "John Doe",
|
||||
"recipientTitle": "Ambassador",
|
||||
"missionCountry": "France",
|
||||
"missionType": "embassy"
|
||||
},
|
||||
"manifestName": "diplomatic"
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > "${TEST_DATA_DIR}/issuance-request-judicial.json" << 'EOF'
|
||||
{
|
||||
"claims": {
|
||||
"role": "judge",
|
||||
"appointmentAuthority": "Supreme Court",
|
||||
"jurisdiction": "EU",
|
||||
"appointmentDate": "2024-01-01T00:00:00Z"
|
||||
},
|
||||
"manifestName": "judicial"
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > "${TEST_DATA_DIR}/issuance-request-financial.json" << 'EOF'
|
||||
{
|
||||
"claims": {
|
||||
"role": "financial-officer",
|
||||
"appointmentDate": "2024-01-01T00:00:00Z",
|
||||
"jurisdiction": "EU"
|
||||
},
|
||||
"manifestName": "financial"
|
||||
}
|
||||
EOF
|
||||
|
||||
# Webhook test payloads
|
||||
cat > "${TEST_DATA_DIR}/webhook-issuance-successful.json" << 'EOF'
|
||||
{
|
||||
"requestId": "test-request-id-123",
|
||||
"requestStatus": "issuance_successful",
|
||||
"credential": {
|
||||
"id": "vc:test:123",
|
||||
"type": ["VerifiableCredential", "IdentityCredential"],
|
||||
"issuer": "did:web:test.verifiedid.msidentity.com",
|
||||
"issuanceDate": "2024-01-01T00:00:00Z",
|
||||
"credentialSubject": {
|
||||
"email": "test@example.com",
|
||||
"name": "Test User"
|
||||
},
|
||||
"proof": {
|
||||
"type": "JsonWebSignature2020",
|
||||
"created": "2024-01-01T00:00:00Z",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"verificationMethod": "did:web:test#key",
|
||||
"jws": "test-jws-signature"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > "${TEST_DATA_DIR}/webhook-issuance-failed.json" << 'EOF'
|
||||
{
|
||||
"requestId": "test-request-id-123",
|
||||
"requestStatus": "issuance_failed",
|
||||
"error": {
|
||||
"code": "ISSUANCE_FAILED",
|
||||
"message": "Test error message"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Verification test payload
|
||||
cat > "${TEST_DATA_DIR}/verification-request.json" << 'EOF'
|
||||
{
|
||||
"credential": {
|
||||
"id": "vc:test:123",
|
||||
"type": ["VerifiableCredential", "IdentityCredential"],
|
||||
"issuer": "did:web:test.verifiedid.msidentity.com",
|
||||
"issuanceDate": "2024-01-01T00:00:00Z",
|
||||
"credentialSubject": {
|
||||
"email": "test@example.com",
|
||||
"name": "Test User"
|
||||
},
|
||||
"proof": {
|
||||
"type": "JsonWebSignature2020",
|
||||
"created": "2024-01-01T00:00:00Z",
|
||||
"proofPurpose": "assertionMethod",
|
||||
"verificationMethod": "did:web:test#key",
|
||||
"jws": "test-jws-signature"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# eIDAS bridge test payload
|
||||
cat > "${TEST_DATA_DIR}/eidas-verify-issue-request.json" << 'EOF'
|
||||
{
|
||||
"document": "base64-encoded-document-here",
|
||||
"userId": "user-123",
|
||||
"userEmail": "test@example.com",
|
||||
"pin": "1234"
|
||||
}
|
||||
EOF
|
||||
|
||||
# Test script for API endpoints
|
||||
cat > "${TEST_DATA_DIR}/test-endpoints.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Test Entra VerifiedID API endpoints
|
||||
|
||||
BASE_URL="${API_BASE_URL:-http://localhost:4002}"
|
||||
AUTH_TOKEN="${AUTH_TOKEN:-}"
|
||||
|
||||
echo "Testing Entra VerifiedID endpoints..."
|
||||
|
||||
# Test issuance
|
||||
echo "1. Testing credential issuance..."
|
||||
curl -X POST "${BASE_URL}/vc/issue/entra" \
|
||||
-H "Content-Type: application/json" \
|
||||
${AUTH_TOKEN:+-H "Authorization: Bearer ${AUTH_TOKEN}"} \
|
||||
-d @issuance-request.json
|
||||
|
||||
echo -e "\n\n2. Testing credential verification..."
|
||||
curl -X POST "${BASE_URL}/vc/verify/entra" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @verification-request.json
|
||||
|
||||
echo -e "\n\n3. Testing webhook endpoint..."
|
||||
curl -X POST "${BASE_URL}/vc/entra/webhook" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @webhook-issuance-successful.json
|
||||
|
||||
echo -e "\n\n4. Testing status endpoint..."
|
||||
curl "${BASE_URL}/vc/entra/status/test-request-id-123"
|
||||
EOF
|
||||
|
||||
chmod +x "${TEST_DATA_DIR}/test-endpoints.sh"
|
||||
|
||||
# Rate limit test script
|
||||
cat > "${TEST_DATA_DIR}/test-rate-limits.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Test rate limiting by sending multiple requests
|
||||
|
||||
BASE_URL="${API_BASE_URL:-http://localhost:4002}"
|
||||
AUTH_TOKEN="${AUTH_TOKEN:-}"
|
||||
|
||||
echo "Testing rate limits (sending 15 requests rapidly)..."
|
||||
|
||||
for i in {1..15}; do
|
||||
echo "Request $i..."
|
||||
curl -X POST "${BASE_URL}/vc/issue/entra" \
|
||||
-H "Content-Type: application/json" \
|
||||
${AUTH_TOKEN:+-H "Authorization: Bearer ${AUTH_TOKEN}"} \
|
||||
-d '{"claims": {"test": "true"}}' \
|
||||
-w "\nStatus: %{http_code}\n" \
|
||||
-s -o /dev/null
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
echo "Rate limit test complete. Check for 429 responses."
|
||||
EOF
|
||||
|
||||
chmod +x "${TEST_DATA_DIR}/test-rate-limits.sh"
|
||||
|
||||
log_success "Test data generated in ${TEST_DATA_DIR}/"
|
||||
log_info "Files created:"
|
||||
ls -la "${TEST_DATA_DIR}"
|
||||
|
||||
90
scripts/test/run-integration-tests-with-setup.sh
Executable file
90
scripts/test/run-integration-tests-with-setup.sh
Executable file
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
# Run Integration Tests with Automatic Setup
|
||||
# Checks for credentials and provides setup instructions if missing
|
||||
|
||||
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")/../.."
|
||||
|
||||
log_info "Entra VerifiedID Integration Test Runner"
|
||||
echo ""
|
||||
|
||||
# Check for environment variables
|
||||
MISSING_VARS=()
|
||||
|
||||
check_var() {
|
||||
local var=$1
|
||||
if [ -z "${!var:-}" ]; then
|
||||
MISSING_VARS+=("${var}")
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
check_var "ENTRA_TENANT_ID" || true
|
||||
check_var "ENTRA_CLIENT_ID" || true
|
||||
check_var "ENTRA_CLIENT_SECRET" || true
|
||||
check_var "ENTRA_CREDENTIAL_MANIFEST_ID" || true
|
||||
|
||||
if [ ${#MISSING_VARS[@]} -gt 0 ]; then
|
||||
log_warning "Missing required environment variables:"
|
||||
for var in "${MISSING_VARS[@]}"; do
|
||||
echo " - ${var}"
|
||||
done
|
||||
echo ""
|
||||
log_info "To set up credentials:"
|
||||
echo "1. Run: ./scripts/deploy/setup-entra-automated.sh"
|
||||
echo "2. Or manually set environment variables:"
|
||||
echo " export ENTRA_TENANT_ID=<tenant-id>"
|
||||
echo " export ENTRA_CLIENT_ID=<client-id>"
|
||||
echo " export ENTRA_CLIENT_SECRET=<client-secret>"
|
||||
echo " export ENTRA_CREDENTIAL_MANIFEST_ID=<manifest-id>"
|
||||
echo ""
|
||||
log_info "Loading from .env file if available..."
|
||||
if [ -f ".env" ]; then
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
log_info "Loaded from .env"
|
||||
fi
|
||||
|
||||
# Re-check
|
||||
MISSING_VARS=()
|
||||
check_var "ENTRA_TENANT_ID" || true
|
||||
check_var "ENTRA_CLIENT_ID" || true
|
||||
check_var "ENTRA_CLIENT_SECRET" || true
|
||||
check_var "ENTRA_CREDENTIAL_MANIFEST_ID" || true
|
||||
|
||||
if [ ${#MISSING_VARS[@]} -gt 0 ]; then
|
||||
log_error "Still missing required variables. Cannot run integration tests."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_success "All required environment variables are set"
|
||||
echo ""
|
||||
|
||||
# Run integration tests
|
||||
log_info "Running integration tests..."
|
||||
echo ""
|
||||
|
||||
if pnpm --filter @the-order/auth test entra-verifiedid.integration.test.ts --run 2>&1 | tee /tmp/integration-test.log; then
|
||||
log_success "Integration tests passed!"
|
||||
exit 0
|
||||
else
|
||||
log_error "Integration tests failed"
|
||||
log_info "Check /tmp/integration-test.log for details"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
208
scripts/test/test-all-entra-features.sh
Executable file
208
scripts/test/test-all-entra-features.sh
Executable file
@@ -0,0 +1,208 @@
|
||||
#!/bin/bash
|
||||
# Comprehensive test script for all Entra VerifiedID features
|
||||
# Tests issuance, verification, webhooks, retry, rate limiting, multi-manifest
|
||||
|
||||
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}[TEST]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[PASS]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[FAIL]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[SKIP]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
BASE_URL="${API_BASE_URL:-http://localhost:4002}"
|
||||
AUTH_TOKEN="${AUTH_TOKEN:-}"
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
TESTS_SKIPPED=0
|
||||
|
||||
# Test function
|
||||
run_test() {
|
||||
local test_name=$1
|
||||
local test_command=$2
|
||||
|
||||
log_info "Testing: ${test_name}"
|
||||
if eval "${test_command}" > /tmp/test-output.log 2>&1; then
|
||||
log_success "${test_name}"
|
||||
((TESTS_PASSED++))
|
||||
return 0
|
||||
else
|
||||
log_error "${test_name}"
|
||||
cat /tmp/test-output.log | head -5
|
||||
((TESTS_FAILED++))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if service is running
|
||||
log_info "Checking if service is running..."
|
||||
if ! curl -sf "${BASE_URL}/health" > /dev/null; then
|
||||
log_warning "Service not running at ${BASE_URL}"
|
||||
log_warning "Skipping API tests (unit tests will still run)"
|
||||
SKIP_API_TESTS=true
|
||||
else
|
||||
SKIP_API_TESTS=false
|
||||
log_success "Service is running"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "=== Running Entra VerifiedID Feature Tests ==="
|
||||
echo ""
|
||||
|
||||
# 1. Unit Tests
|
||||
log_info "1. Running Unit Tests..."
|
||||
if pnpm --filter @the-order/auth test entra-verifiedid.test.ts --run 2>&1 | tee /tmp/unit-test.log; then
|
||||
log_success "Unit tests passed"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
log_error "Unit tests failed"
|
||||
((TESTS_FAILED++))
|
||||
fi
|
||||
|
||||
# 2. Credential Issuance Test
|
||||
if [ "${SKIP_API_TESTS}" = "false" ]; then
|
||||
log_info "2. Testing Credential Issuance..."
|
||||
ISSUANCE_RESPONSE=$(curl -s -X POST "${BASE_URL}/vc/issue/entra" \
|
||||
-H "Content-Type: application/json" \
|
||||
${AUTH_TOKEN:+-H "Authorization: Bearer ${AUTH_TOKEN}"} \
|
||||
-d '{"claims": {"email": "test@example.com", "name": "Test User"}}' || echo "ERROR")
|
||||
|
||||
if echo "${ISSUANCE_RESPONSE}" | jq -e '.requestId' > /dev/null 2>&1; then
|
||||
REQUEST_ID=$(echo "${ISSUANCE_RESPONSE}" | jq -r '.requestId')
|
||||
log_success "Credential issuance successful (Request ID: ${REQUEST_ID})"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
log_error "Credential issuance failed"
|
||||
echo "${ISSUANCE_RESPONSE}" | head -3
|
||||
((TESTS_FAILED++))
|
||||
fi
|
||||
else
|
||||
log_warning "Skipping credential issuance test (service not running)"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
|
||||
# 3. Status Endpoint Test
|
||||
if [ "${SKIP_API_TESTS}" = "false" ] && [ -n "${REQUEST_ID:-}" ]; then
|
||||
log_info "3. Testing Status Endpoint..."
|
||||
STATUS_RESPONSE=$(curl -s "${BASE_URL}/vc/entra/status/${REQUEST_ID}" || echo "ERROR")
|
||||
if echo "${STATUS_RESPONSE}" | jq -e '.requestId' > /dev/null 2>&1; then
|
||||
log_success "Status endpoint working"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
log_warning "Status endpoint test inconclusive"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
else
|
||||
log_warning "Skipping status endpoint test"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
|
||||
# 4. Webhook Endpoint Test
|
||||
if [ "${SKIP_API_TESTS}" = "false" ]; then
|
||||
log_info "4. Testing Webhook Endpoint..."
|
||||
WEBHOOK_RESPONSE=$(curl -s -X POST "${BASE_URL}/vc/entra/webhook" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"requestId":"test-123","requestStatus":"issuance_successful"}' || echo "ERROR")
|
||||
|
||||
if echo "${WEBHOOK_RESPONSE}" | jq -e '.received' > /dev/null 2>&1; then
|
||||
log_success "Webhook endpoint working"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
log_error "Webhook endpoint test failed"
|
||||
((TESTS_FAILED++))
|
||||
fi
|
||||
else
|
||||
log_warning "Skipping webhook endpoint test"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
|
||||
# 5. Multi-Manifest Test
|
||||
if [ "${SKIP_API_TESTS}" = "false" ]; then
|
||||
log_info "5. Testing Multi-Manifest Support..."
|
||||
MULTI_MANIFEST_RESPONSE=$(curl -s -X POST "${BASE_URL}/vc/issue/entra" \
|
||||
-H "Content-Type: application/json" \
|
||||
${AUTH_TOKEN:+-H "Authorization: Bearer ${AUTH_TOKEN}"} \
|
||||
-d '{"claims": {"test": "true"}, "manifestName": "diplomatic"}' || echo "ERROR")
|
||||
|
||||
if echo "${MULTI_MANIFEST_RESPONSE}" | jq -e '.requestId' > /dev/null 2>&1; then
|
||||
log_success "Multi-manifest support working"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
log_warning "Multi-manifest test inconclusive (may require valid manifest)"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
else
|
||||
log_warning "Skipping multi-manifest test"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
|
||||
# 6. Rate Limiting Test
|
||||
if [ "${SKIP_API_TESTS}" = "false" ]; then
|
||||
log_info "6. Testing Rate Limiting..."
|
||||
RATE_LIMIT_HIT=false
|
||||
for i in {1..15}; do
|
||||
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "${BASE_URL}/vc/issue/entra" \
|
||||
-H "Content-Type: application/json" \
|
||||
${AUTH_TOKEN:+-H "Authorization: Bearer ${AUTH_TOKEN}"} \
|
||||
-d '{"claims": {"test": "true"}}')
|
||||
if [ "${HTTP_CODE}" = "429" ]; then
|
||||
RATE_LIMIT_HIT=true
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
if [ "${RATE_LIMIT_HIT}" = "true" ]; then
|
||||
log_success "Rate limiting working (429 received)"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
log_warning "Rate limit not hit (may need to adjust limits or test more aggressively)"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
else
|
||||
log_warning "Skipping rate limiting test"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
|
||||
# 7. Metrics Test
|
||||
if [ "${SKIP_API_TESTS}" = "false" ]; then
|
||||
log_info "7. Testing Metrics Endpoint..."
|
||||
METRICS_RESPONSE=$(curl -s "${BASE_URL}/metrics" | grep -c "entra_" || echo "0")
|
||||
if [ "${METRICS_RESPONSE}" -gt 0 ]; then
|
||||
log_success "Metrics endpoint contains Entra metrics (${METRICS_RESPONSE} found)"
|
||||
((TESTS_PASSED++))
|
||||
else
|
||||
log_warning "No Entra metrics found (may not have made requests yet)"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
else
|
||||
log_warning "Skipping metrics test"
|
||||
((TESTS_SKIPPED++))
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
log_info "=== Test Summary ==="
|
||||
log_success "Passed: ${TESTS_PASSED}"
|
||||
if [ ${TESTS_FAILED} -gt 0 ]; then
|
||||
log_error "Failed: ${TESTS_FAILED}"
|
||||
fi
|
||||
if [ ${TESTS_SKIPPED} -gt 0 ]; then
|
||||
log_warning "Skipped: ${TESTS_SKIPPED}"
|
||||
fi
|
||||
|
||||
if [ ${TESTS_FAILED} -eq 0 ]; then
|
||||
log_success "All tests passed or skipped!"
|
||||
exit 0
|
||||
else
|
||||
log_error "Some tests failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
79
scripts/test/test-entra-integration.sh
Executable file
79
scripts/test/test-entra-integration.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
# Test Entra VerifiedID Integration
|
||||
# Runs all integration tests for Entra VerifiedID
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[TEST]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[PASS]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[FAIL]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
log_info "Running Entra VerifiedID Integration Tests..."
|
||||
|
||||
# Check if environment variables are set
|
||||
if [ -z "${ENTRA_TENANT_ID:-}" ] || [ -z "${ENTRA_CLIENT_ID:-}" ] || [ -z "${ENTRA_CLIENT_SECRET:-}" ]; then
|
||||
log_error "Entra environment variables not set"
|
||||
log_info "Set: ENTRA_TENANT_ID, ENTRA_CLIENT_ID, ENTRA_CLIENT_SECRET"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run unit tests
|
||||
log_info "Running unit tests..."
|
||||
if pnpm --filter @the-order/auth test entra-verifiedid.test.ts 2>&1 | tee /tmp/entra-unit-test.log; then
|
||||
log_success "Unit tests passed"
|
||||
else
|
||||
log_error "Unit tests failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run integration tests (if credentials are available)
|
||||
if [ -n "${ENTRA_CREDENTIAL_MANIFEST_ID:-}" ]; then
|
||||
log_info "Running integration tests..."
|
||||
if pnpm --filter @the-order/auth test entra-verifiedid.integration.test.ts 2>&1 | tee /tmp/entra-integration-test.log; then
|
||||
log_success "Integration tests passed"
|
||||
else
|
||||
log_warning "Integration tests failed (may require valid Entra credentials)"
|
||||
fi
|
||||
else
|
||||
log_info "Skipping integration tests (ENTRA_CREDENTIAL_MANIFEST_ID not set)"
|
||||
fi
|
||||
|
||||
# Test API endpoints (if service is running)
|
||||
log_info "Testing API endpoints..."
|
||||
BASE_URL="${API_BASE_URL:-http://localhost:4002}"
|
||||
|
||||
# Test health endpoint
|
||||
if curl -sf "${BASE_URL}/health" > /dev/null; then
|
||||
log_success "Service is running"
|
||||
|
||||
# Test issuance endpoint (will fail without valid credentials, but tests endpoint exists)
|
||||
if curl -sf -X POST "${BASE_URL}/vc/issue/entra" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"claims": {"test": "true"}}' > /dev/null 2>&1; then
|
||||
log_success "Issuance endpoint accessible"
|
||||
else
|
||||
log_info "Issuance endpoint exists (authentication may be required)"
|
||||
fi
|
||||
|
||||
# Test webhook endpoint
|
||||
if curl -sf -X POST "${BASE_URL}/vc/entra/webhook" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"requestId":"test","requestStatus":"issuance_successful"}' > /dev/null 2>&1; then
|
||||
log_success "Webhook endpoint accessible"
|
||||
else
|
||||
log_info "Webhook endpoint exists"
|
||||
fi
|
||||
else
|
||||
log_info "Service not running locally, skipping API endpoint tests"
|
||||
fi
|
||||
|
||||
log_success "All automated tests completed!"
|
||||
log_info "Review test logs in /tmp/entra-*.log"
|
||||
|
||||
91
scripts/tools/convert-svg-to-png.sh
Executable file
91
scripts/tools/convert-svg-to-png.sh
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
# Convert SVG files to PNG for Entra VerifiedID credential images
|
||||
# Supports multiple conversion methods
|
||||
|
||||
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"; }
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <input.svg> [output.png] [width] [height]"
|
||||
echo ""
|
||||
echo "Converts SVG to PNG for Entra VerifiedID credential images"
|
||||
echo ""
|
||||
echo "Arguments:"
|
||||
echo " input.svg - Input SVG file"
|
||||
echo " output.png - Output PNG file (default: input.png)"
|
||||
echo " width - Output width in pixels (default: 200)"
|
||||
echo " height - Output height in pixels (default: 200)"
|
||||
echo ""
|
||||
echo "Requirements:"
|
||||
echo " - ImageMagick (convert) OR"
|
||||
echo " - Inkscape OR"
|
||||
echo " - Node.js with sharp package"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
INPUT_FILE="$1"
|
||||
OUTPUT_FILE="${2:-${INPUT_FILE%.svg}.png}"
|
||||
WIDTH="${3:-200}"
|
||||
HEIGHT="${4:-200}"
|
||||
|
||||
if [ ! -f "${INPUT_FILE}" ]; then
|
||||
log_error "Input file not found: ${INPUT_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Converting ${INPUT_FILE} to ${OUTPUT_FILE} (${WIDTH}x${HEIGHT})"
|
||||
|
||||
# Try ImageMagick first
|
||||
if command -v convert &> /dev/null; then
|
||||
log_info "Using ImageMagick..."
|
||||
convert -background none -resize "${WIDTH}x${HEIGHT}" "${INPUT_FILE}" "${OUTPUT_FILE}"
|
||||
log_success "Conversion complete: ${OUTPUT_FILE}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Try Inkscape
|
||||
if command -v inkscape &> /dev/null; then
|
||||
log_info "Using Inkscape..."
|
||||
inkscape "${INPUT_FILE}" --export-filename="${OUTPUT_FILE}" --export-width="${WIDTH}" --export-height="${HEIGHT}" --export-type=png
|
||||
log_success "Conversion complete: ${OUTPUT_FILE}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Try Node.js with sharp
|
||||
if command -v node &> /dev/null; then
|
||||
log_info "Trying Node.js conversion..."
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const sharp = require('sharp');
|
||||
sharp('${INPUT_FILE}')
|
||||
.resize(${WIDTH}, ${HEIGHT})
|
||||
.png()
|
||||
.toFile('${OUTPUT_FILE}')
|
||||
.then(() => console.log('Conversion complete'))
|
||||
.catch(err => {
|
||||
console.error('Sharp not available or error:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
" 2>/dev/null && log_success "Conversion complete: ${OUTPUT_FILE}" && exit 0
|
||||
fi
|
||||
|
||||
log_error "No conversion tool found. Install one of:"
|
||||
echo " - ImageMagick: sudo apt-get install imagemagick"
|
||||
echo " - Inkscape: sudo apt-get install inkscape"
|
||||
echo " - sharp (Node.js): pnpm add sharp"
|
||||
exit 1
|
||||
|
||||
66
scripts/tools/convert-svg-with-sharp.js
Executable file
66
scripts/tools/convert-svg-with-sharp.js
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Convert SVG to PNG using sharp
|
||||
* Usage: node convert-svg-with-sharp.js <input.svg> <output.png> <width> <height>
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Try to find sharp in various locations
|
||||
let sharp;
|
||||
try {
|
||||
sharp = require('sharp');
|
||||
} catch (e) {
|
||||
// Try packages/auth/node_modules/sharp
|
||||
const authSharpPath = path.join(__dirname, '../../packages/auth/node_modules/sharp');
|
||||
if (fs.existsSync(authSharpPath)) {
|
||||
sharp = require(authSharpPath);
|
||||
} else {
|
||||
// Try root node_modules/sharp
|
||||
const rootSharpPath = path.join(__dirname, '../../node_modules/sharp');
|
||||
if (fs.existsSync(rootSharpPath)) {
|
||||
sharp = require(rootSharpPath);
|
||||
} else {
|
||||
console.error('Error: sharp module not found. Install with: pnpm add sharp');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [,, inputFile, outputFile, width, height] = process.argv;
|
||||
|
||||
if (!inputFile || !outputFile) {
|
||||
console.error('Usage: node convert-svg-with-sharp.js <input.svg> <output.png> <width> <height>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const inputPath = path.resolve(inputFile);
|
||||
const outputPath = path.resolve(outputFile);
|
||||
const widthNum = parseInt(width || '200', 10);
|
||||
const heightNum = parseInt(height || '200', 10);
|
||||
|
||||
if (!fs.existsSync(inputPath)) {
|
||||
console.error(`Error: Input file not found: ${inputPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Ensure output directory exists
|
||||
const outputDir = path.dirname(outputPath);
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
sharp(inputPath)
|
||||
.resize(widthNum, heightNum)
|
||||
.png()
|
||||
.toFile(outputPath)
|
||||
.then(() => {
|
||||
console.log(`Success: ${outputPath}`);
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`Error converting ${inputPath}:`, error.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
117
scripts/tools/prepare-credential-images.sh
Executable file
117
scripts/tools/prepare-credential-images.sh
Executable file
@@ -0,0 +1,117 @@
|
||||
#!/bin/bash
|
||||
# Prepare all credential images from SVG sources
|
||||
# Converts SVG files to PNG for Entra VerifiedID compatibility
|
||||
|
||||
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")/../.."
|
||||
|
||||
IMAGES_DIR="assets/credential-images"
|
||||
SVG_DIR="${IMAGES_DIR}/svg"
|
||||
PNG_DIR="${IMAGES_DIR}/png"
|
||||
|
||||
mkdir -p "${SVG_DIR}" "${PNG_DIR}"
|
||||
|
||||
log_info "Preparing credential images from SVG sources"
|
||||
echo ""
|
||||
|
||||
# Check for SVG files
|
||||
if [ ! -d "${SVG_DIR}" ] || [ -z "$(find "${SVG_DIR}" -name "*.svg" 2>/dev/null)" ]; then
|
||||
log_warning "No SVG files found in ${SVG_DIR}"
|
||||
log_info "Place your SVG logo files in: ${SVG_DIR}/"
|
||||
log_info "Expected files:"
|
||||
echo " - default-logo.svg"
|
||||
echo " - diplomatic-logo.svg"
|
||||
echo " - judicial-logo.svg"
|
||||
echo " - financial-logo.svg"
|
||||
echo ""
|
||||
log_info "Creating example structure..."
|
||||
|
||||
# Create example SVG files (placeholder)
|
||||
cat > "${SVG_DIR}/.gitkeep" << 'EOF'
|
||||
# Place your SVG logo files here
|
||||
# Files will be automatically converted to PNG for Entra VerifiedID
|
||||
EOF
|
||||
|
||||
log_info "After adding SVG files, run this script again to convert them"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Convert each SVG to PNG
|
||||
log_info "Converting SVG files to PNG..."
|
||||
for svg_file in "${SVG_DIR}"/*.svg; do
|
||||
if [ -f "${svg_file}" ]; then
|
||||
filename=$(basename "${svg_file}" .svg)
|
||||
png_file="${PNG_DIR}/${filename}.png"
|
||||
|
||||
log_info "Converting: ${filename}.svg → ${filename}.png"
|
||||
if ./scripts/tools/convert-svg-to-png.sh "${svg_file}" "${png_file}" 200 200; then
|
||||
log_success "Created: ${png_file}"
|
||||
else
|
||||
log_warning "Failed to convert ${filename}.svg"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Create multiple sizes
|
||||
log_info "Creating multiple sizes..."
|
||||
SIZES=(200 400 800)
|
||||
for size in "${SIZES[@]}"; do
|
||||
for svg_file in "${SVG_DIR}"/*.svg; do
|
||||
if [ -f "${svg_file}" ]; then
|
||||
filename=$(basename "${svg_file}" .svg)
|
||||
png_file="${PNG_DIR}/${filename}-${size}x${size}.png"
|
||||
|
||||
if ./scripts/tools/convert-svg-to-png.sh "${svg_file}" "${png_file}" "${size}" "${size}" 2>/dev/null; then
|
||||
log_success "Created: ${filename}-${size}x${size}.png"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Generate upload instructions
|
||||
cat > "${PNG_DIR}/UPLOAD_INSTRUCTIONS.md" << 'EOF'
|
||||
# Credential Image Upload Instructions
|
||||
|
||||
## Generated PNG Files
|
||||
|
||||
PNG files have been generated from SVG sources for Entra VerifiedID compatibility.
|
||||
|
||||
## Upload to CDN/Storage
|
||||
|
||||
1. Upload all PNG files to your CDN or static storage
|
||||
2. Ensure files are publicly accessible via HTTPS
|
||||
3. Update manifest templates with the image URLs
|
||||
|
||||
## Recommended URLs
|
||||
|
||||
- Default: `https://cdn.theorder.org/images/credential-logo.png`
|
||||
- Diplomatic: `https://cdn.theorder.org/images/diplomatic-logo.png`
|
||||
- Judicial: `https://cdn.theorder.org/images/judicial-logo.png`
|
||||
- Financial: `https://cdn.theorder.org/images/financial-logo.png`
|
||||
|
||||
## Update Configuration
|
||||
|
||||
After uploading, update:
|
||||
- Manifest templates in `manifests/entra/`
|
||||
- Environment variable: `ENTRA_CREDENTIAL_LOGO_URI`
|
||||
- Or in code: `logoUri` in `EntraVerifiedIDClient` config
|
||||
EOF
|
||||
|
||||
log_success "Image preparation complete!"
|
||||
log_info "PNG files created in: ${PNG_DIR}/"
|
||||
log_info "Next steps:"
|
||||
echo "1. Review generated PNG files"
|
||||
echo "2. Upload to CDN/storage"
|
||||
echo "3. Update manifest templates with image URLs"
|
||||
echo "4. See: ${PNG_DIR}/UPLOAD_INSTRUCTIONS.md"
|
||||
|
||||
256
scripts/validation/check-seal-deployment-issues.sh
Executable file
256
scripts/validation/check-seal-deployment-issues.sh
Executable file
@@ -0,0 +1,256 @@
|
||||
#!/bin/bash
|
||||
# Comprehensive check for gaps, errors, issues, and warnings
|
||||
# in the Order of St John seal deployment process
|
||||
|
||||
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}[CHECK]${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")/../.."
|
||||
|
||||
ISSUES=0
|
||||
WARNINGS=0
|
||||
ERRORS=0
|
||||
|
||||
echo ""
|
||||
log_info "=== Comprehensive Seal Deployment Issue Check ==="
|
||||
echo ""
|
||||
|
||||
# 1. Pre-deployment checks
|
||||
log_info "1. PRE-DEPLOYMENT CHECKS"
|
||||
echo ""
|
||||
|
||||
# Check script permissions
|
||||
log_info " Checking script permissions..."
|
||||
for script in scripts/deploy/prepare-all-credential-seals.sh \
|
||||
scripts/validation/validate-seal-files.sh \
|
||||
scripts/deploy/complete-seal-deployment.sh \
|
||||
scripts/deploy/update-manifest-seal-urls.sh; do
|
||||
if [ -f "${script}" ]; then
|
||||
if [ -x "${script}" ]; then
|
||||
log_success " ${script}: Executable"
|
||||
else
|
||||
log_error " ${script}: Not executable"
|
||||
((ERRORS++))
|
||||
fi
|
||||
else
|
||||
log_error " ${script}: File not found"
|
||||
((ERRORS++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check script syntax
|
||||
log_info " Checking script syntax..."
|
||||
for script in scripts/deploy/prepare-all-credential-seals.sh \
|
||||
scripts/validation/validate-seal-files.sh \
|
||||
scripts/deploy/complete-seal-deployment.sh \
|
||||
scripts/deploy/update-manifest-seal-urls.sh; do
|
||||
if bash -n "${script}" 2>/dev/null; then
|
||||
log_success " ${script}: Syntax valid"
|
||||
else
|
||||
log_error " ${script}: Syntax errors"
|
||||
((ERRORS++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check directories
|
||||
log_info " Checking directories..."
|
||||
[ -d "assets/credential-images/svg" ] && log_success " SVG directory exists" || { log_error " SVG directory missing"; ((ERRORS++)); }
|
||||
[ -d "assets/credential-images/png" ] && log_success " PNG directory exists" || { log_warning " PNG directory missing (will be created)"; ((WARNINGS++)); }
|
||||
|
||||
# Check SVG files
|
||||
log_info " Checking SVG files..."
|
||||
EXPECTED_SVGS=(
|
||||
"digital-bank-seal.svg"
|
||||
"iccc-seal.svg"
|
||||
"iccc-provost-marshals-seal.svg"
|
||||
"diplomatic-security-seal.svg"
|
||||
)
|
||||
|
||||
for svg in "${EXPECTED_SVGS[@]}"; do
|
||||
if [ -f "assets/credential-images/svg/${svg}" ]; then
|
||||
log_success " ${svg}: Exists"
|
||||
# Validate SVG structure
|
||||
if grep -q "viewBox\|<svg" "assets/credential-images/svg/${svg}"; then
|
||||
log_success " Valid SVG structure"
|
||||
else
|
||||
log_error " Invalid SVG structure"
|
||||
((ERRORS++))
|
||||
fi
|
||||
if grep -q "maltese-cross\|Maltese Cross" "assets/credential-images/svg/${svg}" -i; then
|
||||
log_success " Contains Maltese Cross"
|
||||
else
|
||||
log_warning " Maltese Cross reference not found"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
else
|
||||
log_error " ${svg}: Missing"
|
||||
((ERRORS++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check conversion tools
|
||||
log_info " Checking conversion tools..."
|
||||
HAS_CONVERTER=false
|
||||
if command -v convert &> /dev/null; then
|
||||
log_success " ImageMagick: Available"
|
||||
HAS_CONVERTER=true
|
||||
elif command -v inkscape &> /dev/null; then
|
||||
log_success " Inkscape: Available"
|
||||
HAS_CONVERTER=true
|
||||
elif command -v node &> /dev/null && node -e "require('sharp')" 2>/dev/null; then
|
||||
log_success " Node.js with sharp: Available"
|
||||
HAS_CONVERTER=true
|
||||
else
|
||||
log_warning " No conversion tool available (ImageMagick, Inkscape, or sharp)"
|
||||
log_warning " PNG conversion will fail without one of these"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 2. During deployment checks
|
||||
log_info "2. DURING DEPLOYMENT CHECKS"
|
||||
echo ""
|
||||
|
||||
# Run deployment and capture output
|
||||
log_info " Running deployment script..."
|
||||
DEPLOY_LOG="/tmp/seal-deployment-check.log"
|
||||
if ./scripts/deploy/complete-seal-deployment.sh > "${DEPLOY_LOG}" 2>&1; then
|
||||
log_success " Deployment script completed"
|
||||
else
|
||||
log_error " Deployment script failed"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for errors in log
|
||||
log_info " Analyzing deployment log..."
|
||||
ERROR_COUNT=$(grep -i "error\|failed\|✗" "${DEPLOY_LOG}" 2>/dev/null | grep -v "WARNING\|warning" | wc -l || echo "0")
|
||||
WARNING_COUNT=$(grep -i "warning\|!" "${DEPLOY_LOG}" 2>/dev/null | wc -l || echo "0")
|
||||
|
||||
if [ "${ERROR_COUNT}" -gt 0 ]; then
|
||||
log_error " Found ${ERROR_COUNT} error(s) in deployment log"
|
||||
((ERRORS+=ERROR_COUNT))
|
||||
echo " Sample errors:"
|
||||
grep -i "error\|failed\|✗" "${DEPLOY_LOG}" 2>/dev/null | grep -v "WARNING\|warning" | head -5 | sed 's/^/ /'
|
||||
fi
|
||||
|
||||
if [ "${WARNING_COUNT}" -gt 0 ]; then
|
||||
log_warning " Found ${WARNING_COUNT} warning(s) in deployment log"
|
||||
((WARNINGS+=WARNING_COUNT))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 3. Post-deployment checks
|
||||
log_info "3. POST-DEPLOYMENT CHECKS"
|
||||
echo ""
|
||||
|
||||
# Check PNG files
|
||||
log_info " Checking generated PNG files..."
|
||||
PNG_COUNT=$(find assets/credential-images/png -name "*.png" -type f 2>/dev/null | wc -l)
|
||||
if [ "${PNG_COUNT}" -gt 0 ]; then
|
||||
log_success " ${PNG_COUNT} PNG file(s) generated"
|
||||
|
||||
# Validate PNG files
|
||||
INVALID_PNG=0
|
||||
for png in assets/credential-images/png/*.png; do
|
||||
if [ -f "${png}" ]; then
|
||||
if file "${png}" | grep -q "PNG"; then
|
||||
size_kb=$(du -k "${png}" | cut -f1)
|
||||
if [ "${size_kb}" -gt 500 ]; then
|
||||
log_warning " $(basename "${png}"): Large size (${size_kb}KB, recommend <100KB)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
else
|
||||
log_error " $(basename "${png}"): Invalid PNG"
|
||||
((INVALID_PNG++))
|
||||
((ERRORS++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${INVALID_PNG} -eq 0 ]; then
|
||||
log_success " All PNG files are valid"
|
||||
fi
|
||||
else
|
||||
if [ "${HAS_CONVERTER}" = "false" ]; then
|
||||
log_warning " No PNG files generated (conversion tool not available)"
|
||||
else
|
||||
log_error " No PNG files generated (conversion may have failed)"
|
||||
((ERRORS++))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check generated reports
|
||||
log_info " Checking generated reports..."
|
||||
REPORTS=(
|
||||
"assets/credential-images/png/MANIFEST.txt"
|
||||
"assets/credential-images/png/VALIDATION_REPORT.txt"
|
||||
"assets/credential-images/DEPLOYMENT_CHECKLIST.md"
|
||||
"assets/credential-images/DEPLOYMENT_SUMMARY.md"
|
||||
)
|
||||
|
||||
for report in "${REPORTS[@]}"; do
|
||||
if [ -f "${report}" ]; then
|
||||
log_success " $(basename "${report}"): Generated"
|
||||
else
|
||||
log_warning " $(basename "${report}"): Not generated"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check manifest templates
|
||||
log_info " Checking manifest templates..."
|
||||
for manifest in manifests/entra/*-manifest-template.json; do
|
||||
if [ -f "${manifest}" ]; then
|
||||
filename=$(basename "${manifest}")
|
||||
if jq -e '.display.logo.uri' "${manifest}" >/dev/null 2>&1; then
|
||||
log_success " ${filename}: Valid JSON"
|
||||
if grep -q "cdn.theorder.org/images.*seal" "${manifest}"; then
|
||||
log_success " Has seal URL reference"
|
||||
else
|
||||
log_warning " Missing seal URL reference"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
else
|
||||
log_error " ${filename}: Invalid JSON"
|
||||
((ERRORS++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# 4. Summary
|
||||
log_info "=== ISSUE SUMMARY ==="
|
||||
echo ""
|
||||
log_success "Checks passed: Multiple"
|
||||
if [ ${WARNINGS} -gt 0 ]; then
|
||||
log_warning "Warnings: ${WARNINGS}"
|
||||
fi
|
||||
if [ ${ERRORS} -gt 0 ]; then
|
||||
log_error "Errors: ${ERRORS}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [ ${ERRORS} -eq 0 ] && [ ${WARNINGS} -eq 0 ]; then
|
||||
log_success "No issues found! Deployment is ready."
|
||||
exit 0
|
||||
elif [ ${ERRORS} -eq 0 ]; then
|
||||
log_warning "Deployment has warnings but no critical errors."
|
||||
exit 0
|
||||
else
|
||||
log_error "Deployment has errors that need to be fixed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
172
scripts/validation/validate-entra-config.sh
Executable file
172
scripts/validation/validate-entra-config.sh
Executable file
@@ -0,0 +1,172 @@
|
||||
#!/bin/bash
|
||||
# Validate Entra VerifiedID Configuration
|
||||
# Checks all configuration files and environment setup
|
||||
|
||||
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}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[PASS]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[FAIL]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
|
||||
log_info "Validating Entra VerifiedID Configuration..."
|
||||
|
||||
# Check environment variables
|
||||
log_info "Checking environment variables..."
|
||||
if [ -f ".env" ]; then
|
||||
source .env 2>/dev/null || true
|
||||
fi
|
||||
|
||||
check_var() {
|
||||
local var=$1
|
||||
local required=${2:-false}
|
||||
|
||||
if [ -z "${!var:-}" ]; then
|
||||
if [ "${required}" = "true" ]; then
|
||||
log_error "${var} is not set (required)"
|
||||
((ERRORS++))
|
||||
else
|
||||
log_warning "${var} is not set (optional)"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
else
|
||||
log_success "${var} is set"
|
||||
fi
|
||||
}
|
||||
|
||||
check_var "ENTRA_TENANT_ID" true
|
||||
check_var "ENTRA_CLIENT_ID" true
|
||||
check_var "ENTRA_CLIENT_SECRET" true
|
||||
check_var "ENTRA_CREDENTIAL_MANIFEST_ID" true
|
||||
check_var "ENTRA_MANIFESTS" false
|
||||
check_var "ENTRA_RATE_LIMIT_ISSUANCE" false
|
||||
check_var "ENTRA_RATE_LIMIT_VERIFICATION" false
|
||||
|
||||
# Validate manifest JSON if set
|
||||
if [ -n "${ENTRA_MANIFESTS:-}" ]; then
|
||||
log_info "Validating ENTRA_MANIFESTS JSON..."
|
||||
if echo "${ENTRA_MANIFESTS}" | jq empty 2>/dev/null; then
|
||||
log_success "ENTRA_MANIFESTS is valid JSON"
|
||||
else
|
||||
log_error "ENTRA_MANIFESTS is not valid JSON"
|
||||
((ERRORS++))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check required files
|
||||
log_info "Checking required files..."
|
||||
REQUIRED_FILES=(
|
||||
"packages/auth/src/entra-verifiedid.ts"
|
||||
"packages/auth/src/entra-verifiedid-enhanced.ts"
|
||||
"services/identity/src/entra-integration.ts"
|
||||
"services/identity/src/entra-webhooks.ts"
|
||||
"packages/monitoring/src/entra-metrics.ts"
|
||||
)
|
||||
|
||||
for file in "${REQUIRED_FILES[@]}"; do
|
||||
if [ -f "${file}" ]; then
|
||||
log_success "Found: ${file}"
|
||||
else
|
||||
log_error "Missing: ${file}"
|
||||
((ERRORS++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check scripts
|
||||
log_info "Checking automation scripts..."
|
||||
SCRIPTS=(
|
||||
"scripts/deploy/setup-entra-automated.sh"
|
||||
"scripts/deploy/create-entra-app.sh"
|
||||
"scripts/deploy/configure-env-dev.sh"
|
||||
"scripts/test/test-entra-integration.sh"
|
||||
)
|
||||
|
||||
for script in "${SCRIPTS[@]}"; do
|
||||
if [ -f "${script}" ] && [ -x "${script}" ]; then
|
||||
log_success "Found and executable: ${script}"
|
||||
elif [ -f "${script}" ]; then
|
||||
log_warning "Found but not executable: ${script}"
|
||||
((WARNINGS++))
|
||||
else
|
||||
log_error "Missing: ${script}"
|
||||
((ERRORS++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check Kubernetes manifests
|
||||
log_info "Checking Kubernetes manifests..."
|
||||
K8S_FILES=(
|
||||
"infra/k8s/identity-service-entra-secrets.yaml"
|
||||
"infra/k8s/identity-service-deployment-entra.yaml"
|
||||
)
|
||||
|
||||
for file in "${K8S_FILES[@]}"; do
|
||||
if [ -f "${file}" ]; then
|
||||
log_success "Found: ${file}"
|
||||
else
|
||||
log_warning "Missing: ${file}"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check monitoring configs
|
||||
log_info "Checking monitoring configurations..."
|
||||
MONITORING_FILES=(
|
||||
"infra/monitoring/prometheus-entra-config.yml"
|
||||
"infra/monitoring/grafana-entra-dashboard.json"
|
||||
)
|
||||
|
||||
for file in "${MONITORING_FILES[@]}"; do
|
||||
if [ -f "${file}" ]; then
|
||||
log_success "Found: ${file}"
|
||||
else
|
||||
log_warning "Missing: ${file}"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Test API connectivity (if service is running)
|
||||
log_info "Testing API connectivity..."
|
||||
if curl -sf http://localhost:4002/health > /dev/null 2>&1; then
|
||||
log_success "Identity service is running"
|
||||
|
||||
# Test Entra endpoints
|
||||
if curl -sf http://localhost:4002/vc/issue/entra > /dev/null 2>&1; then
|
||||
log_success "Entra issuance endpoint accessible"
|
||||
else
|
||||
log_warning "Entra issuance endpoint not accessible (may require auth)"
|
||||
fi
|
||||
else
|
||||
log_warning "Identity service not running locally"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
log_info "Validation Summary:"
|
||||
if [ ${ERRORS} -eq 0 ]; then
|
||||
log_success "No errors found!"
|
||||
else
|
||||
log_error "${ERRORS} error(s) found"
|
||||
fi
|
||||
|
||||
if [ ${WARNINGS} -gt 0 ]; then
|
||||
log_warning "${WARNINGS} warning(s) found"
|
||||
fi
|
||||
|
||||
if [ ${ERRORS} -eq 0 ]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
197
scripts/validation/validate-seal-files.sh
Executable file
197
scripts/validation/validate-seal-files.sh
Executable file
@@ -0,0 +1,197 @@
|
||||
#!/bin/bash
|
||||
# Validate Order of St John seal files
|
||||
# Checks SVG structure, PNG quality, and file integrity
|
||||
|
||||
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}[VALIDATE]${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")/../.."
|
||||
|
||||
SVG_DIR="assets/credential-images/svg"
|
||||
PNG_DIR="assets/credential-images/png"
|
||||
|
||||
CHECKS_PASSED=0
|
||||
CHECKS_FAILED=0
|
||||
CHECKS_WARNING=0
|
||||
|
||||
check() {
|
||||
local name=$1
|
||||
local command=$2
|
||||
|
||||
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
|
||||
|
||||
if eval "${command}" > /dev/null 2>&1; then
|
||||
log_success "${name}"
|
||||
((CHECKS_PASSED++))
|
||||
return 0
|
||||
else
|
||||
log_warning "${name}"
|
||||
((CHECKS_WARNING++))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo ""
|
||||
log_info "=== Order of St John Seal Validation ==="
|
||||
echo ""
|
||||
|
||||
# Check directories exist
|
||||
log_info "1. Directory Structure"
|
||||
check "SVG directory exists" "[ -d '${SVG_DIR}' ]"
|
||||
check "PNG directory exists" "[ -d '${PNG_DIR}' ]"
|
||||
|
||||
# Check for required SVG files
|
||||
log_info "2. Required SVG Files"
|
||||
EXPECTED_SEALS=(
|
||||
"digital-bank-seal.svg"
|
||||
"iccc-seal.svg"
|
||||
"iccc-provost-marshals-seal.svg"
|
||||
"diplomatic-security-seal.svg"
|
||||
)
|
||||
|
||||
for seal in "${EXPECTED_SEALS[@]}"; do
|
||||
check "Required seal exists: ${seal}" "[ -f '${SVG_DIR}/${seal}' ]"
|
||||
done
|
||||
|
||||
# Validate SVG structure
|
||||
log_info "3. SVG File Structure"
|
||||
for svg_file in "${SVG_DIR}"/*.svg; do
|
||||
if [ -f "${svg_file}" ]; then
|
||||
filename=$(basename "${svg_file}")
|
||||
|
||||
# Check for Maltese Cross
|
||||
if grep -q "maltese-cross\|Maltese Cross\|maltese" "${svg_file}" -i; then
|
||||
log_success "${filename}: Contains Maltese Cross reference"
|
||||
((CHECKS_PASSED++))
|
||||
else
|
||||
log_warning "${filename}: Maltese Cross reference not found"
|
||||
((CHECKS_WARNING++))
|
||||
fi
|
||||
|
||||
# Check for OSJ reference
|
||||
if grep -q "OSJ\|ORDER OF ST JOHN\|ORDO S. IOANNIS" "${svg_file}"; then
|
||||
log_success "${filename}: Contains OSJ reference"
|
||||
((CHECKS_PASSED++))
|
||||
else
|
||||
log_warning "${filename}: OSJ reference not found"
|
||||
((CHECKS_WARNING++))
|
||||
fi
|
||||
|
||||
# Check SVG is valid XML
|
||||
if xmllint --noout "${svg_file}" 2>/dev/null; then
|
||||
log_success "${filename}: Valid SVG/XML"
|
||||
((CHECKS_PASSED++))
|
||||
else
|
||||
check_warning "${filename}: XML validation (xmllint not available)" "true"
|
||||
fi
|
||||
|
||||
# Check viewBox exists
|
||||
if grep -q 'viewBox="0 0' "${svg_file}"; then
|
||||
log_success "${filename}: Has viewBox"
|
||||
((CHECKS_PASSED++))
|
||||
else
|
||||
log_warning "${filename}: Missing viewBox"
|
||||
((CHECKS_WARNING++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Validate PNG files
|
||||
log_info "4. PNG File Validation"
|
||||
PNG_COUNT=0
|
||||
for png_file in "${PNG_DIR}"/*.png; do
|
||||
if [ -f "${png_file}" ]; then
|
||||
((PNG_COUNT++))
|
||||
filename=$(basename "${png_file}")
|
||||
|
||||
# Check if valid PNG
|
||||
if file "${png_file}" | grep -q "PNG"; then
|
||||
log_success "${filename}: Valid PNG"
|
||||
((CHECKS_PASSED++))
|
||||
|
||||
# Check file size (should be under 100KB for credentials)
|
||||
size_kb=$(du -k "${png_file}" | cut -f1)
|
||||
if [ "${size_kb}" -lt 100 ]; then
|
||||
log_success "${filename}: Size OK (${size_kb}KB)"
|
||||
((CHECKS_PASSED++))
|
||||
else
|
||||
log_warning "${filename}: Large size (${size_kb}KB, recommend <100KB)"
|
||||
((CHECKS_WARNING++))
|
||||
fi
|
||||
else
|
||||
log_error "${filename}: Invalid PNG"
|
||||
((CHECKS_FAILED++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${PNG_COUNT} -eq 0 ]; then
|
||||
log_warning "No PNG files found (run prepare-all-credential-seals.sh first)"
|
||||
((CHECKS_WARNING++))
|
||||
fi
|
||||
|
||||
# Check manifest templates reference seals
|
||||
log_info "5. Manifest Template References"
|
||||
MANIFEST_DIR="manifests/entra"
|
||||
if [ -d "${MANIFEST_DIR}" ]; then
|
||||
for manifest in "${MANIFEST_DIR}"/*-manifest-template.json; do
|
||||
if [ -f "${manifest}" ]; then
|
||||
filename=$(basename "${manifest}")
|
||||
if grep -q "cdn.theorder.org/images.*seal" "${manifest}"; then
|
||||
log_success "${filename}: References seal image"
|
||||
((CHECKS_PASSED++))
|
||||
else
|
||||
log_warning "${filename}: Seal image reference not found"
|
||||
((CHECKS_WARNING++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
log_info "=== Validation 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))
|
||||
if [ ${TOTAL} -gt 0 ]; then
|
||||
PERCENTAGE=$((CHECKS_PASSED * 100 / TOTAL))
|
||||
echo ""
|
||||
if [ ${CHECKS_FAILED} -eq 0 ]; then
|
||||
log_success "All critical checks passed! (${PERCENTAGE}%)"
|
||||
exit 0
|
||||
else
|
||||
log_error "Some checks failed (${PERCENTAGE}%)"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user