diff --git a/routers/api/v1/activitypub/authorize_interaction.go b/routers/api/v1/activitypub/authorize_interaction.go index a431bfb060..2def2dd3f8 100644 --- a/routers/api/v1/activitypub/authorize_interaction.go +++ b/routers/api/v1/activitypub/authorize_interaction.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/forgefed" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/activitypub" ap "github.com/go-ap/activitypub" @@ -51,7 +52,7 @@ func AuthorizeInteraction(ctx *context.Context) { ctx.ServerError("PersonIRIToName", err) return } - ctx.Redirect(name) + ctx.Redirect(setting.AppSubURL + name) case forgefed.RepositoryType: // Federated repository err = forgefed.OnRepository(object, func(r *forgefed.Repository) error { @@ -66,7 +67,7 @@ func AuthorizeInteraction(ctx *context.Context) { ctx.ServerError("RepositoryIRIToName", err) return } - ctx.Redirect(username + "/" + reponame) + ctx.Redirect(setting.AppSubURL + username + "/" + reponame) case forgefed.TicketType: // Federated issue or pull request err = forgefed.OnTicket(object, func(t *forgefed.Ticket) error { @@ -81,7 +82,7 @@ func AuthorizeInteraction(ctx *context.Context) { ctx.ServerError("TicketIRIToName", err) return } - ctx.Redirect(username + "/" + reponame + "/issues/" + strconv.FormatInt(idx, 10)) + ctx.Redirect(setting.AppSubURL + username + "/" + reponame + "/issues/" + strconv.FormatInt(idx, 10)) default: ctx.ServerError("Not implemented", err) return diff --git a/routers/api/v1/activitypub/create.go b/routers/api/v1/activitypub/create.go index e2bb2e4618..90f19dfc54 100644 --- a/routers/api/v1/activitypub/create.go +++ b/routers/api/v1/activitypub/create.go @@ -91,9 +91,8 @@ func createPerson(ctx context.Context, person *ap.Person) error { return user_model.SetUserSetting(user.ID, user_model.UserActivityPubPubPem, person.PublicKey.PublicKeyPem) } -// Create a new federated repo from a Repository object -func createRepository(ctx context.Context, repository *forgefed.Repository) error { - ownerURL, err := url.Parse(repository.AttributedTo.GetLink().String()) +func createPersonFromIRI(ctx context.Context, personIRI ap.IRI) error { + ownerURL, err := url.Parse(personIRI.String()) if err != nil { return err } @@ -102,6 +101,7 @@ func createRepository(ctx context.Context, repository *forgefed.Repository) erro if err != nil { return err } + // Parse person object ap.ItemTyperFunc = forgefed.GetItemByType ap.JSONItemUnmarshal = forgefed.JSONUnmarshalerFn @@ -110,12 +110,17 @@ func createRepository(ctx context.Context, repository *forgefed.Repository) erro if err != nil { return err } + // Create federated user - err = createPerson(ctx, object.(*ap.Person)) + return createPerson(ctx, object.(*ap.Person)) +} + +// Create a new federated repo from a Repository object +func createRepository(ctx context.Context, repository *forgefed.Repository) error { + err := createPersonFromIRI(ctx, repository.AttributedTo.GetLink()) if err != nil { return err } - user, err := activitypub.PersonIRIToUser(ctx, repository.AttributedTo.GetLink()) if err != nil { return err @@ -145,17 +150,8 @@ func createRepository(ctx context.Context, repository *forgefed.Repository) erro return nil } -// Create a ticket -func createTicket(ctx context.Context, ticket *forgefed.Ticket) error { - if ticket.Origin != nil { - return createPullRequest(ctx, ticket) - } - return createIssue(ctx, ticket) -} - -// Create an issue -func createIssue(ctx context.Context, ticket *forgefed.Ticket) error { - repoURL, err := url.Parse(ticket.Context.GetLink().String()) +func createRepositoryFromIRI(ctx context.Context, repoIRI ap.IRI) error { + repoURL, err := url.Parse(repoIRI.String()) if err != nil { return err } @@ -164,6 +160,7 @@ func createIssue(ctx context.Context, ticket *forgefed.Ticket) error { if err != nil { return err } + // Parse repository object ap.ItemTyperFunc = forgefed.GetItemByType ap.JSONItemUnmarshal = forgefed.JSONUnmarshalerFn @@ -172,10 +169,24 @@ func createIssue(ctx context.Context, ticket *forgefed.Ticket) error { if err != nil { return err } + // Create federated repo - err = forgefed.OnRepository(object, func(r *forgefed.Repository) error { + return forgefed.OnRepository(object, func(r *forgefed.Repository) error { return createRepository(ctx, r) }) +} + +// Create a ticket +func createTicket(ctx context.Context, ticket *forgefed.Ticket) error { + if ticket.Origin != nil && ticket.Target != nil { + return createPullRequest(ctx, ticket) + } + return createIssue(ctx, ticket) +} + +// Create an issue +func createIssue(ctx context.Context, ticket *forgefed.Ticket) error { + err := createRepositoryFromIRI(ctx, ticket.Context.GetLink()) if err != nil { return err } @@ -194,53 +205,62 @@ func createIssue(ctx context.Context, ticket *forgefed.Ticket) error { return err } issue := &issues_model.Issue{ - Index: idx, + Index: idx, // This doesn't seem to work? RepoID: repo.ID, Repo: repo, Title: ticket.Summary.String(), PosterID: user.ID, Poster: user, Content: ticket.Content.String(), + IsClosed: ticket.IsResolved, } return issue_service.NewIssue(repo, issue, nil, nil, nil) } // Create a pull request func createPullRequest(ctx context.Context, ticket *forgefed.Ticket) error { - // TODO: Clean this up - - actorUser, err := activitypub.PersonIRIToUser(ctx, ticket.AttributedTo.GetLink()) + err := createRepositoryFromIRI(ctx, ticket.Context.GetLink()) if err != nil { return err } - // TODO: The IRI processing stuff should be moved to iri.go - originIRI := ticket.Origin.GetLink() - originIRISplit := strings.Split(originIRI.String(), "/") - originInstance := originIRISplit[2] - originUsername := originIRISplit[3] - originReponame := originIRISplit[4] - originBranch := originIRISplit[len(originIRISplit)-1] - originRepo, _ := repo_model.GetRepositoryByOwnerAndName(originUsername+"@"+originInstance, originReponame) - - targetIRI := ticket.Target.GetLink() - targetIRISplit := strings.Split(targetIRI.String(), "/") - // targetInstance := targetIRISplit[2] - targetUsername := targetIRISplit[3] - targetReponame := targetIRISplit[4] - targetBranch := targetIRISplit[len(targetIRISplit)-1] - - targetRepo, _ := repo_model.GetRepositoryByOwnerAndName(targetUsername, targetReponame) - - prIssue := &issues_model.Issue{ - RepoID: targetRepo.ID, - Title: "Hello from test.exozy.me!", // Don't hardcode, get the title from the Ticket object - PosterID: actorUser.ID, - Poster: actorUser, - IsPull: true, - Content: "🎉", // TODO: Get content from Ticket object + user, err := activitypub.PersonIRIToUser(ctx, ticket.AttributedTo.GetLink()) + if err != nil { + return err } + // Extract origin and target repos + originUsername, originReponame, originBranch, err := activitypub.BranchIRIToName(ticket.Origin.GetLink()) + if err != nil { + return err + } + originRepo, err := repo_model.GetRepositoryByOwnerAndName(originUsername, originReponame) + if err != nil { + return err + } + targetUsername, targetReponame, targetBranch, err := activitypub.BranchIRIToName(ticket.Target.GetLink()) + if err != nil { + return err + } + targetRepo, err := repo_model.GetRepositoryByOwnerAndName(targetUsername, targetReponame) + if err != nil { + return err + } + + idx, err := strconv.ParseInt(ticket.Name.String()[1:], 10, 64) + if err != nil { + return err + } + prIssue := &issues_model.Issue{ + Index: idx, + RepoID: targetRepo.ID, + Title: ticket.Summary.String(), + PosterID: user.ID, + Poster: user, + IsPull: true, + Content: ticket.Content.String(), + IsClosed: ticket.IsResolved, + } pr := &issues_model.PullRequest{ HeadRepoID: originRepo.ID, BaseRepoID: targetRepo.ID, @@ -251,12 +271,16 @@ func createPullRequest(ctx context.Context, ticket *forgefed.Ticket) error { MergeBase: "", Type: issues_model.PullRequestGitea, } - return pull_service.NewPullRequest(ctx, targetRepo, prIssue, []int64{}, []string{}, pr, []int64{}) } // Create a comment func createComment(ctx context.Context, note *ap.Note) error { + err := createPersonFromIRI(ctx, note.AttributedTo.GetLink()) + if err != nil { + return err + } + actorUser, err := activitypub.PersonIRIToUser(ctx, note.AttributedTo.GetLink()) if err != nil { return err diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index 8e48263774..1d19c1ddb2 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -78,6 +78,8 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er return } // 4. Create a federated user for the actor + // TODO: This is a very bad place for creating federated users + // We end up creating way more users than necessary! var person ap.Person err = person.UnmarshalJSON(b) if err != nil { diff --git a/routers/api/v1/activitypub/ticket.go b/routers/api/v1/activitypub/ticket.go index 09da07e2e2..d20efc12ab 100644 --- a/routers/api/v1/activitypub/ticket.go +++ b/routers/api/v1/activitypub/ticket.go @@ -65,9 +65,9 @@ func Ticket(ctx *context.APIContext) { return } - ticket.Name = ap.NaturalLanguageValuesNew() // Setting a NaturalLanguageValue to a number causes go-ap's JSON parsing to do weird things // Workaround: set it to #1 instead of 1 + ticket.Name = ap.NaturalLanguageValuesNew() err = ticket.Name.Set("en", ap.Content("#"+ctx.Params("id"))) if err != nil { ctx.ServerError("Set Name", err) diff --git a/services/activitypub/iri.go b/services/activitypub/iri.go index d7d098f4a9..f74c027222 100644 --- a/services/activitypub/iri.go +++ b/services/activitypub/iri.go @@ -83,7 +83,7 @@ func RepositoryIRIToRepository(ctx context.Context, repoIRI ap.IRI) (*repo_model func TicketIRIToName(ticketIRI ap.IRI) (string, string, int64, error) { ticketIRISplit := strings.Split(ticketIRI.String(), "/") if len(ticketIRISplit) < 5 { - return "", "", 0, errors.New("not a Ticket actor IRI") + return "", "", 0, errors.New("not a Ticket object IRI") } instance := ticketIRISplit[2] @@ -100,3 +100,22 @@ func TicketIRIToName(ticketIRI ap.IRI) (string, string, int64, error) { // Remote repo return username + "@" + instance, reponame, idx, nil } + +// Returns the owner, repo name, and idx of a Branch object IRI +func BranchIRIToName(ticketIRI ap.IRI) (string, string, string, error) { + ticketIRISplit := strings.Split(ticketIRI.String(), "/") + if len(ticketIRISplit) < 5 { + return "", "", "", errors.New("not a Branch object IRI") + } + + instance := ticketIRISplit[2] + username := ticketIRISplit[len(ticketIRISplit)-3] + reponame := ticketIRISplit[len(ticketIRISplit)-2] + branch := ticketIRISplit[len(ticketIRISplit)-1] + if instance == setting.Domain { + // Local repo + return username, reponame, branch, nil + } + // Remote repo + return username + "@" + instance, reponame, branch, nil +}