From 8337fc54c255631b4d28e28f2aca5dc26dd71a29 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Thu, 17 Feb 2022 15:56:53 +0000 Subject: [PATCH 1/2] Added ContextUser. --- integrations/api_user_org_perm_test.go | 2 +- modules/context/context.go | 5 +- modules/context/org.go | 3 +- modules/context/repo.go | 1 + routers/api/v1/admin/org.go | 8 +- routers/api/v1/admin/repo.go | 8 +- routers/api/v1/admin/user.go | 82 ++++++--------- routers/api/v1/api.go | 17 ++- routers/api/v1/org/org.go | 17 +-- routers/api/v1/user/follower.go | 42 ++------ routers/api/v1/user/gpg_key.go | 6 +- routers/api/v1/user/key.go | 6 +- routers/api/v1/user/repo.go | 6 +- routers/api/v1/user/star.go | 7 +- routers/api/v1/user/user.go | 17 +-- routers/api/v1/user/watch.go | 5 +- routers/web/feed/profile.go | 22 ++-- routers/web/repo/http.go | 15 +-- routers/web/user/home.go | 8 +- routers/web/user/profile.go | 140 +++++-------------------- routers/web/web.go | 18 +++- services/context/user.go | 62 +++++++++++ 22 files changed, 208 insertions(+), 289 deletions(-) create mode 100644 services/context/user.go diff --git a/integrations/api_user_org_perm_test.go b/integrations/api_user_org_perm_test.go index 0dcdbd77adf2..f4047e72bee7 100644 --- a/integrations/api_user_org_perm_test.go +++ b/integrations/api_user_org_perm_test.go @@ -133,7 +133,7 @@ func TestUnknowUser(t *testing.T) { var apiError api.APIError DecodeJSON(t, resp, &apiError) - assert.Equal(t, "GetUserByName", apiError.Message) + assert.Equal(t, "user redirect does not exist [name: unknow]", apiError.Message) } func TestUnknowOrganization(t *testing.T) { diff --git a/modules/context/context.go b/modules/context/context.go index 6aeeb9e69467..b24ec7d02839 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -67,8 +67,9 @@ type Context struct { IsSigned bool IsBasicAuth bool - Repo *Repository - Org *Organization + ContextUser *user_model.User + Repo *Repository + Org *Organization } // TrHTMLEscapeArgs runs Tr but pre-escapes all arguments with html.EscapeString. diff --git a/modules/context/org.go b/modules/context/org.go index 585a5fd762c6..0315c5b2dfad 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -53,7 +53,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { var err error ctx.Org.Organization, err = models.GetOrgByName(orgName) if err != nil { - if user_model.IsErrUserNotExist(err) { + if models.IsErrOrgNotExist(err) { redirectUserID, err := user_model.LookupUserRedirect(orgName) if err == nil { RedirectToUser(ctx, orgName, redirectUserID) @@ -68,6 +68,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { return } org := ctx.Org.Organization + ctx.ContextUser = org.AsUser() ctx.Data["Org"] = org teams, err := org.LoadTeams() diff --git a/modules/context/repo.go b/modules/context/repo.go index 76fe1c5676a5..7713ba32af85 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -438,6 +438,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { } } ctx.Repo.Owner = owner + ctx.ContextUser = owner ctx.Data["Username"] = ctx.Repo.Owner.Name // Get repository. diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index aaa27afb9e9b..b2bfe0dc0293 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -15,7 +15,6 @@ import ( "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" - "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -45,11 +44,8 @@ func CreateOrg(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateOrgOption) - u := user.GetUserByParams(ctx) - if ctx.Written() { - return - } visibility := api.VisibleTypePublic if form.Visibility != "" { @@ -67,7 +63,7 @@ func CreateOrg(ctx *context.APIContext) { Visibility: visibility, } - if err := models.CreateOrganization(org, u); err != nil { + if err := models.CreateOrganization(org, ctx.ContextUser); err != nil { if user_model.IsErrUserAlreadyExist(err) || db.IsErrNameReserved(err) || db.IsErrNameCharsNotAllowed(err) || diff --git a/routers/api/v1/admin/repo.go b/routers/api/v1/admin/repo.go index 467f8a22ffac..712ced89c99e 100644 --- a/routers/api/v1/admin/repo.go +++ b/routers/api/v1/admin/repo.go @@ -9,7 +9,6 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/repo" - "code.gitea.io/gitea/routers/api/v1/user" ) // CreateRepo api for creating a repository @@ -42,11 +41,8 @@ func CreateRepo(ctx *context.APIContext) { // "$ref": "#/responses/error" // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateRepoOption) - owner := user.GetUserByParams(ctx) - if ctx.Written() { - return - } - repo.CreateUserRepo(ctx, owner, *form) + repo.CreateUserRepo(ctx, ctx.ContextUser, *form) } diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 0ecebad5d7aa..11b7cbb95bd7 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -73,6 +73,7 @@ func CreateUser(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateUserOption) u := &user_model.User{ @@ -162,13 +163,10 @@ func EditUser(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.EditUserOption) - u := user.GetUserByParams(ctx) - if ctx.Written() { - return - } - parseAuthSource(ctx, u, form.SourceID, form.LoginName) + parseAuthSource(ctx, ctx.ContextUser, form.SourceID, form.LoginName) if ctx.Written() { return } @@ -192,24 +190,24 @@ func EditUser(ctx *context.APIContext) { ctx.Error(http.StatusBadRequest, "PasswordPwned", errors.New("PasswordPwned")) return } - if u.Salt, err = user_model.GetUserSalt(); err != nil { + if ctx.ContextUser.Salt, err = user_model.GetUserSalt(); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateUser", err) return } - if err = u.SetPassword(form.Password); err != nil { + if err = ctx.ContextUser.SetPassword(form.Password); err != nil { ctx.InternalServerError(err) return } } if form.MustChangePassword != nil { - u.MustChangePassword = *form.MustChangePassword + ctx.ContextUser.MustChangePassword = *form.MustChangePassword } - u.LoginName = form.LoginName + ctx.ContextUser.LoginName = form.LoginName if form.FullName != nil { - u.FullName = *form.FullName + ctx.ContextUser.FullName = *form.FullName } var emailChanged bool if form.Email != nil { @@ -224,47 +222,47 @@ func EditUser(ctx *context.APIContext) { return } - emailChanged = !strings.EqualFold(u.Email, email) - u.Email = email + emailChanged = !strings.EqualFold(ctx.ContextUser.Email, email) + ctx.ContextUser.Email = email } if form.Website != nil { - u.Website = *form.Website + ctx.ContextUser.Website = *form.Website } if form.Location != nil { - u.Location = *form.Location + ctx.ContextUser.Location = *form.Location } if form.Description != nil { - u.Description = *form.Description + ctx.ContextUser.Description = *form.Description } if form.Active != nil { - u.IsActive = *form.Active + ctx.ContextUser.IsActive = *form.Active } if len(form.Visibility) != 0 { - u.Visibility = api.VisibilityModes[form.Visibility] + ctx.ContextUser.Visibility = api.VisibilityModes[form.Visibility] } if form.Admin != nil { - u.IsAdmin = *form.Admin + ctx.ContextUser.IsAdmin = *form.Admin } if form.AllowGitHook != nil { - u.AllowGitHook = *form.AllowGitHook + ctx.ContextUser.AllowGitHook = *form.AllowGitHook } if form.AllowImportLocal != nil { - u.AllowImportLocal = *form.AllowImportLocal + ctx.ContextUser.AllowImportLocal = *form.AllowImportLocal } if form.MaxRepoCreation != nil { - u.MaxRepoCreation = *form.MaxRepoCreation + ctx.ContextUser.MaxRepoCreation = *form.MaxRepoCreation } if form.AllowCreateOrganization != nil { - u.AllowCreateOrganization = *form.AllowCreateOrganization + ctx.ContextUser.AllowCreateOrganization = *form.AllowCreateOrganization } if form.ProhibitLogin != nil { - u.ProhibitLogin = *form.ProhibitLogin + ctx.ContextUser.ProhibitLogin = *form.ProhibitLogin } if form.Restricted != nil { - u.IsRestricted = *form.Restricted + ctx.ContextUser.IsRestricted = *form.Restricted } - if err := user_model.UpdateUser(u, emailChanged); err != nil { + if err := user_model.UpdateUser(ctx.ContextUser, emailChanged); err != nil { if user_model.IsErrEmailAlreadyUsed(err) || user_model.IsErrEmailInvalid(err) { ctx.Error(http.StatusUnprocessableEntity, "", err) } else { @@ -272,9 +270,9 @@ func EditUser(ctx *context.APIContext) { } return } - log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name) + log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, ctx.ContextUser.Name) - ctx.JSON(http.StatusOK, convert.ToUser(u, ctx.User)) + ctx.JSON(http.StatusOK, convert.ToUser(ctx.ContextUser, ctx.User)) } // DeleteUser api for deleting a user @@ -298,17 +296,12 @@ func DeleteUser(ctx *context.APIContext) { // "422": // "$ref": "#/responses/validationError" - u := user.GetUserByParams(ctx) - if ctx.Written() { - return - } - - if u.IsOrganization() { - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", u.Name)) + if ctx.ContextUser.IsOrganization() { + ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", ctx.ContextUser.Name)) return } - if err := user_service.DeleteUser(u); err != nil { + if err := user_service.DeleteUser(ctx.ContextUser); err != nil { if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) { ctx.Error(http.StatusUnprocessableEntity, "", err) @@ -317,7 +310,7 @@ func DeleteUser(ctx *context.APIContext) { } return } - log.Trace("Account deleted by admin(%s): %s", ctx.User.Name, u.Name) + log.Trace("Account deleted by admin(%s): %s", ctx.User.Name, ctx.ContextUser.Name) ctx.Status(http.StatusNoContent) } @@ -348,12 +341,10 @@ func CreatePublicKey(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateKeyOption) - u := user.GetUserByParams(ctx) - if ctx.Written() { - return - } - user.CreateUserPublicKey(ctx, *form, u.ID) + + user.CreateUserPublicKey(ctx, *form, ctx.ContextUser.ID) } // DeleteUserPublicKey api for deleting a user's public key @@ -383,12 +374,7 @@ func DeleteUserPublicKey(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - u := user.GetUserByParams(ctx) - if ctx.Written() { - return - } - - if err := asymkey_service.DeletePublicKey(u, ctx.ParamsInt64(":id")); err != nil { + if err := asymkey_service.DeletePublicKey(ctx.ContextUser, ctx.ParamsInt64(":id")); err != nil { if asymkey_model.IsErrKeyNotExist(err) { ctx.NotFound() } else if asymkey_model.IsErrKeyAccessDenied(err) { @@ -398,7 +384,7 @@ func DeleteUserPublicKey(ctx *context.APIContext) { } return } - log.Trace("Key deleted by admin(%s): %s", ctx.User.Name, u.Name) + log.Trace("Key deleted by admin(%s): %s", ctx.User.Name, ctx.ContextUser.Name) ctx.Status(http.StatusNoContent) } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 6d8ab8ce98fd..caab60ec56db 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -87,6 +87,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/settings" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/services/auth" + context_service "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation @@ -156,6 +157,7 @@ func repoAssignment() func(ctx *context.APIContext) { } } ctx.Repo.Owner = owner + ctx.ContextUser = owner // Get repository. repo, err := repo_model.GetRepositoryByName(owner.ID, repoName) @@ -441,6 +443,7 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) { } return } + ctx.ContextUser = ctx.Org.Organization.AsUser() } if assignTeam { @@ -636,7 +639,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken) m.Combo("/{id}").Delete(user.DeleteAccessToken) }, reqBasicOrRevProxyAuth()) - }) + }, context_service.UserAssignmentAPI()) }) m.Group("/users", func() { @@ -653,7 +656,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { m.Get("/starred", user.GetStarredRepos) m.Get("/subscriptions", user.GetWatchedRepos) - }) + }, context_service.UserAssignmentAPI()) }, reqToken()) m.Group("/user", func() { @@ -669,7 +672,11 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { m.Get("/followers", user.ListMyFollowers) m.Group("/following", func() { m.Get("", user.ListMyFollowing) - m.Combo("/{username}").Get(user.CheckMyFollowing).Put(user.Follow).Delete(user.Unfollow) + m.Group("/{username}", func() { + m.Get("", user.CheckMyFollowing) + m.Put("", user.Follow) + m.Delete("", user.Unfollow) + }, context_service.UserAssignmentAPI()) }) m.Group("/keys", func() { @@ -1004,7 +1011,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { m.Group("/users/{username}/orgs", func() { m.Get("", org.ListUserOrgs) m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) - }) + }, context_service.UserAssignmentAPI()) m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create) m.Get("/orgs", org.GetAll) m.Group("/orgs/{org}", func() { @@ -1082,7 +1089,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { m.Get("/orgs", org.ListUserOrgs) m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg) m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo) - }) + }, context_service.UserAssignmentAPI()) }) m.Group("/unadopted", func() { m.Get("", admin.ListUnadoptedRepositories) diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 1f097225f229..8689e04c3953 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -99,11 +99,7 @@ func ListUserOrgs(ctx *context.APIContext) { // "200": // "$ref": "#/responses/OrganizationList" - u := user.GetUserByParams(ctx) - if ctx.Written() { - return - } - listUserOrgs(ctx, u) + listUserOrgs(ctx, ctx.ContextUser) } // GetUserOrgsPermissions get user permissions in organization @@ -132,11 +128,6 @@ func GetUserOrgsPermissions(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - var u *user_model.User - if u = user.GetUserByParams(ctx); u == nil { - return - } - var o *user_model.User if o = user.GetUserByParamsName(ctx, ":org"); o == nil { return @@ -144,13 +135,13 @@ func GetUserOrgsPermissions(ctx *context.APIContext) { op := api.OrganizationPermissions{} - if !models.HasOrgOrUserVisible(o, u) { + if !models.HasOrgOrUserVisible(o, ctx.ContextUser) { ctx.NotFound("HasOrgOrUserVisible", nil) return } org := models.OrgFromUser(o) - authorizeLevel, err := org.GetOrgUserMaxAuthorizeLevel(u.ID) + authorizeLevel, err := org.GetOrgUserMaxAuthorizeLevel(ctx.ContextUser.ID) if err != nil { ctx.Error(http.StatusInternalServerError, "GetOrgUserAuthorizeLevel", err) return @@ -169,7 +160,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) { op.IsOwner = true } - op.CanCreateRepository, err = org.CanCreateOrgRepo(u.ID) + op.CanCreateRepository, err = org.CanCreateOrgRepo(ctx.ContextUser.ID) if err != nil { ctx.Error(http.StatusInternalServerError, "CanCreateOrgRepo", err) return diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 1eacb89db2bf..e7f25108cfc7 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -82,11 +82,7 @@ func ListFollowers(ctx *context.APIContext) { // "200": // "$ref": "#/responses/UserList" - u := GetUserByParams(ctx) - if ctx.Written() { - return - } - listUserFollowers(ctx, u) + listUserFollowers(ctx, ctx.ContextUser) } func listUserFollowing(ctx *context.APIContext, u *user_model.User) { @@ -148,11 +144,7 @@ func ListFollowing(ctx *context.APIContext) { // "200": // "$ref": "#/responses/UserList" - u := GetUserByParams(ctx) - if ctx.Written() { - return - } - listUserFollowing(ctx, u) + listUserFollowing(ctx, ctx.ContextUser) } func checkUserFollowing(ctx *context.APIContext, u *user_model.User, followID int64) { @@ -180,25 +172,21 @@ func CheckMyFollowing(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - target := GetUserByParams(ctx) - if ctx.Written() { - return - } - checkUserFollowing(ctx, ctx.User, target.ID) + checkUserFollowing(ctx, ctx.User, ctx.ContextUser.ID) } // CheckFollowing check if one user is following another user func CheckFollowing(ctx *context.APIContext) { - // swagger:operation GET /users/{follower}/following/{followee} user userCheckFollowing + // swagger:operation GET /users/{username}/following/{target} user userCheckFollowing // --- // summary: Check if one user is following another user // parameters: - // - name: follower + // - name: username // in: path // description: username of following user // type: string // required: true - // - name: followee + // - name: target // in: path // description: username of followed user // type: string @@ -209,15 +197,11 @@ func CheckFollowing(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - u := GetUserByParams(ctx) - if ctx.Written() { - return - } target := GetUserByParamsName(ctx, ":target") if ctx.Written() { return } - checkUserFollowing(ctx, u, target.ID) + checkUserFollowing(ctx, ctx.ContextUser, target.ID) } // Follow follow a user @@ -235,11 +219,7 @@ func Follow(ctx *context.APIContext) { // "204": // "$ref": "#/responses/empty" - target := GetUserByParams(ctx) - if ctx.Written() { - return - } - if err := user_model.FollowUser(ctx.User.ID, target.ID); err != nil { + if err := user_model.FollowUser(ctx.User.ID, ctx.ContextUser.ID); err != nil { ctx.Error(http.StatusInternalServerError, "FollowUser", err) return } @@ -261,11 +241,7 @@ func Unfollow(ctx *context.APIContext) { // "204": // "$ref": "#/responses/empty" - target := GetUserByParams(ctx) - if ctx.Written() { - return - } - if err := user_model.UnfollowUser(ctx.User.ID, target.ID); err != nil { + if err := user_model.UnfollowUser(ctx.User.ID, ctx.ContextUser.ID); err != nil { ctx.Error(http.StatusInternalServerError, "UnfollowUser", err) return } diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 26aeeeabf9d4..915c66a159e5 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -64,11 +64,7 @@ func ListGPGKeys(ctx *context.APIContext) { // "200": // "$ref": "#/responses/GPGKeyList" - user := GetUserByParams(ctx) - if ctx.Written() { - return - } - listGPGKeys(ctx, user.ID, utils.GetListOptions(ctx)) + listGPGKeys(ctx, ctx.ContextUser.ID, utils.GetListOptions(ctx)) } // ListMyGPGKeys get the GPG key list of the authenticated user diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index e8cc2035e5de..ce69f3f13a00 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -151,11 +151,7 @@ func ListPublicKeys(ctx *context.APIContext) { // "200": // "$ref": "#/responses/PublicKeyList" - user := GetUserByParams(ctx) - if ctx.Written() { - return - } - listPublicKeys(ctx, user) + listPublicKeys(ctx, ctx.ContextUser) } // GetPublicKey get a public key diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 109548ec768c..0342db9aaadd 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -79,12 +79,8 @@ func ListUserRepos(ctx *context.APIContext) { // "200": // "$ref": "#/responses/RepositoryList" - user := GetUserByParams(ctx) - if ctx.Written() { - return - } private := ctx.IsSigned - listUserRepos(ctx, user, private) + listUserRepos(ctx, ctx.ContextUser, private) } // ListMyRepos - list the repositories you own or have access to. diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index cc527d921369..fe17b73aff71 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -62,15 +62,14 @@ func GetStarredRepos(ctx *context.APIContext) { // "200": // "$ref": "#/responses/RepositoryList" - user := GetUserByParams(ctx) - private := user.ID == ctx.User.ID - repos, err := getStarredRepos(user, private, utils.GetListOptions(ctx)) + private := ctx.ContextUser.ID == ctx.User.ID + repos, err := getStarredRepos(ctx.ContextUser, private, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) return } - ctx.SetTotalCountHeader(int64(user.NumStars)) + ctx.SetTotalCountHeader(int64(ctx.ContextUser.NumStars)) ctx.JSON(http.StatusOK, &repos) } diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index bba7b7a5d148..2f5d604d3b63 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -98,18 +98,12 @@ func GetInfo(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - u := GetUserByParams(ctx) - - if ctx.Written() { - return - } - - if !models.IsUserVisibleToViewer(u, ctx.User) { + if !models.IsUserVisibleToViewer(ctx.ContextUser, ctx.User) { // fake ErrUserNotExist error message to not leak information about existence ctx.NotFound("GetUserByName", user_model.ErrUserNotExist{Name: ctx.Params(":username")}) return } - ctx.JSON(http.StatusOK, convert.ToUser(u, ctx.User)) + ctx.JSON(http.StatusOK, convert.ToUser(ctx.ContextUser, ctx.User)) } // GetAuthenticatedUser get current user's information @@ -145,12 +139,7 @@ func GetUserHeatmapData(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - user := GetUserByParams(ctx) - if ctx.Written() { - return - } - - heatmap, err := models.GetUserHeatmapDataByUser(user, ctx.User) + heatmap, err := models.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.User) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserHeatmapDataByUser", err) return diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index 49b1d47d95d2..4902513df899 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -60,9 +60,8 @@ func GetWatchedRepos(ctx *context.APIContext) { // "200": // "$ref": "#/responses/RepositoryList" - user := GetUserByParams(ctx) - private := user.ID == ctx.User.ID - repos, total, err := getWatchedRepos(user, private, utils.GetListOptions(ctx)) + private := ctx.ContextUser.ID == ctx.User.ID + repos, total, err := getWatchedRepos(ctx.ContextUser, private, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) } diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go index 1a7f4ad24b21..3e06b4019edb 100644 --- a/routers/web/feed/profile.go +++ b/routers/web/feed/profile.go @@ -51,10 +51,20 @@ func RetrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) []*mode return actions } -// ShowUserFeed show user activity as RSS / Atom feed -func ShowUserFeed(ctx *context.Context, ctxUser *user_model.User, formatType string) { +// ShowUserFeedRSS show user activity as RSS feed +func ShowUserFeedRSS(ctx *context.Context) { + showUserFeed(ctx, "rss") +} + +// ShowUserFeedAtom show user activity as Atom feed +func ShowUserFeedAtom(ctx *context.Context) { + showUserFeed(ctx, "atom") +} + +// showUserFeed show user activity as RSS / Atom feed +func showUserFeed(ctx *context.Context, formatType string) { actions := RetrieveFeeds(ctx, models.GetFeedsOptions{ - RequestedUser: ctxUser, + RequestedUser: ctx.ContextUser, Actor: ctx.User, IncludePrivate: false, OnlyPerformedBy: true, @@ -66,9 +76,9 @@ func ShowUserFeed(ctx *context.Context, ctxUser *user_model.User, formatType str } feed := &feeds.Feed{ - Title: ctx.Tr("home.feed_of", ctxUser.DisplayName()), - Link: &feeds.Link{Href: ctxUser.HTMLURL()}, - Description: ctxUser.Description, + Title: ctx.Tr("home.feed_of", ctx.ContextUser.DisplayName()), + Link: &feeds.Link{Href: ctx.ContextUser.HTMLURL()}, + Description: ctx.ContextUser.Description, Created: time.Now(), } diff --git a/routers/web/repo/http.go b/routers/web/repo/http.go index a27a60cb84c7..ea0fd4c4277f 100644 --- a/routers/web/repo/http.go +++ b/routers/web/repo/http.go @@ -25,7 +25,6 @@ import ( "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" - user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" @@ -111,19 +110,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { reponame = reponame[:len(reponame)-5] } - owner, err := user_model.GetUserByName(username) - if err != nil { - if user_model.IsErrUserNotExist(err) { - if redirectUserID, err := user_model.LookupUserRedirect(username); err == nil { - context.RedirectToUser(ctx, username, redirectUserID) - } else { - ctx.NotFound(fmt.Sprintf("User %s does not exist", username), nil) - } - } else { - ctx.ServerError("GetUserByName", err) - } - return - } + owner := ctx.ContextUser if !owner.IsOrganization() && !owner.IsActive { ctx.PlainText(http.StatusForbidden, "Repository cannot be accessed. You cannot push or open issues/pull-requests.") return diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 33512d97c06e..e1654ea45f5a 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -714,8 +714,8 @@ func loadRepoByIDs(ctxUser *user_model.User, issueCountByRepo map[int64]int64, u } // ShowSSHKeys output all the ssh keys of user by uid -func ShowSSHKeys(ctx *context.Context, uid int64) { - keys, err := asymkey_model.ListPublicKeys(uid, db.ListOptions{}) +func ShowSSHKeys(ctx *context.Context) { + keys, err := asymkey_model.ListPublicKeys(ctx.ContextUser.ID, db.ListOptions{}) if err != nil { ctx.ServerError("ListPublicKeys", err) return @@ -730,8 +730,8 @@ func ShowSSHKeys(ctx *context.Context, uid int64) { } // ShowGPGKeys output all the public GPG keys of user by uid -func ShowGPGKeys(ctx *context.Context, uid int64) { - keys, err := asymkey_model.ListGPGKeys(db.DefaultContext, uid, db.ListOptions{}) +func ShowGPGKeys(ctx *context.Context) { + keys, err := asymkey_model.ListGPGKeys(db.DefaultContext, ctx.ContextUser.ID, db.ListOptions{}) if err != nil { ctx.ServerError("ListGPGKeys", err) return diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 9c0ce10dae6e..5216cc29c337 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -8,7 +8,6 @@ package user import ( "fmt" "net/http" - "path" "strings" "code.gitea.io/gitea/models" @@ -24,76 +23,18 @@ import ( "code.gitea.io/gitea/routers/web/org" ) -// GetUserByName get user by name -func GetUserByName(ctx *context.Context, name string) *user_model.User { - user, err := user_model.GetUserByName(name) - if err != nil { - if user_model.IsErrUserNotExist(err) { - if redirectUserID, err := user_model.LookupUserRedirect(name); err == nil { - context.RedirectToUser(ctx, name, redirectUserID) - } else { - ctx.NotFound("GetUserByName", err) - } - } else { - ctx.ServerError("GetUserByName", err) - } - return nil - } - return user -} - -// GetUserByParams returns user whose name is presented in URL paramenter. -func GetUserByParams(ctx *context.Context) *user_model.User { - return GetUserByName(ctx, ctx.Params(":username")) -} - // Profile render user's profile page func Profile(ctx *context.Context) { - uname := ctx.Params(":username") - - // Special handle for FireFox requests favicon.ico. - if uname == "favicon.ico" { - ctx.ServeFile(path.Join(setting.StaticRootPath, "public/img/favicon.png")) - return - } - - if strings.HasSuffix(uname, ".png") { - ctx.Error(http.StatusNotFound) + if strings.Contains(ctx.Req.Header.Get("Accept"), "application/rss+xml") { + feed.ShowUserFeedRSS(ctx) return } - - isShowKeys := false - if strings.HasSuffix(uname, ".keys") { - isShowKeys = true - uname = strings.TrimSuffix(uname, ".keys") - } - - isShowGPG := false - if strings.HasSuffix(uname, ".gpg") { - isShowGPG = true - uname = strings.TrimSuffix(uname, ".gpg") - } - - showFeedType := "" - if strings.HasSuffix(uname, ".rss") { - showFeedType = "rss" - uname = strings.TrimSuffix(uname, ".rss") - } else if strings.Contains(ctx.Req.Header.Get("Accept"), "application/rss+xml") { - showFeedType = "rss" - } - if strings.HasSuffix(uname, ".atom") { - showFeedType = "atom" - uname = strings.TrimSuffix(uname, ".atom") - } else if strings.Contains(ctx.Req.Header.Get("Accept"), "application/atom+xml") { - showFeedType = "atom" - } - - ctxUser := GetUserByName(ctx, uname) - if ctx.Written() { + if strings.Contains(ctx.Req.Header.Get("Accept"), "application/atom+xml") { + feed.ShowUserFeedAtom(ctx) return } - if ctxUser.IsOrganization() { + if ctx.ContextUser.IsOrganization() { /* // TODO: enable after rss.RetrieveFeeds() do handle org correctly // Show Org RSS feed @@ -108,49 +49,31 @@ func Profile(ctx *context.Context) { } // check view permissions - if !models.IsUserVisibleToViewer(ctxUser, ctx.User) { - ctx.NotFound("user", fmt.Errorf(uname)) - return - } - - // Show SSH keys. - if isShowKeys { - ShowSSHKeys(ctx, ctxUser.ID) - return - } - - // Show GPG keys. - if isShowGPG { - ShowGPGKeys(ctx, ctxUser.ID) - return - } - - // Show User RSS feed - if len(showFeedType) != 0 { - feed.ShowUserFeed(ctx, ctxUser, showFeedType) + if !models.IsUserVisibleToViewer(ctx.ContextUser, ctx.User) { + ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name)) return } // Show OpenID URIs - openIDs, err := user_model.GetUserOpenIDs(ctxUser.ID) + openIDs, err := user_model.GetUserOpenIDs(ctx.ContextUser.ID) if err != nil { ctx.ServerError("GetUserOpenIDs", err) return } var isFollowing bool - if ctx.User != nil && ctxUser != nil { - isFollowing = user_model.IsFollowing(ctx.User.ID, ctxUser.ID) + if ctx.User != nil { + isFollowing = user_model.IsFollowing(ctx.User.ID, ctx.ContextUser.ID) } - ctx.Data["Title"] = ctxUser.DisplayName() + ctx.Data["Title"] = ctx.ContextUser.DisplayName() ctx.Data["PageIsUserProfile"] = true - ctx.Data["Owner"] = ctxUser + ctx.Data["Owner"] = ctx.ContextUser ctx.Data["OpenIDs"] = openIDs ctx.Data["IsFollowing"] = isFollowing if setting.Service.EnableUserHeatmap { - data, err := models.GetUserHeatmapDataByUser(ctxUser, ctx.User) + data, err := models.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.User) if err != nil { ctx.ServerError("GetUserHeatmapDataByUser", err) return @@ -158,13 +81,13 @@ func Profile(ctx *context.Context) { ctx.Data["HeatmapData"] = data } - if len(ctxUser.Description) != 0 { + if len(ctx.ContextUser.Description) != 0 { content, err := markdown.RenderString(&markup.RenderContext{ URLPrefix: ctx.Repo.RepoLink, Metas: map[string]string{"mode": "document"}, GitRepo: ctx.Repo.GitRepo, Ctx: ctx, - }, ctxUser.Description) + }, ctx.ContextUser.Description) if err != nil { ctx.ServerError("RenderString", err) return @@ -172,10 +95,10 @@ func Profile(ctx *context.Context) { ctx.Data["RenderedDescription"] = content } - showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == ctxUser.ID) + showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == ctx.ContextUser.ID) orgs, err := models.FindOrgs(models.FindOrgOptions{ - UserID: ctxUser.ID, + UserID: ctx.ContextUser.ID, IncludePrivate: showPrivate, }) if err != nil { @@ -238,7 +161,7 @@ func Profile(ctx *context.Context) { switch tab { case "followers": - items, err := user_model.GetUserFollowers(ctxUser, db.ListOptions{ + items, err := user_model.GetUserFollowers(ctx.ContextUser, db.ListOptions{ PageSize: setting.UI.User.RepoPagingNum, Page: page, }) @@ -248,9 +171,9 @@ func Profile(ctx *context.Context) { } ctx.Data["Cards"] = items - total = ctxUser.NumFollowers + total = ctx.ContextUser.NumFollowers case "following": - items, err := user_model.GetUserFollowing(ctxUser, db.ListOptions{ + items, err := user_model.GetUserFollowing(ctx.ContextUser, db.ListOptions{ PageSize: setting.UI.User.RepoPagingNum, Page: page, }) @@ -260,10 +183,10 @@ func Profile(ctx *context.Context) { } ctx.Data["Cards"] = items - total = ctxUser.NumFollowing + total = ctx.ContextUser.NumFollowing case "activity": ctx.Data["Feeds"] = feed.RetrieveFeeds(ctx, models.GetFeedsOptions{ - RequestedUser: ctxUser, + RequestedUser: ctx.ContextUser, Actor: ctx.User, IncludePrivate: showPrivate, OnlyPerformedBy: true, @@ -284,7 +207,7 @@ func Profile(ctx *context.Context) { Keyword: keyword, OrderBy: orderBy, Private: ctx.IsSigned, - StarredByID: ctxUser.ID, + StarredByID: ctx.ContextUser.ID, Collaborate: util.OptionalBoolFalse, TopicOnly: topicOnly, Language: language, @@ -316,7 +239,7 @@ func Profile(ctx *context.Context) { Keyword: keyword, OrderBy: orderBy, Private: ctx.IsSigned, - WatchedByID: ctxUser.ID, + WatchedByID: ctx.ContextUser.ID, Collaborate: util.OptionalBoolFalse, TopicOnly: topicOnly, Language: language, @@ -336,7 +259,7 @@ func Profile(ctx *context.Context) { }, Actor: ctx.User, Keyword: keyword, - OwnerID: ctxUser.ID, + OwnerID: ctx.ContextUser.ID, OrderBy: orderBy, Private: ctx.IsSigned, Collaborate: util.OptionalBoolFalse, @@ -361,24 +284,19 @@ func Profile(ctx *context.Context) { } ctx.Data["Page"] = pager - ctx.Data["ShowUserEmail"] = len(ctxUser.Email) > 0 && ctx.IsSigned && (!ctxUser.KeepEmailPrivate || ctxUser.ID == ctx.User.ID) + ctx.Data["ShowUserEmail"] = len(ctx.ContextUser.Email) > 0 && ctx.IsSigned && (!ctx.ContextUser.KeepEmailPrivate || ctx.ContextUser.ID == ctx.User.ID) ctx.HTML(http.StatusOK, tplProfile) } // Action response for follow/unfollow user request func Action(ctx *context.Context) { - u := GetUserByParams(ctx) - if ctx.Written() { - return - } - var err error switch ctx.FormString("action") { case "follow": - err = user_model.FollowUser(ctx.User.ID, u.ID) + err = user_model.FollowUser(ctx.User.ID, ctx.ContextUser.ID) case "unfollow": - err = user_model.UnfollowUser(ctx.User.ID, u.ID) + err = user_model.UnfollowUser(ctx.User.ID, ctx.ContextUser.ID) } if err != nil { @@ -386,5 +304,5 @@ func Action(ctx *context.Context) { return } // FIXME: We should check this URL and make sure that it's a valid Gitea URL - ctx.RedirectToFirst(ctx.FormString("redirect_to"), u.HomeLink()) + ctx.RedirectToFirst(ctx.FormString("redirect_to"), ctx.ContextUser.HomeLink()) } diff --git a/routers/web/web.go b/routers/web/web.go index d8c197fb967e..c8e884dd733e 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -29,12 +29,14 @@ import ( "code.gitea.io/gitea/routers/web/dev" "code.gitea.io/gitea/routers/web/events" "code.gitea.io/gitea/routers/web/explore" + "code.gitea.io/gitea/routers/web/feed" "code.gitea.io/gitea/routers/web/org" "code.gitea.io/gitea/routers/web/repo" "code.gitea.io/gitea/routers/web/user" user_setting "code.gitea.io/gitea/routers/web/user/setting" "code.gitea.io/gitea/routers/web/user/setting/security" auth_service "code.gitea.io/gitea/services/auth" + context_service "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/lfs" "code.gitea.io/gitea/services/mailer" @@ -491,11 +493,21 @@ func RegisterRoutes(m *web.Route) { // ***** END: Admin ***** m.Group("", func() { - m.Get("/{username}", user.Profile) + m.Get("/favicon.ico", func(ctx *context.Context) { + ctx.ServeFile(path.Join(setting.StaticRootPath, "public/img/favicon.png")) + }) + m.Group("/{username}", func() { + m.Get(".png", func(ctx *context.Context) { ctx.Error(http.StatusNotFound) }) + m.Get(".keys", user.ShowSSHKeys) + m.Get(".gpg", user.ShowGPGKeys) + m.Get(".rss", feed.ShowUserFeedRSS) + m.Get(".atom", feed.ShowUserFeedAtom) + m.Get("", user.Profile) + }, context_service.UserAssignmentWeb()) m.Get("/attachments/{uuid}", repo.GetAttachment) }, ignSignIn) - m.Post("/{username}", reqSignIn, user.Action) + m.Post("/{username}", reqSignIn, context_service.UserAssignmentWeb(), user.Action) if !setting.IsProd { m.Get("/template/*", dev.TemplatePreview) @@ -1101,7 +1113,7 @@ func RegisterRoutes(m *web.Route) { m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) - }, ignSignInAndCsrf) + }, ignSignInAndCsrf, context_service.UserAssignmentWeb()) }) }) // ***** END: Repository ***** diff --git a/services/context/user.go b/services/context/user.go new file mode 100644 index 000000000000..ddb518a34177 --- /dev/null +++ b/services/context/user.go @@ -0,0 +1,62 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package context + +import ( + "fmt" + "net/http" + "strings" + + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/context" +) + +// UserAssignmentWeb returns a middleware to handle context-user assignment for web routes +func UserAssignmentWeb() func(ctx *context.Context) { + return func(ctx *context.Context) { + userAssignment(ctx, func(status int, title string, obj interface{}) { + err, ok := obj.(error) + if !ok { + err = fmt.Errorf("%s", obj) + } + if status == http.StatusNotFound { + ctx.NotFound(title, err) + } else { + ctx.ServerError(title, err) + } + }) + } +} + +// UserAssignmentAPI returns a middleware to handle context-user assignment for api routes +func UserAssignmentAPI() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + userAssignment(ctx.Context, ctx.Error) + } +} + +func userAssignment(ctx *context.Context, errCb func(int, string, interface{})) { + username := ctx.Params(":username") + + if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(username) { + ctx.ContextUser = ctx.User + } else { + var err error + ctx.ContextUser, err = user_model.GetUserByName(username) + if err != nil { + if user_model.IsErrUserNotExist(err) { + if redirectUserID, err := user_model.LookupUserRedirect(username); err == nil { + context.RedirectToUser(ctx, username, redirectUserID) + } else if user_model.IsErrUserRedirectNotExist(err) { + errCb(http.StatusNotFound, "GetUserByName", err) + } else { + errCb(http.StatusInternalServerError, "LookupUserRedirect", err) + } + } else { + errCb(http.StatusInternalServerError, "GetUserByName", err) + } + } + } +} From 09618acdb7aded00f55a4958a6c0c12439bcce8a Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Thu, 17 Feb 2022 16:16:31 +0000 Subject: [PATCH 2/2] Fixed swagger. --- templates/swagger/v1_json.tmpl | 66 +++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 0b0b83ebbc16..c39d8a00681f 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -12045,39 +12045,6 @@ } } }, - "/users/{follower}/following/{followee}": { - "get": { - "tags": [ - "user" - ], - "summary": "Check if one user is following another user", - "operationId": "userCheckFollowing", - "parameters": [ - { - "type": "string", - "description": "username of following user", - "name": "follower", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "username of followed user", - "name": "followee", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "$ref": "#/responses/empty" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, "/users/{username}": { "get": { "produces": [ @@ -12183,6 +12150,39 @@ } } }, + "/users/{username}/following/{target}": { + "get": { + "tags": [ + "user" + ], + "summary": "Check if one user is following another user", + "operationId": "userCheckFollowing", + "parameters": [ + { + "type": "string", + "description": "username of following user", + "name": "username", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "username of followed user", + "name": "target", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/empty" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/users/{username}/gpg_keys": { "get": { "produces": [