diff --git a/activitypub/shared/types/context.go b/activitypub/context.go similarity index 99% rename from activitypub/shared/types/context.go rename to activitypub/context.go index a91d659..f75906c 100644 --- a/activitypub/shared/types/context.go +++ b/activitypub/context.go @@ -1,4 +1,4 @@ -package types +package activitypub var BaseLdContext = []any{ "https://www.w3.org/ns/activitystreams", diff --git a/activitypub/import.go b/activitypub/import.go index a344c25..d91d665 100644 --- a/activitypub/import.go +++ b/activitypub/import.go @@ -4,13 +4,12 @@ import ( "encoding/json" "errors" "io" - "net/http" "time" + "git.mstar.dev/mstar/goutils/other" "git.mstar.dev/mstar/goutils/sliceutils" "github.com/rs/zerolog/log" - apshared "git.mstar.dev/mstar/linstrom/activitypub/shared" "git.mstar.dev/mstar/linstrom/config" "git.mstar.dev/mstar/linstrom/storage-new/dbgen" webshared "git.mstar.dev/mstar/linstrom/web/shared" @@ -47,18 +46,18 @@ func ImportRemoteAccount(targetName string) (string, error) { RestrictedFollow *bool `json:"manuallyApprovesFollowers"` } // Get the target user's link first - webfinger, err := apshared.GetAccountWebfinger(targetName) + webfinger, err := GetAccountWebfinger(targetName) if err != nil { - return "", err + return "", other.Error("activitypub", "webfinger request failed", err) } - selfLinks := sliceutils.Filter(webfinger.Links, func(t apshared.LinkData) bool { + selfLinks := sliceutils.Filter(webfinger.Links, func(t LinkData) bool { return t.Relation == "self" }) if len(selfLinks) == 0 { return "", errors.New("No self link") } APLink := selfLinks[0] - req, err := http.NewRequest("GET", *APLink.Href, nil) + req, err := webshared.NewRequest("GET", *APLink.Href, nil) if err != nil { return "", err } @@ -86,12 +85,16 @@ func ImportRemoteAccount(targetName string) (string, error) { return "", err } defer response.Body.Close() + body, _ := io.ReadAll(response.Body) + log.Debug(). + Int("status", response.StatusCode). + Bytes("body", body). + Any("headers", response.Header). + Msg("Response information") if response.StatusCode != 200 { return "", errors.New("Bad status") } var data InboundUser - body, _ := io.ReadAll(response.Body) - log.Info().Bytes("body", body).Msg("Body from request") err = json.Unmarshal(body, &data) if err != nil { return "", err diff --git a/activitypub/shared/util.go b/activitypub/util.go similarity index 95% rename from activitypub/shared/util.go rename to activitypub/util.go index 3ba0c6a..abee15f 100644 --- a/activitypub/shared/util.go +++ b/activitypub/util.go @@ -1,4 +1,4 @@ -package apshared +package activitypub import "strings" diff --git a/activitypub/shared/webfinger.go b/activitypub/webfinger.go similarity index 97% rename from activitypub/shared/webfinger.go rename to activitypub/webfinger.go index fe4d972..6530b21 100644 --- a/activitypub/shared/webfinger.go +++ b/activitypub/webfinger.go @@ -1,10 +1,9 @@ -package apshared +package activitypub import ( "encoding/json" "errors" "io" - "net/http" webshared "git.mstar.dev/mstar/linstrom/web/shared" ) @@ -44,7 +43,7 @@ func GetAccountWebfinger(fullHandle string) (*WebfingerData, error) { if err != nil { return nil, err } - webfingerRequest, err := http.NewRequest( + webfingerRequest, err := webshared.NewRequest( // Webfinger requests are GET "GET", // The webfinger url is located at /.well-known/webfinger diff --git a/config/config.go b/config/config.go index 0436ecb..bdcffc1 100644 --- a/config/config.go +++ b/config/config.go @@ -207,7 +207,7 @@ var defaultConfig Config = Config{ // Example: "git.mstar.dev" (with subdomain = "git" and domain = "mstar.dev") func (gc *ConfigGeneral) GetFullDomain() string { if gc.Subdomain != nil { - return *gc.Subdomain + gc.Domain + return *gc.Subdomain + "." + gc.Domain } return gc.Domain } diff --git a/storage-new/models/RemoteServerSoftwareType.go b/storage-new/models/RemoteServerSoftwareType.go index de63ca3..e22d906 100644 --- a/storage-new/models/RemoteServerSoftwareType.go +++ b/storage-new/models/RemoteServerSoftwareType.go @@ -18,6 +18,7 @@ const ( ServerSoftwareWafrn = ServerSoftwareType("Wafrn") // Linstrom with no known forks ServerSoftwareLinstrom = ServerSoftwareType("Linstrom") + ServerSoftwareUnknown = ServerSoftwareType("Unknown") ) // A list of all known server software systems @@ -27,6 +28,7 @@ var AllServerSoftwareTypes = []ServerSoftwareType{ ServerSoftwarePlemora, ServerSoftwareWafrn, ServerSoftwareLinstrom, + ServerSoftwareUnknown, } func (r *ServerSoftwareType) Value() (driver.Value, error) { diff --git a/storage/user.go b/storage/user.go index e919405..9453cc1 100644 --- a/storage/user.go +++ b/storage/user.go @@ -8,7 +8,7 @@ import ( "strings" "time" - ap "git.mstar.dev/mstar/linstrom/activitypub/shared" + "git.mstar.dev/mstar/linstrom/activitypub" "git.mstar.dev/mstar/linstrom/shared" "github.com/go-webauthn/webauthn/webauthn" "github.com/google/uuid" @@ -121,7 +121,7 @@ func (s *Storage) FindAccountByFullHandle(handle string) (*Account, error) { // Failed to find in cache, go the slow route of hitting the db log.Debug().Str("account-handle", handle).Msg("Didn't hit account in cache, going to db") - name, server, err := ap.SplitFullHandle(handle) + name, server, err := activitypub.SplitFullHandle(handle) if err != nil { log.Warn().Err(err).Str("account-handle", handle).Msg("Failed to split up account handle") return nil, err diff --git a/web/public/api/activitypub/user.go b/web/public/api/activitypub/user.go index aa72d8d..5e95059 100644 --- a/web/public/api/activitypub/user.go +++ b/web/public/api/activitypub/user.go @@ -12,7 +12,7 @@ import ( "git.mstar.dev/mstar/goutils/sliceutils" "github.com/rs/zerolog/hlog" - "git.mstar.dev/mstar/linstrom/activitypub/shared/types" + "git.mstar.dev/mstar/linstrom/activitypub" "git.mstar.dev/mstar/linstrom/config" "git.mstar.dev/mstar/linstrom/storage-new" "git.mstar.dev/mstar/linstrom/storage-new/dbgen" @@ -53,6 +53,7 @@ func users(w http.ResponseWriter, r *http.Request) { } log := hlog.FromRequest(r) userId := r.PathValue("id") + log.Debug().Any("headers", r.Header).Msg("request headers") user, err := dbgen.User.Where(dbgen.User.ID.Eq(userId)). Preload(dbgen.User.Icon).Preload(dbgen.User.Banner). Preload(dbgen.User.BeingTypes). @@ -75,7 +76,7 @@ func users(w http.ResponseWriter, r *http.Request) { keyBytes = keyBytesToPem(user.PublicKeyRsa) } data := Outbound{ - Context: types.BaseLdContext, + Context: activitypub.BaseLdContext, Id: apUrl, Type: "Person", PreferredUsername: user.Username, @@ -136,7 +137,12 @@ func userInbox(w http.ResponseWriter, r *http.Request) { log := hlog.FromRequest(r) userId := r.PathValue("id") data, err := io.ReadAll(r.Body) - log.Info().Err(err).Str("userId", userId).Bytes("body", data).Msg("Inbox message") + log.Info(). + Err(err). + Str("userId", userId). + Bytes("body", data). + Any("headers", r.Header). + Msg("Inbox message") } /* diff --git a/web/public/api/activitypub/util.go b/web/public/api/activitypub/util.go index 100da5e..631128a 100644 --- a/web/public/api/activitypub/util.go +++ b/web/public/api/activitypub/util.go @@ -9,7 +9,7 @@ import ( func userIdToApUrl(id string) string { return fmt.Sprintf( - "%s/api/ap/users/%s", + "%s/api/activitypub/user/%s", config.GlobalConfig.General.GetFullPublicUrl(), id, ) diff --git a/web/shared/client.go b/web/shared/client.go index bee544d..a6130c1 100644 --- a/web/shared/client.go +++ b/web/shared/client.go @@ -2,17 +2,20 @@ package webshared import ( "crypto/sha256" + "io" "net/http" + "strings" "time" "git.mstar.dev/mstar/goutils/maputils" + "github.com/rs/zerolog/log" "git.mstar.dev/mstar/linstrom/config" ) // No init needed, zero value is good -var RequestClient http.Client +var RequestClient = http.Client{} const xRandomHeader = "X-Auth-Random" @@ -36,35 +39,40 @@ func SignRequest(r *http.Request, keyId string, privateKeyBytes, postBody []byte host = hostString } else { host = config.GlobalConfig.General.GetFullDomain() - headers.Set("Date", host) + headers.Set("Host", host) } applyBodyHash(headers, postBody) - mappedHeaders := maputils.MapSameKeys(headers, func(k string, v []string) string { + mappedHeaders := maputils.MapNewKeys(headers, func(k string, v []string) (string, string) { if len(v) > 0 { - return v[0] + return strings.ToLower(k), v[0] } else { - return "" + return strings.ToLower(k), "" } }) var signedString string + var usedHeaders []string if config.GlobalConfig.Experimental.UseEd25519Keys { - tmp, err := CreateSignatureED(method, r.URL.RawPath, mappedHeaders, privateKeyBytes) + tmp, tmp2, err := CreateSignatureED(method, r.URL.Path, mappedHeaders, privateKeyBytes) if err != nil { return err } signedString = tmp + usedHeaders = tmp2 } else { - tmp, err := CreateSignatureRSA(method, r.URL.RawPath, mappedHeaders, privateKeyBytes) + tmp, tmp2, err := CreateSignatureRSA(method, r.URL.Path, mappedHeaders, privateKeyBytes) if err != nil { return err } signedString = tmp + usedHeaders = tmp2 } + log.Debug().Str("string-to-sign", signedString).Any("headers", mappedHeaders).Send() signature := CreateSignatureHeaderContent( keyId, signedString, - maputils.KeysFromMap(mappedHeaders)..., + usedHeaders..., ) + log.Debug().Str("signature-header", signature).Send() headers.Set("Signature", signature) return nil } @@ -77,3 +85,17 @@ func applyBodyHash(headers http.Header, body []byte) error { headers.Set("Digest", string(hash[:])) return nil } + +func NewRequest(method string, url string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, err + } + req.Header.Add( + "User-Agent", + "Linstrom v0.0.0-pre-alpha ("+config.GlobalConfig.General.GetFullDomain()+")", + ) + req.Header.Add("Date", time.Now().Format(time.RFC1123)) + req.Header.Add("Host", config.GlobalConfig.General.GetFullDomain()) + return req, nil +} diff --git a/web/shared/signing.go b/web/shared/signing.go index eb5a43a..533890d 100644 --- a/web/shared/signing.go +++ b/web/shared/signing.go @@ -4,6 +4,9 @@ import ( "encoding/base64" "strings" + "github.com/rs/zerolog/log" + + "git.mstar.dev/mstar/linstrom/config" "git.mstar.dev/mstar/linstrom/shared" ) @@ -15,10 +18,10 @@ func CreateSignatureRSA( target string, headers map[string]string, privateKeyBytes []byte, -) (string, error) { - message := genPreSignatureString(method, target, headers) +) (string, []string, error) { + message, usedHeaders := genPreSignatureString(method, target, headers) signed, err := shared.Sign(message, privateKeyBytes, true) - return base64.StdEncoding.EncodeToString(signed), err + return base64.StdEncoding.EncodeToString(signed), usedHeaders, err } // Generate the signed string of the headers, method and target @@ -29,24 +32,29 @@ func CreateSignatureED( target string, headers map[string]string, privateKeyBytes []byte, -) (string, error) { - message := genPreSignatureString(method, target, headers) +) (string, []string, error) { + message, usedHeaders := genPreSignatureString(method, target, headers) signed, err := shared.Sign(message, privateKeyBytes, false) if err != nil { - return "", err + return "", nil, err } - return base64.StdEncoding.EncodeToString(signed), nil + return base64.StdEncoding.EncodeToString(signed), usedHeaders, nil } -func genPreSignatureString(method, target string, headers map[string]string) string { +func genPreSignatureString(method, target string, headers map[string]string) (string, []string) { dataBuilder := strings.Builder{} - dataBuilder.WriteString("(request-target) ") + dataBuilder.WriteString("(request-target): ") dataBuilder.WriteString(strings.ToLower(method) + " ") dataBuilder.WriteString(target + "\n") + dataBuilder.WriteString("algorithm: rsa-sha256\n") + usedHeaders := []string{"(request-target)", "algorithm"} for k, v := range headers { dataBuilder.WriteString(k + ": " + v + "\n") + usedHeaders = append(usedHeaders, k) } - return dataBuilder.String() + tmp := dataBuilder.String() + log.Debug().Str("Raw signature string", tmp).Send() + return tmp, usedHeaders } // Generate the content of the "Signature" header based on @@ -55,6 +63,8 @@ func genPreSignatureString(method, target string, headers map[string]string) str func CreateSignatureHeaderContent(userId string, hash string, headerNames ...string) string { builder := strings.Builder{} builder.WriteString("keyId=\"") + builder.WriteString(config.GlobalConfig.General.GetFullPublicUrl()) + builder.WriteString("/api/activitypub/user/") builder.WriteString(userId) builder.WriteString("\",headers=\"") for i, header := range headerNames { @@ -63,7 +73,7 @@ func CreateSignatureHeaderContent(userId string, hash string, headerNames ...str builder.WriteRune(' ') } } - builder.WriteString("\",signature=\"") + builder.WriteString("\",algorithm=\"rsa-sha256\",signature=\"") builder.WriteString(hash) builder.WriteRune('"')