Rename cavage singing func, add import for server
All checks were successful
/ docker (push) Successful in 4m1s
All checks were successful
/ docker (push) Successful in 4m1s
This commit is contained in:
parent
5e13817563
commit
08f6de0bd7
39 changed files with 2035 additions and 364 deletions
|
@ -3,20 +3,34 @@ package activitypub
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.mstar.dev/mstar/goutils/other"
|
||||
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"git.mstar.dev/mstar/linstrom/config"
|
||||
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
|
||||
"git.mstar.dev/mstar/linstrom/storage-new/models"
|
||||
webshared "git.mstar.dev/mstar/linstrom/web/shared"
|
||||
)
|
||||
|
||||
var interestingMetadata = []string{
|
||||
"nodeDescription",
|
||||
"maintainer",
|
||||
"tosUrl",
|
||||
"nodeAdmins",
|
||||
"privacyPolicyUrl",
|
||||
"inquiryUrl",
|
||||
"impressumUrl",
|
||||
"donationUrl",
|
||||
"staffAccounts",
|
||||
}
|
||||
|
||||
func ImportRemoteAccount(targetName string) (string, error) {
|
||||
type InboundUserKey struct {
|
||||
Id string `json:"id"`
|
||||
|
@ -62,70 +76,363 @@ func ImportRemoteAccount(targetName string) (string, error) {
|
|||
// Server actor key for signing
|
||||
linstromActor, err := dbgen.User.Where(dbgen.User.Username.Eq("linstrom")).First()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", other.Error("activitypub", "failed to get server actor", err)
|
||||
}
|
||||
var response *http.Response
|
||||
// if config.GlobalConfig.Experimental.UseEd25519Keys {
|
||||
// response, err = webshared.RequestSignedCavage(
|
||||
// "GET",
|
||||
// *APLink.Href,
|
||||
// nil,
|
||||
// config.GlobalConfig.General.GetFullPublicUrl()+"/api/activitypub/user/"+linstromActor.ID+"#main-key",
|
||||
// linstromActor.PrivateKeyEd,
|
||||
// true,
|
||||
// )
|
||||
// } else {
|
||||
// response, err = webshared.RequestSignedCavage(
|
||||
// "GET",
|
||||
// *APLink.Href,
|
||||
// nil,
|
||||
// config.GlobalConfig.General.GetFullPublicUrl()+"/api/activitypub/user/"+linstromActor.ID+"#main-key",
|
||||
// linstromActor.PrivateKeyRsa,
|
||||
// false,
|
||||
// )
|
||||
// }
|
||||
response, err = customReq(*APLink.Href, linstromActor)
|
||||
response, err = webshared.RequestSignedCavage("GET", *APLink.Href, nil, linstromActor)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", other.Error("activitypub", "failed to complete cavage signed request", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
log.Debug().
|
||||
log.Trace().
|
||||
Int("status", response.StatusCode).
|
||||
Bytes("body", body).
|
||||
Any("headers", response.Header).
|
||||
Msg("Response information")
|
||||
if response.StatusCode != 200 {
|
||||
return "", errors.New("bad status")
|
||||
return "", fmt.Errorf("activitypub: invalid status code: %v", response.StatusCode)
|
||||
}
|
||||
var data InboundUser
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", other.Error("activitypub", "failed to unmarshal response", err)
|
||||
}
|
||||
log.Info().Any("received-data", data).Msg("Response data")
|
||||
log.Debug().Any("received-data", data).Msg("Response data")
|
||||
log.Info().Str("user", targetName).Msg("Import completed")
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func customReq(target string, linstromActor *models.User) (*http.Response, error) {
|
||||
req, err := webshared.NewRequest("GET", target, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Accept", "application/activity+json")
|
||||
func ImportRemoteServer(host string) (uint, error) {
|
||||
// Nodeinfo is not locked behind authentication, so don't do it
|
||||
|
||||
var keyBytes []byte
|
||||
if config.GlobalConfig.Experimental.UseEd25519Keys {
|
||||
keyBytes = linstromActor.PrivateKeyEd
|
||||
req, err := webshared.NewRequest("GET", "https://"+host+"/.well-known/nodeinfo", nil)
|
||||
if err != nil {
|
||||
return 0, other.Error("activitypub", "failed to create overview request", err)
|
||||
}
|
||||
response, err := webshared.RequestClient.Do(req)
|
||||
if err != nil {
|
||||
return 0, other.Error("activitypub", "overview request failed", err)
|
||||
}
|
||||
var nodeInfoOverview webshared.NodeInfoOverview
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
err = decoder.Decode(&nodeInfoOverview)
|
||||
if err != nil {
|
||||
return 0, other.Error("activitypub", "overview unmarshal failed", err)
|
||||
}
|
||||
relevantInfos := sliceutils.Filter(nodeInfoOverview.Links, func(t webshared.NodeInfoLink) bool {
|
||||
return strings.HasSuffix(t.Rel, "schema/2.0") || strings.HasSuffix(t.Rel, "schema/2.1")
|
||||
})
|
||||
var data webshared.NodeInfo2
|
||||
v21Slice := sliceutils.Filter(relevantInfos, func(t webshared.NodeInfoLink) bool {
|
||||
return strings.HasSuffix(t.Rel, "schema/2.1")
|
||||
})
|
||||
if len(v21Slice) > 0 {
|
||||
req, err = webshared.NewRequest("GET", v21Slice[0].Href, nil)
|
||||
if err != nil {
|
||||
return 0, other.Error("activitypub", "info 2.1 request creation failed", err)
|
||||
}
|
||||
} else {
|
||||
keyBytes = linstromActor.PrivateKeyRsa
|
||||
req, err = webshared.NewRequest("GET", relevantInfos[0].Href, nil)
|
||||
if err != nil {
|
||||
return 0, other.Error("activitypub", "info 2.0 request creation failed", err)
|
||||
}
|
||||
}
|
||||
res, err := webshared.RequestClient.Do(req)
|
||||
if err != nil {
|
||||
return 0, other.Error("activitypub", "info request failed to complete", err)
|
||||
}
|
||||
decoder = json.NewDecoder(res.Body)
|
||||
err = decoder.Decode(&data)
|
||||
if err != nil {
|
||||
return 0, other.Error("activitypub", "failed to unmarshal info", err)
|
||||
}
|
||||
log.Debug().Any("nodeinfo", data).Msg("Server info received")
|
||||
rs := dbgen.RemoteServer
|
||||
// rsm := dbgen.RemoteServerMetadata
|
||||
serverModelType := webshared.MapNodeServerTypeToModelType(data.Software.Name)
|
||||
existingEntry, err := rs.Where(rs.Domain.Eq(host)).Preload(rs.Metadata).First()
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
existingEntry = &models.RemoteServer{
|
||||
ServerType: serverModelType,
|
||||
Version: data.Version,
|
||||
Domain: host,
|
||||
SpecificType: data.Software.Name,
|
||||
}
|
||||
if err = rs.Create(existingEntry); err != nil {
|
||||
return 0, other.Error("activitypub", "failed to create new server entry", err)
|
||||
}
|
||||
case nil:
|
||||
default:
|
||||
return 0, other.Error("activitypub", "db failure", err)
|
||||
}
|
||||
existingEntry.SpecificType = data.Software.Name
|
||||
existingEntry.IsSelf = false
|
||||
existingEntry.ServerType = serverModelType
|
||||
existingEntry.Version = data.Version
|
||||
|
||||
if name, ok := data.Metadata["nodeName"].(string); ok {
|
||||
existingEntry.Name = name
|
||||
} else {
|
||||
existingEntry.Name = host
|
||||
}
|
||||
// Cast without check for existence is ok here
|
||||
// Default value for `Any` is `nil`, which fails to cast to a valid string
|
||||
if description, ok := data.Metadata["nodeDescription"].(string); ok {
|
||||
log.Debug().Msg("Description found")
|
||||
targets := sliceutils.Filter(
|
||||
existingEntry.Metadata,
|
||||
func(t models.RemoteServerMetadata) bool {
|
||||
return t.Key == "description"
|
||||
},
|
||||
)
|
||||
if len(targets) > 0 {
|
||||
targets[0].Value = description
|
||||
} else {
|
||||
err := rs.Metadata.Model(existingEntry).Append(&models.RemoteServerMetadata{
|
||||
Value: description,
|
||||
Key: "description",
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Uint("server-id", existingEntry.ID).
|
||||
Str("key", "description").
|
||||
Str("value", description).
|
||||
Msg("Failed to append new metadata to server")
|
||||
}
|
||||
}
|
||||
}
|
||||
if maintainer, ok := data.Metadata["maintainer"].(map[string]any); ok {
|
||||
log.Debug().Msg("Maintainer found")
|
||||
name, nameOk := maintainer["name"].(string)
|
||||
email, emailOk := maintainer["email"].(string)
|
||||
if nameOk && emailOk {
|
||||
targets := sliceutils.Filter(
|
||||
existingEntry.Metadata,
|
||||
func(t models.RemoteServerMetadata) bool {
|
||||
return t.Key == "maintainer"
|
||||
},
|
||||
)
|
||||
val := fmt.Sprintf("%s <%s>", name, email)
|
||||
if len(targets) > 0 {
|
||||
targets[0].Value = val
|
||||
} else {
|
||||
err = rs.Metadata.Model(existingEntry).Append(&models.RemoteServerMetadata{
|
||||
Key: "maintainer",
|
||||
Value: val,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Uint("server-id", existingEntry.ID).
|
||||
Str("key", "maintainer").
|
||||
Str("value", val).
|
||||
Msg("Failed to append new metadata to server")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if tosUrl, ok := data.Metadata["tosUrl"].(string); ok {
|
||||
log.Debug().Msg("Tos url found")
|
||||
targets := sliceutils.Filter(
|
||||
existingEntry.Metadata,
|
||||
func(t models.RemoteServerMetadata) bool {
|
||||
return t.Key == "tosUrl"
|
||||
},
|
||||
)
|
||||
if len(targets) > 0 {
|
||||
targets[0].Value = tosUrl
|
||||
} else {
|
||||
err := rs.Metadata.Model(existingEntry).Append(&models.RemoteServerMetadata{
|
||||
Value: tosUrl,
|
||||
Key: "tosUrl",
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Uint("server-id", existingEntry.ID).
|
||||
Str("key", "tosUrl").
|
||||
Str("value", tosUrl).
|
||||
Msg("Failed to append new metadata to server")
|
||||
}
|
||||
}
|
||||
}
|
||||
if nodeAdmins, ok := data.Metadata["nodeAdmins"].([]map[string]any); ok {
|
||||
log.Debug().Msg("Node admins url found")
|
||||
targets := sliceutils.Filter(
|
||||
existingEntry.Metadata,
|
||||
func(t models.RemoteServerMetadata) bool {
|
||||
return t.Key == "nodeAdmins"
|
||||
},
|
||||
)
|
||||
valueBuilder := strings.Builder{}
|
||||
for _, v := range nodeAdmins {
|
||||
name, nameOk := v["name"].(string)
|
||||
email, emailOk := v["email"].(string)
|
||||
if nameOk && emailOk {
|
||||
valueBuilder.WriteString(name)
|
||||
valueBuilder.WriteString(" <")
|
||||
valueBuilder.WriteString(email)
|
||||
valueBuilder.WriteString(">;")
|
||||
}
|
||||
}
|
||||
if len(targets) > 0 {
|
||||
targets[0].Value = valueBuilder.String()
|
||||
} else {
|
||||
err := rs.Metadata.Model(existingEntry).Append(&models.RemoteServerMetadata{
|
||||
Value: valueBuilder.String(),
|
||||
Key: "nodeAdmins",
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Uint("server-id", existingEntry.ID).
|
||||
Str("key", "nodeAdmins").
|
||||
Str("value", valueBuilder.String()).
|
||||
Msg("Failed to append new metadata to server")
|
||||
}
|
||||
}
|
||||
}
|
||||
if privacyPolicyUrl, ok := data.Metadata["privacyPolicyUrl"].(string); ok {
|
||||
log.Debug().Msg("Privacy policy url found")
|
||||
targets := sliceutils.Filter(
|
||||
existingEntry.Metadata,
|
||||
func(t models.RemoteServerMetadata) bool {
|
||||
return t.Key == "privacyPolicyUrl"
|
||||
},
|
||||
)
|
||||
if len(targets) > 0 {
|
||||
targets[0].Value = privacyPolicyUrl
|
||||
} else {
|
||||
err := rs.Metadata.Model(existingEntry).Append(&models.RemoteServerMetadata{
|
||||
Value: privacyPolicyUrl,
|
||||
Key: "privacyPolicyUrl",
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Uint("server-id", existingEntry.ID).
|
||||
Str("key", "privacyPolicyUrl").
|
||||
Str("value", privacyPolicyUrl).
|
||||
Msg("Failed to append new metadata to server")
|
||||
}
|
||||
}
|
||||
}
|
||||
if inquiryUrl, ok := data.Metadata["inquiryUrl"].(string); ok {
|
||||
log.Debug().Msg("Inquiry found")
|
||||
targets := sliceutils.Filter(
|
||||
existingEntry.Metadata,
|
||||
func(t models.RemoteServerMetadata) bool {
|
||||
return t.Key == "inquiryUrl"
|
||||
},
|
||||
)
|
||||
if len(targets) > 0 {
|
||||
targets[0].Value = inquiryUrl
|
||||
} else {
|
||||
err := rs.Metadata.Model(existingEntry).Append(&models.RemoteServerMetadata{
|
||||
Value: inquiryUrl,
|
||||
Key: "inquiryUrl",
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Uint("server-id", existingEntry.ID).
|
||||
Str("key", "inquiryUrl").
|
||||
Str("value", inquiryUrl).
|
||||
Msg("Failed to append new metadata to server")
|
||||
}
|
||||
}
|
||||
}
|
||||
if impressumUrl, ok := data.Metadata["impressumUrl"].(string); ok {
|
||||
log.Debug().Msg("Impressum url found")
|
||||
targets := sliceutils.Filter(
|
||||
existingEntry.Metadata,
|
||||
func(t models.RemoteServerMetadata) bool {
|
||||
return t.Key == "impressumUrl"
|
||||
},
|
||||
)
|
||||
if len(targets) > 0 {
|
||||
targets[0].Value = impressumUrl
|
||||
} else {
|
||||
err := rs.Metadata.Model(existingEntry).Append(&models.RemoteServerMetadata{
|
||||
Value: impressumUrl,
|
||||
Key: "impressumUrl",
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Uint("server-id", existingEntry.ID).
|
||||
Str("key", "impressumUrl").
|
||||
Str("value", impressumUrl).
|
||||
Msg("Failed to append new metadata to server")
|
||||
}
|
||||
}
|
||||
}
|
||||
if donationUrl, ok := data.Metadata["donationUrl"].(string); ok {
|
||||
log.Debug().Msg("Donation url found")
|
||||
targets := sliceutils.Filter(
|
||||
existingEntry.Metadata,
|
||||
func(t models.RemoteServerMetadata) bool {
|
||||
return t.Key == "donationUrl"
|
||||
},
|
||||
)
|
||||
if len(targets) > 0 {
|
||||
targets[0].Value = donationUrl
|
||||
} else {
|
||||
err := rs.Metadata.Model(existingEntry).Append(&models.RemoteServerMetadata{
|
||||
Value: donationUrl,
|
||||
Key: "donationUrl",
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Uint("server-id", existingEntry.ID).
|
||||
Str("key", "donationUrl").
|
||||
Str("value", donationUrl).
|
||||
Msg("Failed to append new metadata to server")
|
||||
}
|
||||
}
|
||||
}
|
||||
if staffAccounts, ok := data.Metadata["nodeAdmins"].([]any); ok {
|
||||
log.Debug().Msg("Node admins url found")
|
||||
targets := sliceutils.Filter(
|
||||
existingEntry.Metadata,
|
||||
func(t models.RemoteServerMetadata) bool {
|
||||
return t.Key == "staffAccounts"
|
||||
},
|
||||
)
|
||||
valueBuilder := strings.Builder{}
|
||||
for _, v := range staffAccounts {
|
||||
if acc, ok := v.(string); ok {
|
||||
valueBuilder.WriteString(acc)
|
||||
valueBuilder.WriteString(";")
|
||||
}
|
||||
}
|
||||
if len(targets) > 0 {
|
||||
targets[0].Value = valueBuilder.String()
|
||||
} else {
|
||||
err := rs.Metadata.Model(existingEntry).Append(&models.RemoteServerMetadata{
|
||||
Value: valueBuilder.String(),
|
||||
Key: "staffAccounts",
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Uint("server-id", existingEntry.ID).
|
||||
Str("key", "staffAccounts").
|
||||
Str("value", valueBuilder.String()).
|
||||
Msg("Failed to append new metadata to server")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sign and send
|
||||
err = webshared.SignRequest(req, linstromActor.ID+"#main-key", keyBytes, nil)
|
||||
// err = webshared.SignRequestWithHttpsig(req, linstromActor.ID+"#main-key", keyBytes, nil)
|
||||
id := existingEntry.ID
|
||||
existingEntry.ID = 0
|
||||
_, err = rs.Where(rs.ID.Eq(id)).UpdateColumns(existingEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, other.Error("activitypub", "failed to store update in db", err)
|
||||
}
|
||||
return webshared.RequestClient.Do(req)
|
||||
return id, nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue