mirror of
https://github.com/TronoSfera/backupy-agent.git
synced 2026-05-19 02:23:30 +03:00
Source ports from the TronoSfera/backupy-cloud monorepo:
- apps/agent/ — Go agent (WSS client, persistent queue, Docker
discovery, 5 DB drivers: PG/MySQL/Mongo/Redis/SQLite,
pre/post hooks, Prometheus metrics)
- apps/backupy-decrypt/ — standalone CLI for client-side decryption
- packages/proto/ — protobuf wire format (generated .pb.go committed
so the repo builds without protoc)
- docs/ — agent spec + wire-protocol contract
Apache-2.0 license. Image published to ghcr.io/tronosfera/backupy-agent
on every v* tag via .github/workflows/release.yml (multi-arch amd64+arm64).
94 lines
2.8 KiB
Go
94 lines
2.8 KiB
Go
package jwt
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// mintToken builds a JWT the same way the server does. The signature is
|
|
// intentionally not verified by the CLI; we still produce one so the
|
|
// token's structure is valid.
|
|
func mintToken(t *testing.T, claims map[string]any) string {
|
|
t.Helper()
|
|
hdr := map[string]string{"alg": "HS256", "typ": "JWT"}
|
|
hdrJSON, err := json.Marshal(hdr)
|
|
require.NoError(t, err)
|
|
pld, err := json.Marshal(claims)
|
|
require.NoError(t, err)
|
|
enc := base64.RawURLEncoding
|
|
signingInput := enc.EncodeToString(hdrJSON) + "." + enc.EncodeToString(pld)
|
|
mac := hmac.New(sha256.New, []byte("any-secret"))
|
|
mac.Write([]byte(signingInput))
|
|
return signingInput + "." + enc.EncodeToString(mac.Sum(nil))
|
|
}
|
|
|
|
func validClaims() map[string]any {
|
|
return map[string]any{
|
|
"iss": "backupy-server",
|
|
"sub": "user-123",
|
|
"aud": "backupy-decrypt",
|
|
"iat": time.Now().Unix(),
|
|
"exp": time.Now().Add(time.Minute).Unix(),
|
|
"run_id": "run-1",
|
|
"company_id": "company-1",
|
|
"dek": base64.StdEncoding.EncodeToString(make([]byte, 32)),
|
|
"alg": "AES-256-GCM",
|
|
"format_version": 1,
|
|
"sha256": "abc",
|
|
}
|
|
}
|
|
|
|
func TestParseDecryption_Success(t *testing.T) {
|
|
tok := mintToken(t, validClaims())
|
|
c, err := ParseDecryption(tok, "backupy-server", "backupy-decrypt")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "backupy-server", c.Issuer)
|
|
require.Equal(t, "user-123", c.Subject)
|
|
require.Equal(t, "run-1", c.RunID)
|
|
require.Equal(t, "company-1", c.CompanyID)
|
|
require.Equal(t, "AES-256-GCM", c.Algorithm)
|
|
require.Equal(t, 1, c.FormatVersion)
|
|
}
|
|
|
|
func TestParseDecryption_Expired(t *testing.T) {
|
|
cl := validClaims()
|
|
cl["exp"] = time.Now().Add(-time.Minute).Unix()
|
|
tok := mintToken(t, cl)
|
|
_, err := ParseDecryption(tok, "backupy-server", "backupy-decrypt")
|
|
require.ErrorIs(t, err, ErrExpired)
|
|
}
|
|
|
|
func TestParseDecryption_WrongIssuer(t *testing.T) {
|
|
cl := validClaims()
|
|
cl["iss"] = "evil"
|
|
tok := mintToken(t, cl)
|
|
_, err := ParseDecryption(tok, "backupy-server", "backupy-decrypt")
|
|
require.ErrorIs(t, err, ErrWrongIssuer)
|
|
}
|
|
|
|
func TestParseDecryption_WrongAudience(t *testing.T) {
|
|
cl := validClaims()
|
|
cl["aud"] = "other"
|
|
tok := mintToken(t, cl)
|
|
_, err := ParseDecryption(tok, "backupy-server", "backupy-decrypt")
|
|
require.ErrorIs(t, err, ErrWrongAudience)
|
|
}
|
|
|
|
func TestParseDecryption_MissingDEK(t *testing.T) {
|
|
cl := validClaims()
|
|
delete(cl, "dek")
|
|
tok := mintToken(t, cl)
|
|
_, err := ParseDecryption(tok, "backupy-server", "backupy-decrypt")
|
|
require.ErrorIs(t, err, ErrMissingClaim)
|
|
}
|
|
|
|
func TestParseDecryption_Malformed(t *testing.T) {
|
|
_, err := ParseDecryption("not.a.jwt", "backupy-server", "backupy-decrypt")
|
|
require.ErrorIs(t, err, ErrMalformed)
|
|
}
|