Compare commits

..

2 Commits

Author SHA1 Message Date
3e5bf97f4c Merge pull request 'feat: initial version' (#1) from chore/initial-version into main
All checks were successful
Test Stalwart Installation Action / Error Handling Tests (push) Successful in 19s
Test Stalwart Installation Action / Full Configuration (Domains + Users) (push) Successful in 33s
Test Stalwart Installation Action / Basic Installation (No Config) (push) Successful in 49s
Test Stalwart Installation Action / Test Summary (push) Successful in 3s
Reviewed-on: #1
2026-02-15 14:24:12 +00:00
cc3fffbda1 feat: initial version
All checks were successful
Test Stalwart Installation Action / Error Handling Tests (pull_request) Successful in 23s
Test Stalwart Installation Action / Full Configuration (Domains + Users) (pull_request) Successful in 40s
Test Stalwart Installation Action / Basic Installation (No Config) (pull_request) Successful in 47s
Test Stalwart Installation Action / Test Summary (pull_request) Successful in 3s
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-02-15 09:21:37 -05:00
3 changed files with 141 additions and 35 deletions

View File

@@ -93,6 +93,7 @@ jobs:
sudo apt-get install -y jq curl
- name: Install with full configuration
id: install
uses: ./
with:
domains: |
@@ -162,26 +163,137 @@ jobs:
echo "::error::API not accessible after 60 seconds"
exit 1
- name: Verify domains and users were created
run: |
echo "=== Reading Admin Password ==="
if [ -f /tmp/stalwart_admin_password ]; then
ADMIN_PASSWORD=$(cat /tmp/stalwart_admin_password)
echo "✓ Admin password retrieved"
else
echo "::error::Admin password file not found"
exit 1
fi
echo ""
echo "=== Verifying Domains ==="
DOMAINS_RESPONSE=$(curl -s -u "admin:$ADMIN_PASSWORD" \
"http://localhost:8080/api/principal?types=domain&limit=100")
DOMAIN_COUNT=$(echo "$DOMAINS_RESPONSE" | jq '.data.total // 0')
echo "Total domains found: $DOMAIN_COUNT"
# List domains
echo "$DOMAINS_RESPONSE" | jq -r '.data.items[] | " - \(.name): \(.description // "No description")"'
# Verify specific domains exist
if echo "$DOMAINS_RESPONSE" | jq -e '.data.items[] | select(.name == "test1.local")' >/dev/null; then
echo "✓ Domain test1.local exists"
else
echo "::error::Domain test1.local not found"
exit 1
fi
if echo "$DOMAINS_RESPONSE" | jq -e '.data.items[] | select(.name == "test2.local")' >/dev/null; then
echo "✓ Domain test2.local exists"
else
echo "::error::Domain test2.local not found"
exit 1
fi
echo ""
echo "=== Verifying Users ==="
USERS_RESPONSE=$(curl -s -u "admin:$ADMIN_PASSWORD" \
"http://localhost:8080/api/principal?types=individual&limit=100")
USER_COUNT=$(echo "$USERS_RESPONSE" | jq '.data.total // 0')
echo "Total users found: $USER_COUNT"
# List users
echo "$USERS_RESPONSE" | jq -r '.data.items[] | " - \(.name) (\(.emails[0])): roles=\(.roles)"'
# Verify we have at least the 3 users we created
if [ "$USER_COUNT" -lt 3 ]; then
echo "::error::Expected at least 3 users, found $USER_COUNT"
exit 1
fi
# Verify specific users exist and have correct roles
for user_email in "user1@test1.local" "user2@test1.local" "admin@test2.local"; do
USER_DATA=$(echo "$USERS_RESPONSE" | jq --arg email "$user_email" '.data.items[] | select(.emails[] == $email)')
if [ -z "$USER_DATA" ]; then
echo "::error::User $user_email not found"
exit 1
fi
# Check if user has "user" role
HAS_USER_ROLE=$(echo "$USER_DATA" | jq '.roles | contains(["user"])')
if [ "$HAS_USER_ROLE" = "true" ]; then
echo "✓ User $user_email exists with 'user' role"
else
echo "::error::User $user_email exists but missing 'user' role"
exit 1
fi
done
echo ""
echo "✓ All domains and users verified successfully"
- name: Verify unauthenticated JMAP access
run: |
echo "Testing unauthenticated JMAP endpoint..."
# Call without authentication (follow redirects with -L)
HTTP_CODE=$(curl -s -L \
-o /tmp/jmap_response_no_auth.json \
-w "%{http_code}" \
"http://localhost:8080/.well-known/jmap")
echo "HTTP Status Code: $HTTP_CODE"
echo "JMAP Response:"
cat /tmp/jmap_response_no_auth.json
echo ""
# Check if request succeeded
if [ "$HTTP_CODE" != "200" ]; then
echo "::error::JMAP endpoint returned HTTP $HTTP_CODE for unauthenticated request"
cat /tmp/jmap_response_no_auth.json || true
exit 1
fi
# Verify username is empty (no authentication)
USERNAME=$(cat /tmp/jmap_response_no_auth.json | jq -r '.username // empty')
if [ -z "$USERNAME" ]; then
echo "✓ Unauthenticated access returns empty username"
else
echo "::warning::Expected empty username for unauthenticated request, got '$USERNAME'"
fi
- name: Verify created user can authenticate
run: |
echo "Testing user authentication via JMAP endpoint..."
# Test user1@test1.local authentication (show HTTP code for debugging)
HTTP_CODE=$(curl -s -o /tmp/jmap_response.json -w "%{http_code}" \
# Test user1@test1.local authentication (follow redirects with -L)
HTTP_CODE=$(curl -s -L \
-o /tmp/jmap_response_auth.json \
-w "%{http_code}" \
-u "user1@test1.local:UserPass123!" \
"http://localhost:8080/.well-known/jmap")
echo "HTTP Status Code: $HTTP_CODE"
echo "JMAP Response:"
cat /tmp/jmap_response.json | jq '.' || cat /tmp/jmap_response.json
cat /tmp/jmap_response_auth.json
echo ""
# Check if request succeeded
if [ "$HTTP_CODE" != "200" ]; then
echo "::error::JMAP endpoint returned HTTP $HTTP_CODE"
exit 1
fi
# Verify username field contains our test user
USERNAME=$(cat /tmp/jmap_response.json | jq -r '.username // empty')
USERNAME=$(cat /tmp/jmap_response_auth.json | jq -r '.username // empty')
if [ "$USERNAME" = "user1@test1.local" ]; then
echo "✓ User authentication successful: $USERNAME"
@@ -191,7 +303,7 @@ jobs:
fi
# Verify accounts object is not empty (means user is authenticated)
ACCOUNTS=$(cat /tmp/jmap_response.json | jq '.accounts // {}')
ACCOUNTS=$(cat /tmp/jmap_response_auth.json | jq '.accounts // {}')
if [ "$ACCOUNTS" != "{}" ]; then
echo "✓ User has active accounts"
else
@@ -199,22 +311,6 @@ jobs:
exit 1
fi
- name: Verify unauthenticated JMAP access
run: |
echo "Testing unauthenticated JMAP endpoint..."
# Call without authentication
JMAP_RESPONSE=$(curl -sf "http://localhost:8080/.well-known/jmap")
# Verify username is empty (no authentication)
USERNAME=$(echo "$JMAP_RESPONSE" | jq -r '.username // empty')
if [ -z "$USERNAME" ]; then
echo "✓ Unauthenticated access returns empty username"
else
echo "::warning::Expected empty username for unauthenticated request, got '$USERNAME'"
fi
- name: Show logs on failure
if: failure()
run: |

View File

@@ -80,9 +80,4 @@ runs:
run: |
HOSTNAME=$(hostname -f 2>/dev/null || echo "localhost")
echo "::notice::🎉 Stalwart Mail Server installation complete!"
echo "::notice::Web admin: http://$HOSTNAME:8080/login"
if [ -n "${{ inputs.admin_password }}" ]; then
echo "::notice::Admin credentials configured via inputs"
else
echo "::notice::Default admin password: changeme (change this immediately!)"
fi
echo "::notice::Web admin: http://$HOSTNAME:8080/login"

View File

@@ -56,6 +56,10 @@ main() {
log_info "📝 Generated admin password: ${current_password}"
log_warning "⚠️ Save this password securely - it won't be shown again!"
# Save admin password to temp file for testing/debugging (remove in production)
echo "$current_password" > /tmp/stalwart_admin_password
chmod 600 /tmp/stalwart_admin_password
# Create domains if provided
if [ -n "$DOMAINS_JSON" ]; then
log_info "Creating domains..."
@@ -253,14 +257,25 @@ create_users() {
continue
fi
# Get password (use plain password - API handles hashing)
local password
password=$(echo "$user" | jq -r '.password // empty' 2>/dev/null)
# Hash the password with SHA-512 (API requires hashed passwords in secrets array)
local hashed_password=""
local raw_password
raw_password=$(echo "$user" | jq -r '.password // empty' 2>/dev/null)
# Build API payload with required structure
if [ -n "$raw_password" ]; then
if command -v mkpasswd >/dev/null 2>&1; then
hashed_password=$(mkpasswd -m sha-512 "$raw_password")
elif command -v openssl >/dev/null 2>&1; then
hashed_password=$(openssl passwd -6 "$raw_password")
else
log_warning "Cannot hash password for $email - no mkpasswd or openssl found"
fi
fi
# Build API payload with required structure
local payload
if [ -n "$password" ]; then
payload=$(echo "$user" | jq --arg email "$email" --arg pwd "$password" '{
if [ -n "$hashed_password" ]; then
payload=$(echo "$user" | jq --arg email "$email" --arg pwd "$hashed_password" '{
type: "individual",
quota: (.quota // 0),
name: $email,
@@ -269,7 +284,7 @@ create_users() {
emails: [$email],
urls: [],
memberOf: [],
roles: [],
roles: ["user"],
lists: [],
members: [],
enabledPermissions: [],
@@ -286,7 +301,7 @@ create_users() {
emails: [$email],
urls: [],
memberOf: [],
roles: [],
roles: ["user"],
lists: [],
members: [],
enabledPermissions: [],