Files
gitea/routers/web/feed/profile.go
T
silverwindandwxiaoguang 5264304a34 refactor: carry the API token on the doer (#39690)
The token a request authenticated with now lives on the doer, so
permission and visibility checks apply its scope from the doer alone.
Replaces https://github.com/go-gitea/gitea/pull/39167, following
https://github.com/go-gitea/gitea/pull/39167#issuecomment-6063839619.

1. Public-only tokens get 404 instead of 403 for hidden users, orgs and
repos, like GitHub.
2. Org ownership, site admin, repo and org creation, generation and
transfer endpoints reject public-only tokens.
3. API pull request responses set `head.repo` to `null` and
`head.repo_id` to `-1` when the caller cannot see the head repository.
4. Public-only tokens see org memberships like anonymous callers, so
concealed members stay hidden.
5. Site admin access in activity feeds, follower lists, org members,
Actions run lists and packages needs a session or an `all`-scoped token.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-19 00:34:01 +00:00

104 lines
3.0 KiB
Go

// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package feed
import (
"time"
activities_model "gitea.dev/models/activities"
"gitea.dev/models/organization"
"gitea.dev/models/renderhelper"
user_model "gitea.dev/models/user"
"gitea.dev/modules/markup/markdown"
"gitea.dev/modules/setting"
"gitea.dev/services/context"
feed_service "gitea.dev/services/feed"
"github.com/gorilla/feeds"
)
// 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) {
isOrganisation := ctx.ContextUser.IsOrganization()
if !setting.Other.EnableFeed ||
isOrganisation && !organization.HasOrgOrUserVisible(ctx, ctx.ContextUser, ctx.Doer) ||
!isOrganisation && !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
ctx.NotFound(nil)
return
}
includePrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
if ctx.IsSigned && isOrganisation && !includePrivate {
// When feed is requested by a member of the organization,
// include the private repo's the member has access to.
isOrgMember, err := organization.IsOrganizationMember(ctx, ctx.ContextUser.ID, ctx.Doer.ID)
if err != nil {
ctx.ServerError("IsOrganizationMember", err)
return
}
includePrivate = isOrgMember
}
actions, _, err := feed_service.GetFeeds(ctx, activities_model.GetFeedsOptions{
RequestedUser: ctx.ContextUser,
Actor: ctx.Doer,
IncludePrivate: includePrivate,
OnlyPerformedBy: !isOrganisation,
IncludeDeleted: false,
Date: ctx.FormString("date"),
})
if err != nil {
ctx.ServerError("GetFeeds", err)
return
}
rctx := renderhelper.NewRenderContextSimpleDocument(ctx, ctx.ContextUser.HTMLURL(ctx))
ctxUserDescription, err := markdown.RenderString(rctx,
ctx.ContextUser.Description)
if err != nil {
ctx.ServerError("RenderString", err)
return
}
feed := &feeds.Feed{
Title: ctx.Locale.TrString("home.feed_of", ctx.ContextUser.DisplayName()),
Link: &feeds.Link{Href: ctx.ContextUser.HTMLURL(ctx)},
Description: string(ctxUserDescription),
Created: time.Now(),
}
feed.Items, err = feedActionsToFeedItems(ctx, actions)
if err != nil {
ctx.ServerError("convert feed", err)
return
}
writeFeed(ctx, feed, formatType)
}
// writeFeed write a feeds.Feed as atom or rss to ctx.Resp
func writeFeed(ctx *context.Context, feed *feeds.Feed, formatType string) {
if formatType == "atom" {
ctx.Resp.Header().Set("Content-Type", "application/atom+xml;charset=utf-8")
if err := feed.WriteAtom(ctx.Resp); err != nil {
ctx.ServerError("Render Atom failed", err)
}
} else {
ctx.Resp.Header().Set("Content-Type", "application/rss+xml;charset=utf-8")
if err := feed.WriteRss(ctx.Resp); err != nil {
ctx.ServerError("Render RSS failed", err)
}
}
}