More work on getting auth fetch verification working
This commit is contained in:
parent
7eac1db475
commit
9957ba8302
12 changed files with 434 additions and 205 deletions
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -52,7 +53,7 @@ type inboundImportUser struct {
|
|||
RestrictedFollow *bool `json:"manuallyApprovesFollowers"`
|
||||
}
|
||||
|
||||
func ImportRemoteAccount(targetName string) (string, error) {
|
||||
func ImportRemoteAccountByHandle(targetName string) (string, error) {
|
||||
// Get the target user's link first
|
||||
webfinger, err := GetAccountWebfinger(targetName)
|
||||
if err != nil {
|
||||
|
@ -65,177 +66,11 @@ func ImportRemoteAccount(targetName string) (string, error) {
|
|||
return "", errors.New("no self link")
|
||||
}
|
||||
APLink := selfLinks[0]
|
||||
// Server actor key for signing
|
||||
linstromActor, err := dbgen.User.Where(dbgen.User.Username.Eq(shared.ServerActorName)).First()
|
||||
acc, err := ImportRemoteAccountByAPUrl(*APLink.Href)
|
||||
if err != nil {
|
||||
return "", other.Error("activitypub", "failed to get server actor", err)
|
||||
return "", err
|
||||
}
|
||||
var response *http.Response
|
||||
response, err = webshared.RequestSignedCavage("GET", *APLink.Href, nil, linstromActor)
|
||||
if err != nil {
|
||||
return "", other.Error("activitypub", "failed to complete cavage signed request", 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 "", fmt.Errorf("activitypub: invalid status code: %v", response.StatusCode)
|
||||
}
|
||||
var data inboundImportUser
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return "", other.Error("activitypub", "failed to unmarshal response", err)
|
||||
}
|
||||
log.Debug().Any("received-data", data).Msg("Response data")
|
||||
|
||||
// TODO: Store received user in db
|
||||
_, host, _ := SplitFullHandle(targetName)
|
||||
hostId, err := ImportRemoteServer(host)
|
||||
if err != nil {
|
||||
return "", other.Error("activitypub", "failed to import host of target user", err)
|
||||
}
|
||||
|
||||
user, err := dbgen.User.
|
||||
Where(dbgen.User.Username.Eq(targetName)).
|
||||
Where(dbgen.User.ServerId.Eq(hostId)).
|
||||
Preload(dbgen.User.RemoteInfo).
|
||||
Preload(dbgen.User.InfoFields).
|
||||
Preload(dbgen.User.BeingTypes).
|
||||
Preload(dbgen.User.Roles).
|
||||
FirstOrCreate()
|
||||
if err != nil {
|
||||
return "", other.Error("activitypub", "failed to find or create user in db", err)
|
||||
}
|
||||
user.Verified = true
|
||||
user.FinishedRegistration = true
|
||||
|
||||
if !sliceutils.ContainsFunc(user.Roles, func(t models.UserToRole) bool {
|
||||
return t.Role.ID == models.DefaultUserRole.ID
|
||||
}) {
|
||||
roleMapping := models.UserToRole{
|
||||
Role: models.DefaultUserRole,
|
||||
RoleId: models.DefaultUserRole.ID,
|
||||
UserId: user.ID,
|
||||
}
|
||||
if err = dbgen.UserToRole.Create(&roleMapping); err != nil {
|
||||
return "", other.Error(
|
||||
"activitypub",
|
||||
"failed to store default user role to imported account mapping",
|
||||
err,
|
||||
)
|
||||
}
|
||||
if err = dbgen.User.Roles.Model(user).Append(&roleMapping); err != nil {
|
||||
return "", other.Error("activitypub", "failed to attach default role to user", err)
|
||||
}
|
||||
}
|
||||
if user.RemoteInfo == nil {
|
||||
user.RemoteInfo = &models.UserRemoteLinks{
|
||||
UserId: user.ID,
|
||||
ApLink: data.Id,
|
||||
}
|
||||
err = dbgen.UserRemoteLinks.Create(user.RemoteInfo)
|
||||
if err != nil {
|
||||
return "", other.Error("activitypub", "failed to create remote data for user", err)
|
||||
}
|
||||
err = dbgen.User.RemoteInfo.Model(user).Replace(user.RemoteInfo)
|
||||
if err != nil {
|
||||
return "", other.Error("activitypub", "failed to connect remote data to user", err)
|
||||
}
|
||||
user.RemoteInfoId.Int64 = int64(user.RemoteInfo.ID)
|
||||
user.RemoteInfoId.Valid = true
|
||||
}
|
||||
if data.DisplayName != nil {
|
||||
user.DisplayName = *data.DisplayName
|
||||
}
|
||||
if data.Outbox != nil {
|
||||
user.RemoteInfo.OutboxLink.String = *data.Outbox
|
||||
user.RemoteInfo.OutboxLink.Valid = true
|
||||
}
|
||||
user.RemoteInfo.InboxLink = data.Inbox
|
||||
if data.PublicKey != nil {
|
||||
pemBlock, _ := pem.Decode([]byte(data.PublicKey.Pem))
|
||||
if pemBlock.Type != "PUBLIC KEY" && pemBlock.Type != "RSA PUBLIC KEY" {
|
||||
return "", fmt.Errorf("activitypub: invalid public key block type: %v", pemBlock.Type)
|
||||
}
|
||||
user.PublicKeyRsa = pemBlock.Bytes
|
||||
}
|
||||
// Assume published day of user won't change
|
||||
if data.Description != nil {
|
||||
user.Description = *data.Description
|
||||
}
|
||||
if data.PublicUrl != nil {
|
||||
user.RemoteInfo.ViewLink.String = *data.PublicUrl
|
||||
user.RemoteInfo.ViewLink.Valid = true
|
||||
}
|
||||
if data.Discoverable != nil {
|
||||
user.Indexable = *data.Discoverable
|
||||
} else {
|
||||
// Assume false per default
|
||||
user.Indexable = false
|
||||
}
|
||||
if data.Location != nil {
|
||||
user.Location.String = *data.Location
|
||||
user.Location.Valid = true
|
||||
}
|
||||
if data.Birthday != nil {
|
||||
user.Birthday.String = *data.Birthday
|
||||
user.Birthday.Valid = true
|
||||
}
|
||||
if data.RestrictedFollow != nil {
|
||||
user.RestrictedFollow = *data.RestrictedFollow
|
||||
} else {
|
||||
// Assume not restricted if not included in received data
|
||||
user.RestrictedFollow = false
|
||||
}
|
||||
if data.IsCat {
|
||||
if !sliceutils.ContainsFunc(user.BeingTypes, func(t models.UserToBeing) bool {
|
||||
return t.Being == string(models.BEING_CAT)
|
||||
}) {
|
||||
log.Debug().Msg("user doesn't contain cat yet")
|
||||
bt := models.UserToBeing{UserId: user.ID, Being: string(models.BEING_CAT)}
|
||||
if err = dbgen.UserToBeing.Create(&bt); err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to append cat being type to imported user")
|
||||
}
|
||||
if err = dbgen.User.BeingTypes.Model(user).Append(&bt); err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Msg("Failed to append cat being type to imported user relations")
|
||||
}
|
||||
user.BeingTypes = append(user.BeingTypes, bt)
|
||||
}
|
||||
} else {
|
||||
if sliceutils.ContainsFunc(user.BeingTypes, func(t models.UserToBeing) bool {
|
||||
return t.Being == string(models.BEING_CAT)
|
||||
}) {
|
||||
_, err = dbgen.UserToBeing.Where(dbgen.UserToBeing.UserId.Eq(user.ID)).Where(dbgen.UserToBeing.Being.Eq(string(models.BEING_CAT))).Delete()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to remove cat being type from user")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't handle SpeakAsCat yet, as not included in stored data yet
|
||||
|
||||
// Handle media last as more complicated
|
||||
// Icon *inboundImportUserMedia `json:"icon,omitempty"`
|
||||
// Banner *inboundImportUserMedia `json:"image,omitempty"`
|
||||
|
||||
id := user.ID
|
||||
user.ID = ""
|
||||
if _, err = dbgen.User.Where(dbgen.User.ID.Eq(id)).UpdateColumns(user); err != nil {
|
||||
return "", other.Error("activitypub", "failed to update imported user", err)
|
||||
}
|
||||
rlid := user.RemoteInfo.ID
|
||||
user.RemoteInfo.ID = 0
|
||||
if _, err = dbgen.UserRemoteLinks.Where(dbgen.UserRemoteLinks.ID.Eq(rlid)).UpdateColumns(user.RemoteInfo); err != nil {
|
||||
return "", other.Error("activitypub", "failed to update imported user's remote links", err)
|
||||
}
|
||||
|
||||
log.Info().Str("user", targetName).Msg("Import completed")
|
||||
return id, nil
|
||||
return acc.ID, nil
|
||||
}
|
||||
|
||||
func ImportRemoteServer(host string) (uint, error) {
|
||||
|
@ -282,7 +117,7 @@ func ImportRemoteServer(host string) (uint, error) {
|
|||
if err != nil {
|
||||
return 0, other.Error("activitypub", "failed to unmarshal info", err)
|
||||
}
|
||||
log.Debug().Any("nodeinfo", data).Msg("Server info received")
|
||||
// log.Debug().Any("nodeinfo", data).Msg("Server info received")
|
||||
rs := dbgen.RemoteServer
|
||||
// rsm := dbgen.RemoteServerMetadata
|
||||
serverModelType := webshared.MapNodeServerTypeToModelType(data.Software.Name)
|
||||
|
@ -571,3 +406,181 @@ func ImportRemoteServer(host string) (uint, error) {
|
|||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func ImportRemoteAccountByAPUrl(apUrl string) (*models.User, error) {
|
||||
log.Info().Str("ap-url", apUrl).Msg("Importing account by ap url")
|
||||
// Server actor key for signing
|
||||
linstromActor, err := dbgen.User.Where(dbgen.User.Username.Eq(shared.ServerActorName)).First()
|
||||
if err != nil {
|
||||
return nil, other.Error("activitypub", "failed to get server actor", err)
|
||||
}
|
||||
var response *http.Response
|
||||
response, err = webshared.RequestSignedCavage("GET", apUrl, nil, linstromActor)
|
||||
if err != nil {
|
||||
return nil, other.Error("activitypub", "failed to complete cavage signed request", 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 nil, fmt.Errorf("activitypub: invalid status code: %v", response.StatusCode)
|
||||
}
|
||||
var data inboundImportUser
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, other.Error("activitypub", "failed to unmarshal response", err)
|
||||
}
|
||||
// log.Debug().Any("received-data", data).Msg("Response data")
|
||||
|
||||
targetUrl, err := url.Parse(apUrl)
|
||||
if err != nil {
|
||||
return nil, other.Error("activitypub", "failed to parse url as url", err)
|
||||
}
|
||||
hostId, err := ImportRemoteServer(targetUrl.Host)
|
||||
if err != nil {
|
||||
return nil, other.Error("activitypub", "failed to import host of target user", err)
|
||||
}
|
||||
|
||||
user, err := dbgen.User.
|
||||
Where(dbgen.User.Username.Eq(data.PreferredUsername + "@" + targetUrl.Host)).
|
||||
Where(dbgen.User.ServerId.Eq(hostId)).
|
||||
Preload(dbgen.User.RemoteInfo).
|
||||
Preload(dbgen.User.InfoFields).
|
||||
Preload(dbgen.User.BeingTypes).
|
||||
Preload(dbgen.User.Roles).
|
||||
FirstOrCreate()
|
||||
if err != nil {
|
||||
return nil, other.Error("activitypub", "failed to find or create user in db", err)
|
||||
}
|
||||
user.Verified = true
|
||||
user.FinishedRegistration = true
|
||||
|
||||
if !sliceutils.ContainsFunc(user.Roles, func(t models.UserToRole) bool {
|
||||
return t.Role.ID == models.DefaultUserRole.ID
|
||||
}) {
|
||||
roleMapping := models.UserToRole{
|
||||
Role: models.DefaultUserRole,
|
||||
RoleId: models.DefaultUserRole.ID,
|
||||
UserId: user.ID,
|
||||
}
|
||||
if err = dbgen.UserToRole.Create(&roleMapping); err != nil {
|
||||
return nil, other.Error(
|
||||
"activitypub",
|
||||
"failed to store default user role to imported account mapping",
|
||||
err,
|
||||
)
|
||||
}
|
||||
if err = dbgen.User.Roles.Model(user).Append(&roleMapping); err != nil {
|
||||
return nil, other.Error("activitypub", "failed to attach default role to user", err)
|
||||
}
|
||||
}
|
||||
if user.RemoteInfo == nil {
|
||||
user.RemoteInfo = &models.UserRemoteLinks{
|
||||
UserId: user.ID,
|
||||
ApLink: data.Id,
|
||||
}
|
||||
err = dbgen.UserRemoteLinks.Create(user.RemoteInfo)
|
||||
if err != nil {
|
||||
return nil, other.Error("activitypub", "failed to create remote data for user", err)
|
||||
}
|
||||
err = dbgen.User.RemoteInfo.Model(user).Replace(user.RemoteInfo)
|
||||
if err != nil {
|
||||
return nil, other.Error("activitypub", "failed to connect remote data to user", err)
|
||||
}
|
||||
user.RemoteInfoId.Int64 = int64(user.RemoteInfo.ID)
|
||||
user.RemoteInfoId.Valid = true
|
||||
}
|
||||
if data.DisplayName != nil {
|
||||
user.DisplayName = *data.DisplayName
|
||||
}
|
||||
if data.Outbox != nil {
|
||||
user.RemoteInfo.OutboxLink.String = *data.Outbox
|
||||
user.RemoteInfo.OutboxLink.Valid = true
|
||||
}
|
||||
user.RemoteInfo.InboxLink = data.Inbox
|
||||
if data.PublicKey != nil {
|
||||
pemBlock, _ := pem.Decode([]byte(data.PublicKey.Pem))
|
||||
if pemBlock.Type != "PUBLIC KEY" && pemBlock.Type != "RSA PUBLIC KEY" {
|
||||
return nil, fmt.Errorf("activitypub: invalid public key block type: %v", pemBlock.Type)
|
||||
}
|
||||
user.PublicKeyRsa = pemBlock.Bytes
|
||||
}
|
||||
// Assume published day of user won't change
|
||||
if data.Description != nil {
|
||||
user.Description = *data.Description
|
||||
}
|
||||
if data.PublicUrl != nil {
|
||||
user.RemoteInfo.ViewLink.String = *data.PublicUrl
|
||||
user.RemoteInfo.ViewLink.Valid = true
|
||||
}
|
||||
if data.Discoverable != nil {
|
||||
user.Indexable = *data.Discoverable
|
||||
} else {
|
||||
// Assume false per default
|
||||
user.Indexable = false
|
||||
}
|
||||
if data.Location != nil {
|
||||
user.Location.String = *data.Location
|
||||
user.Location.Valid = true
|
||||
}
|
||||
if data.Birthday != nil {
|
||||
user.Birthday.String = *data.Birthday
|
||||
user.Birthday.Valid = true
|
||||
}
|
||||
if data.RestrictedFollow != nil {
|
||||
user.RestrictedFollow = *data.RestrictedFollow
|
||||
} else {
|
||||
// Assume not restricted if not included in received data
|
||||
user.RestrictedFollow = false
|
||||
}
|
||||
if data.IsCat {
|
||||
if !sliceutils.ContainsFunc(user.BeingTypes, func(t models.UserToBeing) bool {
|
||||
return t.Being == string(models.BEING_CAT)
|
||||
}) {
|
||||
log.Debug().Msg("user doesn't contain cat yet")
|
||||
bt := models.UserToBeing{UserId: user.ID, Being: string(models.BEING_CAT)}
|
||||
if err = dbgen.UserToBeing.Create(&bt); err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to append cat being type to imported user")
|
||||
}
|
||||
if err = dbgen.User.BeingTypes.Model(user).Append(&bt); err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Msg("Failed to append cat being type to imported user relations")
|
||||
}
|
||||
user.BeingTypes = append(user.BeingTypes, bt)
|
||||
}
|
||||
} else {
|
||||
if sliceutils.ContainsFunc(user.BeingTypes, func(t models.UserToBeing) bool {
|
||||
return t.Being == string(models.BEING_CAT)
|
||||
}) {
|
||||
_, err = dbgen.UserToBeing.Where(dbgen.UserToBeing.UserId.Eq(user.ID)).Where(dbgen.UserToBeing.Being.Eq(string(models.BEING_CAT))).Delete()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to remove cat being type from user")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't handle SpeakAsCat yet, as not included in stored data yet
|
||||
|
||||
// Handle media last as more complicated
|
||||
// Icon *inboundImportUserMedia `json:"icon,omitempty"`
|
||||
// Banner *inboundImportUserMedia `json:"image,omitempty"`
|
||||
|
||||
id := user.ID
|
||||
user.ID = ""
|
||||
if _, err = dbgen.User.Where(dbgen.User.ID.Eq(id)).UpdateColumns(user); err != nil {
|
||||
return nil, other.Error("activitypub", "failed to update imported user", err)
|
||||
}
|
||||
rlid := user.RemoteInfo.ID
|
||||
user.RemoteInfo.ID = 0
|
||||
if _, err = dbgen.UserRemoteLinks.Where(dbgen.UserRemoteLinks.ID.Eq(rlid)).UpdateColumns(user.RemoteInfo); err != nil {
|
||||
return nil, other.Error("activitypub", "failed to update imported user's remote links", err)
|
||||
}
|
||||
user.ID = id
|
||||
|
||||
log.Info().Str("ap-url", apUrl).Msg("Import completed")
|
||||
return user, nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue