implemented federation
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package federation
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/forgeo/forgebucket/internal/models"
|
||||
)
|
||||
|
||||
// APID returns the canonical ActivityPub actor ID for a local username.
|
||||
func APID(instanceURL, username string) string {
|
||||
return strings.TrimRight(instanceURL, "/") + "/users/" + username
|
||||
}
|
||||
|
||||
// GetOrCreate fetches the FederationActor for a user, creating it with a fresh
|
||||
// RSA-2048 key pair if none exists. Actor URLs are derived from instanceURL.
|
||||
func GetOrCreate(db *xorm.Engine, userID int64, username, instanceURL string) (*models.FederationActor, error) {
|
||||
var actor models.FederationActor
|
||||
if found, _ := db.Where("user_id = ?", userID).Get(&actor); found {
|
||||
return &actor, nil
|
||||
}
|
||||
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("generate rsa key: %w", err)
|
||||
}
|
||||
|
||||
privPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(priv),
|
||||
})
|
||||
pubDER, err := x509.MarshalPKIXPublicKey(&priv.PublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal public key: %w", err)
|
||||
}
|
||||
pubPEM := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubDER})
|
||||
|
||||
base := APID(instanceURL, username)
|
||||
actor = models.FederationActor{
|
||||
UserID: userID,
|
||||
APID: base,
|
||||
InboxURL: base + "/inbox",
|
||||
OutboxURL: base + "/outbox",
|
||||
PublicKey: string(pubPEM),
|
||||
PrivateKey: string(privPEM),
|
||||
}
|
||||
if _, err := db.Insert(&actor); err != nil {
|
||||
// Race condition: another goroutine may have just created it.
|
||||
if found, _ := db.Where("user_id = ?", userID).Get(&actor); found {
|
||||
return &actor, nil
|
||||
}
|
||||
return nil, fmt.Errorf("insert actor: %w", err)
|
||||
}
|
||||
return &actor, nil
|
||||
}
|
||||
|
||||
// ActorJSON builds the JSON-LD actor document returned by GET /users/{username}.
|
||||
func ActorJSON(actor *models.FederationActor, username, displayName string) map[string]any {
|
||||
return map[string]any{
|
||||
"@context": []any{
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
},
|
||||
"id": actor.APID,
|
||||
"type": "Person",
|
||||
"preferredUsername": username,
|
||||
"name": displayName,
|
||||
"inbox": actor.InboxURL,
|
||||
"outbox": actor.OutboxURL,
|
||||
"followers": actor.APID + "/followers",
|
||||
"following": actor.APID + "/following",
|
||||
"publicKey": map[string]any{
|
||||
"id": actor.APID + "#main-key",
|
||||
"owner": actor.APID,
|
||||
"publicKeyPem": actor.PublicKey,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user