forgejo/routers/api/v1/activitypub/repository.go
2024-02-10 15:09:54 +01:00

171 lines
5.5 KiB
Go

// Copyright 2023 The forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activitypub
import (
"fmt"
"io"
"net/http"
"strings"
"code.gitea.io/gitea/models/activitypub"
api "code.gitea.io/gitea/modules/activitypub"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/forgefed"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
ap "github.com/go-ap/activitypub"
//f3 "lab.forgefriends.org/friendlyforgeformat/gof3"
)
// Repository function returns the Repository actor for a repo
func Repository(ctx *context.APIContext) {
// swagger:operation GET /activitypub/repository-id/{repository-id} activitypub activitypubRepository
// ---
// summary: Returns the Repository actor for a repo
// produces:
// - application/json
// parameters:
// - name: repository-id
// in: path
// description: repository ID of the repo
// type: integer
// required: true
// responses:
// "200":
// "$ref": "#/responses/ActivityPub"
link := fmt.Sprintf("%s/api/v1/activitypub/repository-id/%d", strings.TrimSuffix(setting.AppURL, "/"), ctx.Repo.Repository.ID)
repo := forgefed.RepositoryNew(ap.IRI(link))
repo.Name = ap.NaturalLanguageValuesNew()
err := repo.Name.Set("en", ap.Content(ctx.Repo.Repository.Name))
if err != nil {
ctx.ServerError("Set Name", err)
return
}
response(ctx, repo)
}
// PersonInbox function handles the incoming data for a repository inbox
func RepositoryInbox(ctx *context.APIContext) {
// swagger:operation POST /activitypub/repository-id/{repository-id}/inbox activitypub activitypubRepository
// ---
// summary: Send to the inbox
// produces:
// - application/json
// parameters:
// - name: repository-id
// in: path
// description: repository ID of the repo
// type: integer
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/Star"
// responses:
// "204":
// "$ref": "#/responses/empty"
log.Info("RepositoryInbox: repo %v, %v", ctx.Repo.Repository.OwnerName, ctx.Repo.Repository.Name)
opt := web.GetForm(ctx).(*forgefed.Star)
log.Info("RepositoryInbox: Activity.Source %v", opt.Source)
log.Info("RepositoryInbox: Activity.Actor %v", opt.Actor)
// assume actor is: "actor": "https://codeberg.org/api/v1/activitypub/user-id/12345" - NB: This might be actually the ID? Maybe check vocabulary.
// parse actor
actor, err := activitypub.ParseActorIDFromStarActivity(opt)
// Is the actor IRI well formed?
if err != nil {
panic(err)
}
// Is the ActorData Struct valid?
actor.PanicIfInvalid()
log.Info("RepositoryInbox: Actor parsed. %v", actor)
/*
Make http client, this should make a get request on given url
We then need to parse the answer and put it into a person-struct
fill the person struct using some kind of unmarshall function given in
activitypub package/actor.go
*/
// make http client
host := opt.To.GetID().String()
client, err := api.NewClient(ctx, ctx.Doer, host) // ToDo: This is hacky, we need a hostname from somewhere
if err != nil {
panic(err)
}
// get_person_by_rest
bytes := []byte{0} // no body needed for getting user actor
target := opt.Actor.GetID().String() // target is the person actor that originally performed the star activity
response, err := client.Get(bytes, target)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
// parse response
person := ap.Person{}
err = person.UnmarshalJSON(body)
if err != nil {
panic(err)
}
log.Info("target: %v", target)
log.Info("http client. %v", client)
log.Info("response: %v\n error: ", response, err)
log.Info("Person is: %v", person)
log.Info("Person Name is: %v", person.PreferredUsername)
log.Info("Person URL is: %v", person.URL)
// create_user_from_person (if not alreaydy present)
/*
ToDo:
This might be a forgefed specification question?
How do we identify users from other places? Do we get the information needed to actually create a user?
E.g. email adress seems not to be part of the person actor (and may be considered sensitive info by some)
cmd/admin_user_create.go seems to require an EMail adress for user creation.
We might just create a user with a random email adress and a random password.
But what if that user wants to create an account at our instance with the same user name?
Or might it just be easier to change the data structure of the star and
save identifying info of a remote user there? That would make the star structure quite bloaty though.
It would also implicate that every other feature to be federated might need their own struct to be changed.
Or create a federated user struct?
That way, we could save the user info apart from any federated feature but tie it to that.
On a new registration we could check whether that user has already been seen as federated user and maybe ask if they want to connect these information.
Features to be federated in the future might benefit from that
The database of federated features will have to be updated with the new user values then.
The "if not already present" part might be easy:
Check the user database for given user id.
This could happen with something like: user_model.SearchUsers() as seen in routers/api/v1/user.go
SearchUsers is defined in models/user/search.go
And depending on implementation check if the person already exists in federated user db.
*/
// wait 15 sec.
ctx.Status(http.StatusNoContent)
}