2
0

feat(repo): add public app integration toggle for repositories
All checks were successful
Build and Release / Create Release (push) Successful in 1s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 7m21s
Build and Release / Unit Tests (push) Successful in 7m26s
Build and Release / Lint (push) Successful in 7m47s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Successful in 3m12s
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Successful in 8h5m1s
Build and Release / Build Binaries (amd64, darwin, macos) (push) Successful in 6m1s
Build and Release / Build Binaries (arm64, darwin, macos) (push) Successful in 7m14s
Build and Release / Build Binary (linux/arm64) (push) Successful in 9m17s

Add repository setting to control anonymous access to app integration endpoints (issue submission, update checks). When enabled (default), the desktop app can access these endpoints without authentication. When disabled, vault token authentication is required.

This provides granular control over app integration access, allowing repository owners to enforce full authentication on sensitive repositories while maintaining ease of use for public/limited repos.

Changes include:
- New PublicAppIntegration boolean field on Repository model
- Database migration v365 to add the field (defaults to true)
- Repository settings UI to toggle the feature
- Updated checkVaultTokenForRepo to respect the setting
- Security enhancement: IssueStatusJSONEndpoint now only returns app-submitted issues to anonymous users
This commit is contained in:
2026-02-11 20:32:23 -05:00
parent dcceabc713
commit 7102167351
12 changed files with 153 additions and 61 deletions

View File

@@ -786,6 +786,64 @@ func RepoAssignment(ctx *Context) {
}
}
// RepoAssignmentForIntegration is a lightweight middleware that loads a repository
// without checking permissions, org visibility, or unit access. It is used for
// app integration endpoints (issue reporting, update checks) that handle their own
// authentication and must be accessible regardless of repo or org visibility settings.
func RepoAssignmentForIntegration(ctx *Context) {
var err error
userName := ctx.PathParam("username")
repoName := ctx.PathParam("reponame")
repoName = strings.TrimSuffix(repoName, ".git")
// Resolve owner
if ctx.IsSigned && strings.EqualFold(ctx.Doer.LowerName, userName) {
ctx.Repo.Owner = ctx.Doer
} else {
ctx.Repo.Owner, err = user_model.GetUserByName(ctx, userName)
if err != nil {
if user_model.IsErrUserNotExist(err) {
if redirectUserID, err := user_model.LookupUserRedirect(ctx, userName); err == nil {
RedirectToUser(ctx.Base, ctx.Doer, userName, redirectUserID)
} else {
ctx.NotFound(nil)
}
} else {
ctx.ServerError("GetUserByName", err)
}
return
}
}
ctx.ContextUser = ctx.Repo.Owner
// Resolve repo
repo, err := repo_model.GetRepositoryByName(ctx, ctx.Repo.Owner.ID, repoName)
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
if redirectRepoID, err := repo_model.LookupRedirect(ctx, ctx.Repo.Owner.ID, repoName); err == nil {
RedirectToRepo(ctx.Base, redirectRepoID)
} else {
ctx.NotFound(nil)
}
} else {
ctx.ServerError("GetRepositoryByName", err)
}
return
}
repo.Owner = ctx.Repo.Owner
if err = repo.LoadOwner(ctx); err != nil {
ctx.ServerError("LoadOwner", err)
return
}
ctx.Repo.Repository = repo
ctx.Repo.RepoLink = repo.Link()
ctx.Data["Repository"] = repo
ctx.Data["Owner"] = repo.Owner
ctx.Data["RepoLink"] = ctx.Repo.RepoLink
}
const headRefName = "HEAD"
func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool) string {

View File

@@ -126,6 +126,7 @@ type RepoSettingForm struct {
EnableCloseIssuesViaCommitInAnyBranch bool
HideDotfiles bool
EnableBlog bool
EnablePublicAppIntegration bool
EnableProjects bool
ProjectsMode string