diff --git a/models/user/user_repository.go b/models/user/user_repository.go index 43e7db32a3..e6d625aae5 100644 --- a/models/user/user_repository.go +++ b/models/user/user_repository.go @@ -15,7 +15,7 @@ func init() { db.RegisterModel(new(FederatedUser)) } -func CreateFederationUser(ctx context.Context, user FederatedUser) error { +func CreateFederationUser(ctx context.Context, user *FederatedUser) error { if res, err := validation.IsValid(user); !res { return fmt.Errorf("FederatedUser is not valid: %v", err) } diff --git a/models/user/user_service.go b/models/user/user_service.go index 0b010e7f8c..01df922806 100644 --- a/models/user/user_service.go +++ b/models/user/user_service.go @@ -4,30 +4,25 @@ package user import ( + "context" "fmt" "net/url" "strings" "code.gitea.io/gitea/models/forgefed" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" "github.com/google/uuid" pwd_gen "github.com/sethvargo/go-password/password" ) -func CreateFederatedUserFromAP(ctx *context.APIContext, person forgefed.ForgePerson, personID forgefed.PersonID, - federationHostID int64) (*User, error) { - if res, err := validation.IsValid(person); !res { - return nil, err - } - log.Info("RepositoryInbox: validated person: %q", person) +func CreateFederatedUserFromAP(ctx context.Context, person forgefed.ForgePerson, + personID forgefed.PersonID, federationHostID int64) (*User, *FederatedUser, error) { localFqdn, err := url.ParseRequestURI(setting.AppURL) if err != nil { - return nil, err + return nil, nil, err } email := fmt.Sprintf("f%v@%v", uuid.New().String(), localFqdn.Hostname()) @@ -41,10 +36,10 @@ func CreateFederatedUserFromAP(ctx *context.APIContext, person forgefed.ForgePer password, err := pwd_gen.Generate(32, 10, 10, false, true) if err != nil { - return nil, err + return nil, nil, err } - user := &User{ + user := User{ LowerName: strings.ToLower(person.PreferredUsername.String()), Name: name, FullName: fullName, @@ -62,19 +57,20 @@ func CreateFederatedUserFromAP(ctx *context.APIContext, person forgefed.ForgePer IsRestricted: util.OptionalBoolFalse, } - if err := CreateUser(ctx, user, overwrite); err != nil { - return nil, err + // TODO: Transaction around + if err := CreateUser(ctx, &user, overwrite); err != nil { + return nil, nil, err } federatedUser, err := NewFederatedUser(user.ID, personID.ID, federationHostID) if err != nil { - return nil, err + return nil, nil, err } - err = CreateFederationUser(ctx, federatedUser) + err = CreateFederationUser(ctx, &federatedUser) if err != nil { - return nil, err + return nil, nil, err } - return user, nil + return &user, &federatedUser, nil } diff --git a/routers/api/v1/activitypub/repository.go b/routers/api/v1/activitypub/repository.go index 2f61283bc3..e7052625f5 100644 --- a/routers/api/v1/activitypub/repository.go +++ b/routers/api/v1/activitypub/repository.go @@ -6,7 +6,6 @@ package activitypub import ( "fmt" "net/http" - "net/url" "strings" "code.gitea.io/gitea/models/db" @@ -20,10 +19,8 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" - "github.com/google/uuid" ap "github.com/go-ap/activitypub" - pwd_gen "github.com/sethvargo/go-password/password" ) // Repository function returns the Repository actor for a repo @@ -97,28 +94,28 @@ func RepositoryInbox(ctx *context.APIContext) { "RepositoryInbox: Validating ActorID", err) return } - federationInfo, err := forgefed.FindFederationHostByFqdn(ctx, rawActorID.Host) + federationHost, err := forgefed.FindFederationHostByFqdn(ctx, rawActorID.Host) if err != nil { ctx.Error(http.StatusInternalServerError, "RepositoryInbox: Error while loading FederationInfo", err) return } - if federationInfo == nil { - result, err := createFederationInfo(ctx, rawActorID) + if federationHost == nil { + result, err := createFederationHost(ctx, rawActorID) if err != nil { ctx.Error(http.StatusNotAcceptable, "RepositoryInbox: Validate actorId", err) return } - federationInfo = &result - log.Info("RepositoryInbox: federationInfo validated: %v", federationInfo) + federationHost = &result + log.Info("RepositoryInbox: federationInfo validated: %v", federationHost) } - if !activity.IsNewer(federationInfo.LatestActivity) { + if !activity.IsNewer(federationHost.LatestActivity) { ctx.Error(http.StatusNotAcceptable, "RepositoryInbox: Validate Activity", fmt.Errorf("Activity already processed")) return } - actorID, err := forgefed.NewPersonID(actorURI, string(federationInfo.NodeInfo.Source)) + actorID, err := forgefed.NewPersonID(actorURI, string(federationHost.NodeInfo.Source)) if err != nil { ctx.Error(http.StatusNotAcceptable, "RepositoryInbox: Validate actorId", err) return @@ -140,6 +137,8 @@ func RepositoryInbox(ctx *context.APIContext) { log.Info("RepositoryInbox: remoteStargazer: %v", actorAsLoginID) // Check if user already exists + // TODO: search for federation user instead + // users, _, err := SearchFederatedUser(actorID.ID, federationHost.ID) users, err := SearchUsersByLoginName(actorAsLoginID) if err != nil { ctx.Error(http.StatusInternalServerError, "RepositoryInbox: Searching for user failed", err) @@ -150,7 +149,7 @@ func RepositoryInbox(ctx *context.APIContext) { switch len(users) { case 0: { - user, err = createUserFromAP(ctx, actorID) + user, err = createUserFromAP(ctx, actorID, federationHost.ID) if err != nil { ctx.Error(http.StatusInternalServerError, "RepositoryInbox: Creating federated user failed", err) @@ -180,8 +179,8 @@ func RepositoryInbox(ctx *context.APIContext) { return } } - federationInfo.LatestActivity = activity.StartTime - err = forgefed.UpdateFederationHost(ctx, *federationInfo) + federationHost.LatestActivity = activity.StartTime + err = forgefed.UpdateFederationHost(ctx, *federationHost) if err != nil { ctx.Error(http.StatusNotAcceptable, "RepositoryInbox: error updateing federateionInfo", err) return @@ -212,7 +211,7 @@ func SearchUsersByLoginName(loginName string) ([]*user_model.User, error) { return users, nil } -func createFederationInfo(ctx *context.APIContext, actorID forgefed.ActorID) (forgefed.FederationHost, error) { +func createFederationHost(ctx *context.APIContext, actorID forgefed.ActorID) (forgefed.FederationHost, error) { actionsUser := user_model.NewActionsUser() client, err := api.NewClient(ctx, actionsUser, "no idea where to get key material.") if err != nil { @@ -245,68 +244,32 @@ func createFederationInfo(ctx *context.APIContext, actorID forgefed.ActorID) (fo return result, nil } -func createUserFromAP(ctx *context.APIContext, personID forgefed.PersonID) (*user_model.User, error) { +func createUserFromAP(ctx *context.APIContext, personID forgefed.PersonID, federationHostID int64) (*user_model.User, error) { // ToDo: Do we get a publicKeyId from server, repo or owner or repo? actionsUser := user_model.NewActionsUser() client, err := api.NewClient(ctx, actionsUser, "no idea where to get key material.") if err != nil { - return &user_model.User{}, err + return nil, err } body, err := client.GetBody(personID.AsURI()) if err != nil { - return &user_model.User{}, err + return nil, err } person := forgefed.ForgePerson{} err = person.UnmarshalJSON(body) if err != nil { - return &user_model.User{}, err + return nil, err } if res, err := validation.IsValid(person); !res { - return &user_model.User{}, err + return nil, err } log.Info("RepositoryInbox: validated person: %q", person) - localFqdn, err := url.ParseRequestURI(setting.AppURL) + user, _, err := user_model.CreateFederatedUserFromAP(ctx, person, personID, federationHostID) if err != nil { - return &user_model.User{}, err - } - - email := fmt.Sprintf("f%v@%v", uuid.New().String(), localFqdn.Hostname()) - loginName := personID.AsLoginName() - name := fmt.Sprintf("%v%v", person.PreferredUsername.String(), personID.HostSuffix()) - log.Info("RepositoryInbox: person.Name: %v", person.Name) - fullName := person.Name.String() - if len(person.Name) == 0 { - fullName = name - } - - password, err := pwd_gen.Generate(32, 10, 10, false, true) - if err != nil { - return &user_model.User{}, err - } - - user := &user_model.User{ - LowerName: strings.ToLower(person.PreferredUsername.String()), - Name: name, - FullName: fullName, - Email: email, - EmailNotificationsPreference: "disabled", - Passwd: password, - MustChangePassword: false, - LoginName: loginName, - Type: user_model.UserTypeRemoteUser, - IsAdmin: false, - } - - overwrite := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolFalse, - IsRestricted: util.OptionalBoolFalse, - } - - if err := user_model.CreateUser(ctx, user, overwrite); err != nil { - return &user_model.User{}, err + return nil, err } return user, nil