mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-21 08:31:27 -05:00
Start NodeInfo implementation
This commit is contained in:
parent
3c2493902d
commit
310d740cee
5 changed files with 167 additions and 48 deletions
|
@ -36,7 +36,7 @@ type RepositoryID struct {
|
|||
}
|
||||
|
||||
// newActorID receives already validated inputs
|
||||
func newActorID(validatedURI *url.URL, source string) (ActorID, error) {
|
||||
func NewActorID(validatedURI *url.URL) (ActorID, error) {
|
||||
pathWithActorID := strings.Split(validatedURI.Path, "/")
|
||||
if containsEmptyString(pathWithActorID) {
|
||||
pathWithActorID = removeEmptyStrings(pathWithActorID)
|
||||
|
@ -47,20 +47,30 @@ func newActorID(validatedURI *url.URL, source string) (ActorID, error) {
|
|||
|
||||
result := ActorID{}
|
||||
result.ID = id
|
||||
result.Source = source
|
||||
result.Schema = validatedURI.Scheme
|
||||
result.Host = validatedURI.Hostname()
|
||||
result.Path = pathWithoutActorID
|
||||
result.Port = validatedURI.Port()
|
||||
result.UnvalidatedInput = validatedURI.String()
|
||||
|
||||
if valid, err := IsValid(result); !valid {
|
||||
return ActorId{}, err
|
||||
if valid, outcome := validation.IsValid(result); !valid {
|
||||
return ActorID{}, outcome
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func newActorID(validatedURI *url.URL, source string) (ActorID, error) {
|
||||
result, err := NewActorID(validatedURI)
|
||||
if err != nil {
|
||||
return ActorID{}, err
|
||||
}
|
||||
|
||||
result.Source = source
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func NewPersonID(uri, source string) (PersonID, error) {
|
||||
// TODO: remove after test
|
||||
//if !validation.IsValidExternalURL(uri) {
|
||||
|
@ -138,12 +148,10 @@ func (id PersonID) HostSuffix() string {
|
|||
func (id ActorID) Validate() []string {
|
||||
var result []string
|
||||
result = append(result, validation.ValidateNotEmpty(id.ID, "userId")...)
|
||||
result = append(result, validation.ValidateNotEmpty(id.Source, "source")...)
|
||||
result = append(result, validation.ValidateNotEmpty(id.Schema, "schema")...)
|
||||
result = append(result, validation.ValidateNotEmpty(id.Path, "path")...)
|
||||
result = append(result, validation.ValidateNotEmpty(id.Host, "host")...)
|
||||
result = append(result, validation.ValidateNotEmpty(id.UnvalidatedInput, "unvalidatedInput")...)
|
||||
result = append(result, validation.ValidateOneOf(id.Source, []string{"forgejo", "gitea"})...)
|
||||
|
||||
if id.UnvalidatedInput != id.AsURI() {
|
||||
result = append(result, fmt.Sprintf("not all input: %q was parsed: %q", id.UnvalidatedInput, id.AsURI()))
|
||||
|
@ -154,6 +162,8 @@ func (id ActorID) Validate() []string {
|
|||
|
||||
func (id PersonID) Validate() []string {
|
||||
result := id.ActorID.Validate()
|
||||
result = append(result, validation.ValidateNotEmpty(id.Source, "source")...)
|
||||
result = append(result, validation.ValidateOneOf(id.Source, []string{"forgejo", "gitea"})...)
|
||||
switch id.Source {
|
||||
case "forgejo", "gitea":
|
||||
if strings.ToLower(id.Path) != "api/v1/activitypub/user-id" && strings.ToLower(id.Path) != "api/activitypub/user-id" {
|
||||
|
@ -165,6 +175,8 @@ func (id PersonID) Validate() []string {
|
|||
|
||||
func (id RepositoryID) Validate() []string {
|
||||
result := id.ActorID.Validate()
|
||||
result = append(result, validation.ValidateNotEmpty(id.Source, "source")...)
|
||||
result = append(result, validation.ValidateOneOf(id.Source, []string{"forgejo", "gitea"})...)
|
||||
switch id.Source {
|
||||
case "forgejo", "gitea":
|
||||
if strings.ToLower(id.Path) != "api/v1/activitypub/repository-id" && strings.ToLower(id.Path) != "api/activitypub/repository-id" {
|
||||
|
@ -192,31 +204,3 @@ func removeEmptyStrings(ls []string) []string {
|
|||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
func IsValid[T Validateables](value T) (bool, error) {
|
||||
if err := value.Validate(); len(err) > 0 {
|
||||
errString := strings.Join(err, "\n")
|
||||
return false, fmt.Errorf(errString)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (a RepositoryId) IsValid() (bool, error) {
|
||||
if err := a.Validate(); len(err) > 0 {
|
||||
errString := strings.Join(err, "\n")
|
||||
return false, fmt.Errorf(errString)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a PersonId) IsValid() (bool, error) {
|
||||
if err := a.Validate(); len(err) > 0 {
|
||||
errString := strings.Join(err, "\n")
|
||||
return false, fmt.Errorf(errString)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
*/
|
||||
|
|
78
models/forgefed/nodeinfo.go
Normal file
78
models/forgefed/nodeinfo.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2023 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package forgefed
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"code.gitea.io/gitea/modules/validation"
|
||||
"github.com/valyala/fastjson"
|
||||
)
|
||||
|
||||
type (
|
||||
SourceType string
|
||||
)
|
||||
|
||||
type SourceTypes []SourceType
|
||||
|
||||
const (
|
||||
ForgejoSourceType SourceType = "frogejo"
|
||||
)
|
||||
|
||||
var KnownSourceTypes = SourceTypes{
|
||||
ForgejoSourceType,
|
||||
}
|
||||
|
||||
// NodeInfo data type
|
||||
// swagger:model
|
||||
type NodeInfoWellKnown struct {
|
||||
Href string
|
||||
}
|
||||
|
||||
func NodeInfoWellKnownUnmarshalJSON(data []byte) (NodeInfoWellKnown, error) {
|
||||
p := fastjson.Parser{}
|
||||
val, err := p.ParseBytes(data)
|
||||
if err != nil {
|
||||
return NodeInfoWellKnown{}, err
|
||||
}
|
||||
href := string(val.GetStringBytes("links", "0", "href"))
|
||||
return NodeInfoWellKnown{Href: href}, nil
|
||||
}
|
||||
|
||||
func NewNodeInfoWellKnown(body []byte) (NodeInfoWellKnown, error) {
|
||||
result, err := NodeInfoWellKnownUnmarshalJSON(body)
|
||||
if err != nil {
|
||||
return NodeInfoWellKnown{}, err
|
||||
}
|
||||
|
||||
if valid, outcome := validation.IsValid(result); !valid {
|
||||
return NodeInfoWellKnown{}, outcome
|
||||
}
|
||||
|
||||
return NodeInfoWellKnown{}, nil
|
||||
}
|
||||
|
||||
// Validate collects error strings in a slice and returns this
|
||||
func (node NodeInfoWellKnown) Validate() []string {
|
||||
var result []string
|
||||
result = append(result, validation.ValidateNotEmpty(node.Href, "Href")...)
|
||||
|
||||
parsedUrl, err := url.Parse(node.Href)
|
||||
if err != nil {
|
||||
result = append(result, err.Error())
|
||||
return result
|
||||
}
|
||||
|
||||
if parsedUrl.Host == "" {
|
||||
result = append(result, "Href has to be absolute")
|
||||
}
|
||||
|
||||
result = append(result, validation.ValidateOneOf(parsedUrl.Scheme, []string{"http", "https"})...)
|
||||
|
||||
if parsedUrl.RawQuery != "" {
|
||||
result = append(result, "Href may not contain query")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
67
models/forgefed/nodeinfo_test.go
Normal file
67
models/forgefed/nodeinfo_test.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2023 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package forgefed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/validation"
|
||||
)
|
||||
|
||||
func Test_NodeInfoWellKnownUnmarshalJSON(t *testing.T) {
|
||||
type testPair struct {
|
||||
item []byte
|
||||
want NodeInfoWellKnown
|
||||
wantErr error
|
||||
}
|
||||
|
||||
tests := map[string]testPair{
|
||||
"with href": {
|
||||
item: []byte(`{"links":[{"href":"https://federated-repo.prod.meissa.de/api/v1/nodeinfo","rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"}]}`),
|
||||
want: NodeInfoWellKnown{
|
||||
Href: "https://federated-repo.prod.meissa.de/api/v1/nodeinfo",
|
||||
},
|
||||
},
|
||||
"empty": {
|
||||
item: []byte(``),
|
||||
wantErr: fmt.Errorf("cannot parse JSON: cannot parse empty string; unparsed tail: \"\""),
|
||||
},
|
||||
// "with too long href": {
|
||||
// item: []byte(`{"links":[{"href":"https://federated-repo.prod.meissa.de/api/v1/nodeinfohttps://federated-repo.prod.meissa.de/api/v1/nodeinfohttps://federated-repo.prod.meissa.de/api/v1/nodeinfohttps://federated-repo.prod.meissa.de/api/v1/nodeinfohttps://federated-repo.prod.meissa.de/api/v1/nodeinfohttps://federated-repo.prod.meissa.de/api/v1/nodeinfo","rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"}]}`),
|
||||
// wantErr: fmt.Errorf("cannot parse JSON: cannot parse empty string; unparsed tail: \"\""),
|
||||
// },
|
||||
}
|
||||
|
||||
for name, tt := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, err := NodeInfoWellKnownUnmarshalJSON(tt.item)
|
||||
if (err != nil || tt.wantErr != nil) && tt.wantErr.Error() != err.Error() {
|
||||
t.Errorf("UnmarshalJSON() error = \"%v\", wantErr \"%v\"", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("UnmarshalJSON() got = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NodeInfoWellKnownValidate(t *testing.T) {
|
||||
sut := NodeInfoWellKnown{Href: "https://federated-repo.prod.meissa.de/api/v1/nodeinfo"}
|
||||
if b, err := validation.IsValid(sut); !b {
|
||||
t.Errorf("sut should be valid, %v, %v", sut, err)
|
||||
}
|
||||
|
||||
sut = NodeInfoWellKnown{Href: "./federated-repo.prod.meissa.de/api/v1/nodeinfo"}
|
||||
if _, err := validation.IsValid(sut); err.Error() != "Href has to be absolute\nValue is not contained in allowed values [[http https]]" {
|
||||
t.Errorf("validation error expected but was: %v\n", err)
|
||||
}
|
||||
|
||||
sut = NodeInfoWellKnown{Href: "https://federated-repo.prod.meissa.de/api/v1/nodeinfo?alert=1"}
|
||||
if _, err := validation.IsValid(sut); err.Error() != "Href may not contain query" {
|
||||
t.Errorf("sut should be valid, %v, %v", sut, err)
|
||||
}
|
||||
}
|
|
@ -8,24 +8,10 @@ import (
|
|||
"github.com/valyala/fastjson"
|
||||
)
|
||||
|
||||
type (
|
||||
SourceType string
|
||||
)
|
||||
|
||||
type SourceTypes []SourceType
|
||||
|
||||
const (
|
||||
StarType ap.ActivityVocabularyType = "Star"
|
||||
)
|
||||
|
||||
const (
|
||||
ForgejoSourceType SourceType = "frogejo"
|
||||
)
|
||||
|
||||
var KnownSourceTypes = SourceTypes{
|
||||
ForgejoSourceType,
|
||||
}
|
||||
|
||||
// Star activity data type
|
||||
// swagger:model
|
||||
type Star struct {
|
||||
|
|
|
@ -90,6 +90,10 @@ func RepositoryInbox(ctx *context.APIContext) {
|
|||
log.Info("RepositoryInbox: activity:%v", activity)
|
||||
|
||||
// parse actorID (person)
|
||||
// rawActorID, err := forgefed.NewActorID(activity.Actor.GetID().String())
|
||||
|
||||
// nodeInfo, err := createNodeInfo(rawActorID)
|
||||
|
||||
actorID, err := forgefed.NewPersonID(activity.Actor.GetID().String(), string(activity.Source))
|
||||
if err != nil {
|
||||
ctx.ServerError("Validate actorId", err)
|
||||
|
|
Loading…
Reference in a new issue