mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 14:27:27 +00:00
* feat(files): provision-user CLI + anonymous public HTTP serving Lets external services (the TronBrowser extension store) host files on files.profullstack.com without the interactive `ssh join@` onboarding. - `agentbbs provision-user --name <h> --pubkey "<ssh key>"`: registers a member from an SSH *public* key (account = handle + key fingerprint). Reuses SanitizeUsername (same rules as join@) + EnsureUser; Files/SFTP access is free for members, so the account can immediately `scp … files@host:/public/extensions/<slug>/`. JSON output; refuses on key/ handle collision. New auth.FingerprintAuthorizedKey() parses an authorized_keys line to the same SHA256 fp as a live session key (tested). - setup.sh: the files.<host> Caddy site now serves the shared /public area as unauthenticated, read-only static files (handle_path /public/*), so .crx/.zip download links work for anyone — mapping 1:1 to the SFTP path. Non-/public paths still hit the auth'd web file manager. - docs/files.md updated. Note: not compiled here — repo go.mod requires go 1.26 and this sandbox has 1.22.2; changes pass gofmt parse/format checks. Reuses existing store/auth APIs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(vet): redundant newline in wish.Println premium-flow messages `go test ./...` / `go vet ./...` fail on `wish.Println(… "…\n")` — Println already appends a newline. Pre-existing on main (its CI is red for the same two lines); surfaced here. Switched both to `wish.Print` with an explicit trailing "\n\n" so output bytes are unchanged and vet is satisfied. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
50 lines
1.2 KiB
Go
50 lines
1.2 KiB
Go
package auth
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"strings"
|
|
"testing"
|
|
|
|
gossh "golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
func TestFingerprintAuthorizedKey(t *testing.T) {
|
|
pub, _, err := ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sshPub, err := gossh.NewPublicKey(pub)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
authLine := string(gossh.MarshalAuthorizedKey(sshPub)) // "ssh-ed25519 AAAA…\n"
|
|
want := gossh.FingerprintSHA256(sshPub)
|
|
|
|
// Bare line.
|
|
got, err := FingerprintAuthorizedKey(authLine)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != want {
|
|
t.Fatalf("fingerprint = %q, want %q", got, want)
|
|
}
|
|
|
|
// With a trailing comment + surrounding whitespace.
|
|
got2, err := FingerprintAuthorizedKey(" " + strings.TrimRight(authLine, "\n") + " acme@dev ")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error with comment: %v", err)
|
|
}
|
|
if got2 != want {
|
|
t.Fatalf("fingerprint with comment = %q, want %q", got2, want)
|
|
}
|
|
}
|
|
|
|
func TestFingerprintAuthorizedKeyRejectsGarbage(t *testing.T) {
|
|
if _, err := FingerprintAuthorizedKey("not a key"); err == nil {
|
|
t.Fatal("expected error for non-key input")
|
|
}
|
|
if _, err := FingerprintAuthorizedKey(""); err == nil {
|
|
t.Fatal("expected error for empty input")
|
|
}
|
|
}
|