mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Add ContextUser to http request context (#18798)
				
					
				
			This PR adds a middleware which sets a ContextUser (like GetUserByParams before) in a single place which can be used by other methods. For routes which represent a repo or org the respective middlewares set the field too. Also fix a bug in modules/context/org.go during refactoring.
This commit is contained in:
		@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -439,6 +439,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Repo.Owner = owner
 | 
			
		||||
	ctx.ContextUser = owner
 | 
			
		||||
	ctx.Data["Username"] = ctx.Repo.Owner.Name
 | 
			
		||||
 | 
			
		||||
	// redirect link to wiki
 | 
			
		||||
 
 | 
			
		||||
@@ -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) ||
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
	form := web.GetForm(ctx).(*api.CreateRepoOption)
 | 
			
		||||
 | 
			
		||||
	repo.CreateUserRepo(ctx, ctx.ContextUser, *form)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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{
 | 
			
		||||
@@ -163,13 +164,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)
 | 
			
		||||
	form := web.GetForm(ctx).(*api.EditUserOption)
 | 
			
		||||
 | 
			
		||||
	parseAuthSource(ctx, ctx.ContextUser, form.SourceID, form.LoginName)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -193,24 +191,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 {
 | 
			
		||||
@@ -225,47 +223,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.IsErrEmailCharIsNotSupported(err) ||
 | 
			
		||||
			user_model.IsErrEmailInvalid(err) {
 | 
			
		||||
@@ -275,9 +273,9 @@ func EditUser(ctx *context.APIContext) {
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	log.Trace("Account profile updated by admin (%s): %s", ctx.Doer.Name, u.Name)
 | 
			
		||||
	log.Trace("Account profile updated by admin (%s): %s", ctx.Doer.Name, ctx.ContextUser.Name)
 | 
			
		||||
 | 
			
		||||
	ctx.JSON(http.StatusOK, convert.ToUser(u, ctx.Doer))
 | 
			
		||||
	ctx.JSON(http.StatusOK, convert.ToUser(ctx.ContextUser, ctx.Doer))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteUser api for deleting a user
 | 
			
		||||
@@ -301,17 +299,12 @@ func DeleteUser(ctx *context.APIContext) {
 | 
			
		||||
	//   "422":
 | 
			
		||||
	//     "$ref": "#/responses/validationError"
 | 
			
		||||
 | 
			
		||||
	u := user.GetUserByParams(ctx)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
	if ctx.ContextUser.IsOrganization() {
 | 
			
		||||
		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", ctx.ContextUser.Name))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if u.IsOrganization() {
 | 
			
		||||
		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", u.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)
 | 
			
		||||
@@ -320,7 +313,7 @@ func DeleteUser(ctx *context.APIContext) {
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	log.Trace("Account deleted by admin(%s): %s", ctx.Doer.Name, u.Name)
 | 
			
		||||
	log.Trace("Account deleted by admin(%s): %s", ctx.Doer.Name, ctx.ContextUser.Name)
 | 
			
		||||
 | 
			
		||||
	ctx.Status(http.StatusNoContent)
 | 
			
		||||
}
 | 
			
		||||
@@ -351,12 +344,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
 | 
			
		||||
@@ -386,12 +377,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) {
 | 
			
		||||
@@ -401,7 +387,7 @@ func DeleteUserPublicKey(ctx *context.APIContext) {
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	log.Trace("Key deleted by admin(%s): %s", ctx.Doer.Name, u.Name)
 | 
			
		||||
	log.Trace("Key deleted by admin(%s): %s", ctx.Doer.Name, ctx.ContextUser.Name)
 | 
			
		||||
 | 
			
		||||
	ctx.Status(http.StatusNoContent)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
@@ -1005,7 +1012,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() {
 | 
			
		||||
@@ -1083,7 +1090,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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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.Doer, target.ID)
 | 
			
		||||
	checkUserFollowing(ctx, ctx.Doer, 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.Doer.ID, target.ID); err != nil {
 | 
			
		||||
	if err := user_model.FollowUser(ctx.Doer.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.Doer.ID, target.ID); err != nil {
 | 
			
		||||
	if err := user_model.UnfollowUser(ctx.Doer.ID, ctx.ContextUser.ID); err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "UnfollowUser", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -78,12 +78,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.
 | 
			
		||||
 
 | 
			
		||||
@@ -62,15 +62,14 @@ func GetStarredRepos(ctx *context.APIContext) {
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/RepositoryList"
 | 
			
		||||
 | 
			
		||||
	user := GetUserByParams(ctx)
 | 
			
		||||
	private := user.ID == ctx.Doer.ID
 | 
			
		||||
	repos, err := getStarredRepos(user, private, utils.GetListOptions(ctx))
 | 
			
		||||
	private := ctx.ContextUser.ID == ctx.Doer.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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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.Doer) {
 | 
			
		||||
	if !models.IsUserVisibleToViewer(ctx.ContextUser, ctx.Doer) {
 | 
			
		||||
		// 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.Doer))
 | 
			
		||||
	ctx.JSON(http.StatusOK, convert.ToUser(ctx.ContextUser, ctx.Doer))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.Doer)
 | 
			
		||||
	heatmap, err := models.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "GetUserHeatmapDataByUser", err)
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -60,9 +60,8 @@ func GetWatchedRepos(ctx *context.APIContext) {
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/RepositoryList"
 | 
			
		||||
 | 
			
		||||
	user := GetUserByParams(ctx)
 | 
			
		||||
	private := user.ID == ctx.Doer.ID
 | 
			
		||||
	repos, total, err := getWatchedRepos(user, private, utils.GetListOptions(ctx))
 | 
			
		||||
	private := ctx.ContextUser.ID == ctx.Doer.ID
 | 
			
		||||
	repos, total, err := getWatchedRepos(ctx.ContextUser, private, utils.GetListOptions(ctx))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,19 +9,28 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	user_model "code.gitea.io/gitea/models/user"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/feeds"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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, err := models.GetFeeds(ctx, models.GetFeedsOptions{
 | 
			
		||||
		RequestedUser:   ctxUser,
 | 
			
		||||
		RequestedUser:   ctx.ContextUser,
 | 
			
		||||
		Actor:           ctx.Doer,
 | 
			
		||||
		IncludePrivate:  false,
 | 
			
		||||
		OnlyPerformedBy: !ctxUser.IsOrganization(),
 | 
			
		||||
		OnlyPerformedBy: !ctx.ContextUser.IsOrganization(),
 | 
			
		||||
		IncludeDeleted:  false,
 | 
			
		||||
		Date:            ctx.FormString("date"),
 | 
			
		||||
	})
 | 
			
		||||
@@ -31,9 +40,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(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,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"
 | 
			
		||||
@@ -110,19 +109,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
 | 
			
		||||
 
 | 
			
		||||
@@ -756,8 +756,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
 | 
			
		||||
@@ -772,8 +772,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(ctx, uid, db.ListOptions{})
 | 
			
		||||
func ShowGPGKeys(ctx *context.Context) {
 | 
			
		||||
	keys, err := asymkey_model.ListGPGKeys(ctx, ctx.ContextUser.ID, db.ListOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("ListGPGKeys", err)
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ package user
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
@@ -24,121 +23,51 @@ 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"))
 | 
			
		||||
	if strings.Contains(ctx.Req.Header.Get("Accept"), "application/rss+xml") {
 | 
			
		||||
		feed.ShowUserFeedRSS(ctx)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if strings.Contains(ctx.Req.Header.Get("Accept"), "application/atom+xml") {
 | 
			
		||||
		feed.ShowUserFeedAtom(ctx)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strings.HasSuffix(uname, ".png") {
 | 
			
		||||
		ctx.Error(http.StatusNotFound)
 | 
			
		||||
		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")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isShowFeed, uname, showFeedType := feed.GetFeedType(uname, ctx.Req)
 | 
			
		||||
 | 
			
		||||
	ctxUser := GetUserByName(ctx, uname)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctxUser.IsOrganization() {
 | 
			
		||||
		// Show Org RSS feed
 | 
			
		||||
		if isShowFeed {
 | 
			
		||||
			feed.ShowUserFeed(ctx, ctxUser, showFeedType)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if ctx.ContextUser.IsOrganization() {
 | 
			
		||||
		org.Home(ctx)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check view permissions
 | 
			
		||||
	if !models.IsUserVisibleToViewer(ctxUser, ctx.Doer) {
 | 
			
		||||
		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 isShowFeed {
 | 
			
		||||
		feed.ShowUserFeed(ctx, ctxUser, showFeedType)
 | 
			
		||||
	if !models.IsUserVisibleToViewer(ctx.ContextUser, ctx.Doer) {
 | 
			
		||||
		ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// advertise feed via meta tag
 | 
			
		||||
	ctx.Data["FeedURL"] = ctxUser.HTMLURL()
 | 
			
		||||
	ctx.Data["FeedURL"] = ctx.ContextUser.HTMLURL()
 | 
			
		||||
 | 
			
		||||
	// 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.Doer != nil && ctxUser != nil {
 | 
			
		||||
		isFollowing = user_model.IsFollowing(ctx.Doer.ID, ctxUser.ID)
 | 
			
		||||
	if ctx.Doer != nil {
 | 
			
		||||
		isFollowing = user_model.IsFollowing(ctx.Doer.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.Doer)
 | 
			
		||||
		data, err := models.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.ServerError("GetUserHeatmapDataByUser", err)
 | 
			
		||||
			return
 | 
			
		||||
@@ -146,13 +75,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
 | 
			
		||||
@@ -160,10 +89,10 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
		ctx.Data["RenderedDescription"] = content
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctxUser.ID)
 | 
			
		||||
	showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
 | 
			
		||||
 | 
			
		||||
	orgs, err := models.FindOrgs(models.FindOrgOptions{
 | 
			
		||||
		UserID:         ctxUser.ID,
 | 
			
		||||
		UserID:         ctx.ContextUser.ID,
 | 
			
		||||
		IncludePrivate: showPrivate,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -226,7 +155,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,
 | 
			
		||||
		})
 | 
			
		||||
@@ -236,9 +165,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,
 | 
			
		||||
		})
 | 
			
		||||
@@ -248,10 +177,10 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Data["Cards"] = items
 | 
			
		||||
 | 
			
		||||
		total = ctxUser.NumFollowing
 | 
			
		||||
		total = ctx.ContextUser.NumFollowing
 | 
			
		||||
	case "activity":
 | 
			
		||||
		ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{
 | 
			
		||||
			RequestedUser:   ctxUser,
 | 
			
		||||
			RequestedUser:   ctx.ContextUser,
 | 
			
		||||
			Actor:           ctx.Doer,
 | 
			
		||||
			IncludePrivate:  showPrivate,
 | 
			
		||||
			OnlyPerformedBy: true,
 | 
			
		||||
@@ -273,7 +202,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,
 | 
			
		||||
@@ -305,7 +234,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,
 | 
			
		||||
@@ -325,7 +254,7 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
			},
 | 
			
		||||
			Actor:              ctx.Doer,
 | 
			
		||||
			Keyword:            keyword,
 | 
			
		||||
			OwnerID:            ctxUser.ID,
 | 
			
		||||
			OwnerID:            ctx.ContextUser.ID,
 | 
			
		||||
			OrderBy:            orderBy,
 | 
			
		||||
			Private:            ctx.IsSigned,
 | 
			
		||||
			Collaborate:        util.OptionalBoolFalse,
 | 
			
		||||
@@ -350,24 +279,19 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Data["Page"] = pager
 | 
			
		||||
 | 
			
		||||
	ctx.Data["ShowUserEmail"] = len(ctxUser.Email) > 0 && ctx.IsSigned && (!ctxUser.KeepEmailPrivate || ctxUser.ID == ctx.Doer.ID)
 | 
			
		||||
	ctx.Data["ShowUserEmail"] = len(ctx.ContextUser.Email) > 0 && ctx.IsSigned && (!ctx.ContextUser.KeepEmailPrivate || ctx.ContextUser.ID == ctx.Doer.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.Doer.ID, u.ID)
 | 
			
		||||
		err = user_model.FollowUser(ctx.Doer.ID, ctx.ContextUser.ID)
 | 
			
		||||
	case "unfollow":
 | 
			
		||||
		err = user_model.UnfollowUser(ctx.Doer.ID, u.ID)
 | 
			
		||||
		err = user_model.UnfollowUser(ctx.Doer.ID, ctx.ContextUser.ID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -375,5 +299,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())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
@@ -496,11 +498,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)
 | 
			
		||||
@@ -1107,7 +1119,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 *****
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								services/context/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								services/context/user.go
									
									
									
									
									
										Normal file
									
								
							@@ -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.Doer.LowerName == strings.ToLower(username) {
 | 
			
		||||
		ctx.ContextUser = ctx.Doer
 | 
			
		||||
	} 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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -12087,39 +12087,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": [
 | 
			
		||||
@@ -12225,6 +12192,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": [
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user