loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

initial

+278
+35
models/user/federated_user.go
··· 1 + // Copyright 2024 The Forgejo Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package user 5 + 6 + import ( 7 + "code.gitea.io/gitea/modules/validation" 8 + ) 9 + 10 + type FederatedUser struct { 11 + ID int64 `xorm:"pk autoincr"` 12 + UserID int64 `xorm:"NOT NULL"` 13 + ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` 14 + FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` 15 + } 16 + 17 + func NewFederatedUser(userID int64, externalID string, federationHostID int64) (FederatedUser, error) { 18 + result := FederatedUser{ 19 + UserID: userID, 20 + ExternalID: externalID, 21 + FederationHostID: federationHostID, 22 + } 23 + if valid, err := validation.IsValid(result); !valid { 24 + return FederatedUser{}, err 25 + } 26 + return result, nil 27 + } 28 + 29 + func (user FederatedUser) Validate() []string { 30 + var result []string 31 + result = append(result, validation.ValidateNotEmpty(user.UserID, "UserID")...) 32 + result = append(result, validation.ValidateNotEmpty(user.ExternalID, "ExternalID")...) 33 + result = append(result, validation.ValidateNotEmpty(user.FederationHostID, "FederationHostID")...) 34 + return result 35 + }
+29
models/user/federated_user_test.go
··· 1 + // Copyright 2024 The Forgejo Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package user 5 + 6 + import ( 7 + "testing" 8 + 9 + "code.gitea.io/gitea/modules/validation" 10 + ) 11 + 12 + func Test_FederatedUserValidation(t *testing.T) { 13 + sut := FederatedUser{ 14 + UserID: 12, 15 + ExternalID: "12", 16 + FederationHostID: 1, 17 + } 18 + if res, err := validation.IsValid(sut); !res { 19 + t.Errorf("sut should be valid but was %q", err) 20 + } 21 + 22 + sut = FederatedUser{ 23 + ExternalID: "12", 24 + FederationHostID: 1, 25 + } 26 + if res, _ := validation.IsValid(sut); res { 27 + t.Errorf("sut should be invalid") 28 + } 29 + }
+20
models/user/user.go
··· 1 1 // Copyright 2014 The Gogs Authors. All rights reserved. 2 2 // Copyright 2019 The Gitea Authors. All rights reserved. 3 + // Copyright 2024 The Forgejo Authors. All rights reserved. 3 4 // SPDX-License-Identifier: MIT 4 5 5 6 package user ··· 130 131 Avatar string `xorm:"VARCHAR(2048) NOT NULL"` 131 132 AvatarEmail string `xorm:"NOT NULL"` 132 133 UseCustomAvatar bool 134 + 135 + // For federation 136 + NormalizedFederatedURI string 133 137 134 138 // Counters 135 139 NumFollowers int ··· 301 305 // HTMLURL returns the user or organization's full link. 302 306 func (u *User) HTMLURL() string { 303 307 return setting.AppURL + url.PathEscape(u.Name) 308 + } 309 + 310 + // APAPIURL returns the IRI to the api endpoint of the user 311 + func (u *User) APAPIURL() string { 312 + return fmt.Sprintf("%vapi/v1/activitypub/user-id/%v", setting.AppURL, url.PathEscape(fmt.Sprintf("%v", u.ID))) 304 313 } 305 314 306 315 // OrganisationLink returns the organization sub page link. ··· 832 841 } 833 842 834 843 return nil 844 + } 845 + 846 + func (u User) Validate() []string { 847 + var result []string 848 + if err := ValidateUser(&u); err != nil { 849 + result = append(result, err.Error()) 850 + } 851 + if err := ValidateEmail(u.Email); err != nil { 852 + result = append(result, err.Error()) 853 + } 854 + return result 835 855 } 836 856 837 857 // UpdateUserCols update user according special columns
+83
models/user/user_repository.go
··· 1 + // Copyright 2024 The Forgejo Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package user 5 + 6 + import ( 7 + "context" 8 + "fmt" 9 + 10 + "code.gitea.io/gitea/models/db" 11 + "code.gitea.io/gitea/modules/optional" 12 + "code.gitea.io/gitea/modules/validation" 13 + ) 14 + 15 + func init() { 16 + db.RegisterModel(new(FederatedUser)) 17 + } 18 + 19 + func CreateFederatedUser(ctx context.Context, user *User, federatedUser *FederatedUser) error { 20 + if res, err := validation.IsValid(user); !res { 21 + return err 22 + } 23 + overwrite := CreateUserOverwriteOptions{ 24 + IsActive: optional.Some(false), 25 + IsRestricted: optional.Some(false), 26 + } 27 + 28 + // Begin transaction 29 + ctx, committer, err := db.TxContext((ctx)) 30 + if err != nil { 31 + return err 32 + } 33 + defer committer.Close() 34 + 35 + if err := CreateUser(ctx, user, &overwrite); err != nil { 36 + return err 37 + } 38 + 39 + federatedUser.UserID = user.ID 40 + if res, err := validation.IsValid(federatedUser); !res { 41 + return err 42 + } 43 + 44 + _, err = db.GetEngine(ctx).Insert(federatedUser) 45 + if err != nil { 46 + return err 47 + } 48 + 49 + // Commit transaction 50 + return committer.Commit() 51 + } 52 + 53 + func FindFederatedUser(ctx context.Context, externalID string, 54 + federationHostID int64, 55 + ) (*User, *FederatedUser, error) { 56 + federatedUser := new(FederatedUser) 57 + user := new(User) 58 + has, err := db.GetEngine(ctx).Where("external_id=? and federation_host_id=?", externalID, federationHostID).Get(federatedUser) 59 + if err != nil { 60 + return nil, nil, err 61 + } else if !has { 62 + return nil, nil, nil 63 + } 64 + has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user) 65 + if err != nil { 66 + return nil, nil, err 67 + } else if !has { 68 + return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) 69 + } 70 + 71 + if res, err := validation.IsValid(*user); !res { 72 + return nil, nil, err 73 + } 74 + if res, err := validation.IsValid(*federatedUser); !res { 75 + return nil, nil, err 76 + } 77 + return user, federatedUser, nil 78 + } 79 + 80 + func DeleteFederatedUser(ctx context.Context, userID int64) error { 81 + _, err := db.GetEngine(ctx).Delete(&FederatedUser{UserID: userID}) 82 + return err 83 + }
+10
models/user/user_test.go
··· 1 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 + // Copyright 2024 The Forgejo Authors. All rights reserved. 2 3 // SPDX-License-Identifier: MIT 3 4 4 5 package user_test ··· 105 106 assert.True(t, found[user_model.UserTypeIndividual], users) 106 107 assert.True(t, found[user_model.UserTypeRemoteUser], users) 107 108 assert.False(t, found[user_model.UserTypeOrganization], users) 109 + } 110 + 111 + func TestAPAPIURL(t *testing.T) { 112 + user := user_model.User{ID: 1} 113 + url := user.APAPIURL() 114 + expected := "https://try.gitea.io/api/v1/activitypub/user-id/1" 115 + if url != expected { 116 + t.Errorf("unexpected APAPIURL, expected: %q, actual: %q", expected, url) 117 + } 108 118 } 109 119 110 120 func TestSearchUsers(t *testing.T) {
+101
services/federation/federation_service.go
··· 7 7 "context" 8 8 "fmt" 9 9 "net/http" 10 + "net/url" 11 + "strings" 10 12 11 13 "code.gitea.io/gitea/models/forgefed" 12 14 "code.gitea.io/gitea/models/user" 13 15 "code.gitea.io/gitea/modules/activitypub" 16 + "code.gitea.io/gitea/modules/auth/password" 14 17 fm "code.gitea.io/gitea/modules/forgefed" 15 18 "code.gitea.io/gitea/modules/log" 19 + "code.gitea.io/gitea/modules/setting" 16 20 "code.gitea.io/gitea/modules/validation" 21 + 22 + "github.com/google/uuid" 17 23 ) 18 24 19 25 // ProcessLikeActivity receives a ForgeLike activity and does the following: ··· 40 46 if !activity.IsNewer(federationHost.LatestActivity) { 41 47 return http.StatusNotAcceptable, "Activity out of order.", fmt.Errorf("Activity already processed") 42 48 } 49 + actorID, err := fm.NewPersonID(actorURI, string(federationHost.NodeInfo.SoftwareName)) 50 + if err != nil { 51 + return http.StatusNotAcceptable, "Invalid PersonID", err 52 + } 53 + log.Info("Actor accepted:%v", actorID) 54 + 55 + // parse objectID (repository) 56 + objectID, err := fm.NewRepositoryID(activity.Object.GetID().String(), string(forgefed.ForgejoSourceType)) 57 + if err != nil { 58 + return http.StatusNotAcceptable, "Invalid objectId", err 59 + } 60 + if objectID.ID != fmt.Sprint(repositoryID) { 61 + return http.StatusNotAcceptable, "Invalid objectId", err 62 + } 63 + log.Info("Object accepted:%v", objectID) 64 + 65 + // Check if user already exists 66 + user, _, err := user.FindFederatedUser(ctx, actorID.ID, federationHost.ID) 67 + if err != nil { 68 + return http.StatusInternalServerError, "Searching for user failed", err 69 + } 70 + if user != nil { 71 + log.Info("Found local federatedUser: %v", user) 72 + } else { 73 + user, _, err = CreateUserFromAP(ctx, actorID, federationHost.ID) 74 + if err != nil { 75 + return http.StatusInternalServerError, "Error creating federatedUser", err 76 + } 77 + log.Info("Created federatedUser from ap: %v", user) 78 + } 79 + log.Info("Got user:%v", user.Name) 43 80 44 81 return 0, "", nil 45 82 } ··· 96 133 } 97 134 return federationHost, nil 98 135 } 136 + 137 + func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID int64) (*user.User, *user.FederatedUser, error) { 138 + // ToDo: Do we get a publicKeyId from server, repo or owner or repo? 139 + actionsUser := user.NewActionsUser() 140 + client, err := activitypub.NewClient(ctx, actionsUser, "no idea where to get key material.") 141 + if err != nil { 142 + return nil, nil, err 143 + } 144 + 145 + body, err := client.GetBody(personID.AsURI()) 146 + if err != nil { 147 + return nil, nil, err 148 + } 149 + 150 + person := fm.ForgePerson{} 151 + err = person.UnmarshalJSON(body) 152 + if err != nil { 153 + return nil, nil, err 154 + } 155 + if res, err := validation.IsValid(person); !res { 156 + return nil, nil, err 157 + } 158 + log.Info("Fetched valid person:%q", person) 159 + 160 + localFqdn, err := url.ParseRequestURI(setting.AppURL) 161 + if err != nil { 162 + return nil, nil, err 163 + } 164 + email := fmt.Sprintf("f%v@%v", uuid.New().String(), localFqdn.Hostname()) 165 + loginName := personID.AsLoginName() 166 + name := fmt.Sprintf("%v%v", person.PreferredUsername.String(), personID.HostSuffix()) 167 + fullName := person.Name.String() 168 + if len(person.Name) == 0 { 169 + fullName = name 170 + } 171 + password, err := password.Generate(32) 172 + if err != nil { 173 + return nil, nil, err 174 + } 175 + newUser := user.User{ 176 + LowerName: strings.ToLower(person.PreferredUsername.String()), 177 + Name: name, 178 + FullName: fullName, 179 + Email: email, 180 + EmailNotificationsPreference: "disabled", 181 + Passwd: password, 182 + MustChangePassword: false, 183 + LoginName: loginName, 184 + Type: user.UserTypeRemoteUser, 185 + IsAdmin: false, 186 + NormalizedFederatedURI: personID.AsURI(), 187 + } 188 + federatedUser := user.FederatedUser{ 189 + ExternalID: personID.ID, 190 + FederationHostID: federationHostID, 191 + } 192 + err = user.CreateFederatedUser(ctx, &newUser, &federatedUser) 193 + if err != nil { 194 + return nil, nil, err 195 + } 196 + log.Info("Created federatedUser:%q", federatedUser) 197 + 198 + return &newUser, &federatedUser, nil 199 + }