diff --git a/docs/unsure-where-to-put/adr-map-federated-person.md b/docs/unsure-where-to-put/adr-map-federated-person.md index a9568287af..69ad01b0e3 100644 --- a/docs/unsure-where-to-put/adr-map-federated-person.md +++ b/docs/unsure-where-to-put/adr-map-federated-person.md @@ -289,7 +289,6 @@ classDiagram } class Actor { ID - URL Item Type ActivityVocabularyType // Person Name NaturalLanguageValues PreferredUsername NaturalLanguageValues @@ -346,7 +345,6 @@ classDiagram class FederatedUser { ID int64 UserID int64 - RawData map[string]any ExternalID string FederationHost int64 } diff --git a/models/user/federated_user.go b/models/user/federated_user.go index 134efc38f2..cb65948b73 100644 --- a/models/user/federated_user.go +++ b/models/user/federated_user.go @@ -4,28 +4,21 @@ package user import ( - "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/validation" ) -func init() { - db.RegisterModel(new(FederatedUser)) -} - type FederatedUser struct { - ID int64 `xorm:"pk NOT NULL"` - UserID int64 `xorm:"NOT NULL"` - ExternalID string `xorm:"TEXT UNIQUE(federation_mapping) NOT NULL"` - FederationHostID int64 `xorm:"UNIQUE(federation_mapping) NOT NULL"` - RawData map[string]any `xorm:"TEXT JSON"` + ID int64 `xorm:"pk NOT NULL"` + UserID int64 `xorm:"NOT NULL"` + ExternalID string `xorm:"TEXT UNIQUE(federation_mapping) NOT NULL"` + FederationHostID int64 `xorm:"UNIQUE(federation_mapping) NOT NULL"` } -func NewFederatedUser(userID int64, externalID string, federationHostID int64, rawData map[string]any) (FederatedUser, error) { +func NewFederatedUser(userID int64, externalID string, federationHostID int64) (FederatedUser, error) { result := FederatedUser{ UserID: userID, ExternalID: externalID, FederationHostID: federationHostID, - RawData: rawData, } if valid, err := validation.IsValid(result); !valid { return FederatedUser{}, err diff --git a/models/user/user_repository.go b/models/user/user_repository.go index 78be927498..43e7db32a3 100644 --- a/models/user/user_repository.go +++ b/models/user/user_repository.go @@ -1,2 +1,24 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT \ No newline at end of file +// SPDX-License-Identifier: MIT + +package user + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/validation" +) + +func init() { + db.RegisterModel(new(FederatedUser)) +} + +func CreateFederationUser(ctx context.Context, user FederatedUser) error { + if res, err := validation.IsValid(user); !res { + return fmt.Errorf("FederatedUser is not valid: %v", err) + } + _, err := db.GetEngine(ctx).Insert(user) + return err +} diff --git a/models/user/user_service.go b/models/user/user_service.go index 78be927498..0b010e7f8c 100644 --- a/models/user/user_service.go +++ b/models/user/user_service.go @@ -1,2 +1,80 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT \ No newline at end of file +// SPDX-License-Identifier: MIT + +package user + +import ( + "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) + + localFqdn, err := url.ParseRequestURI(setting.AppURL) + if err != nil { + return nil, 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 nil, err + } + + user := &User{ + LowerName: strings.ToLower(person.PreferredUsername.String()), + Name: name, + FullName: fullName, + Email: email, + EmailNotificationsPreference: "disabled", + Passwd: password, + MustChangePassword: false, + LoginName: loginName, + Type: UserTypeRemoteUser, + IsAdmin: false, + } + + overwrite := &CreateUserOverwriteOptions{ + IsActive: util.OptionalBoolFalse, + IsRestricted: util.OptionalBoolFalse, + } + + if err := CreateUser(ctx, user, overwrite); err != nil { + return nil, err + } + + federatedUser, err := NewFederatedUser(user.ID, personID.ID, federationHostID) + if err != nil { + return nil, err + } + + err = CreateFederationUser(ctx, federatedUser) + if err != nil { + return nil, err + } + + return user, nil +}