feat(repo): enforce dot-prefixed repos as always-private system repos #77
@@ -303,6 +303,12 @@ func (repo *Repository) IsBeingCreated() bool {
|
||||
return repo.IsBeingMigrated()
|
||||
}
|
||||
|
||||
// IsSystemRepo returns true when the repository name starts with "."
|
||||
// System repositories are always private and cannot be made public.
|
||||
func (repo *Repository) IsSystemRepo() bool {
|
||||
return strings.HasPrefix(repo.Name, ".")
|
||||
}
|
||||
|
||||
// IsBroken indicates that repository is broken
|
||||
func (repo *Repository) IsBroken() bool {
|
||||
return repo.Status == RepositoryBroken
|
||||
|
||||
@@ -2496,6 +2496,7 @@
|
||||
"repo.settings.visibility.success": "Repository visibility changed.",
|
||||
"repo.settings.visibility.error": "An error occurred while trying to change the repo visibility.",
|
||||
"repo.settings.visibility.fork_error": "Can't change the visibility of a forked repo.",
|
||||
"repo.settings.visibility.system_repo_private": "System repositories (dot-prefixed names) are always private and cannot be made public.",
|
||||
"repo.settings.archive.button": "Archive Repo",
|
||||
"repo.settings.archive.header": "Archive This Repo",
|
||||
"repo.settings.archive.text": "Archiving the repo will make it entirely read-only. It will be hidden from the dashboard. Nobody (not even you!) will be able to make new commits, or open any issues or pull requests.",
|
||||
|
||||
@@ -139,7 +139,7 @@ func Migrate(ctx *context.APIContext) {
|
||||
CloneAddr: remoteAddr,
|
||||
RepoName: form.RepoName,
|
||||
Description: form.Description,
|
||||
Private: form.Private || setting.Repository.ForcePrivate,
|
||||
Private: form.Private || setting.Repository.ForcePrivate || strings.HasPrefix(form.RepoName, "."),
|
||||
Mirror: form.Mirror,
|
||||
LFS: form.LFS,
|
||||
LFSEndpoint: form.LFSEndpoint,
|
||||
@@ -174,7 +174,7 @@ func Migrate(ctx *context.APIContext) {
|
||||
Description: opts.Description,
|
||||
OriginalURL: form.CloneAddr,
|
||||
GitServiceType: gitServiceType,
|
||||
IsPrivate: opts.Private || setting.Repository.ForcePrivate,
|
||||
IsPrivate: opts.Private || setting.Repository.ForcePrivate || strings.HasPrefix(opts.RepoName, "."),
|
||||
IsMirror: opts.Mirror,
|
||||
Status: repo_model.RepositoryBeingMigrated,
|
||||
}, false)
|
||||
|
||||
@@ -253,6 +253,9 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
|
||||
return
|
||||
}
|
||||
|
||||
// System repos (dot-prefixed names) are always private.
|
||||
isPrivate := opt.Private || setting.Repository.ForcePrivate || strings.HasPrefix(opt.Name, ".")
|
||||
|
||||
repo, err := repo_service.CreateRepository(ctx, ctx.Doer, owner, repo_service.CreateRepoOptions{
|
||||
Name: opt.Name,
|
||||
Description: opt.Description,
|
||||
@@ -260,7 +263,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
|
||||
Gitignores: opt.Gitignores,
|
||||
License: opt.License,
|
||||
Readme: opt.Readme,
|
||||
IsPrivate: opt.Private || setting.Repository.ForcePrivate,
|
||||
IsPrivate: isPrivate,
|
||||
AutoInit: opt.AutoInit,
|
||||
DefaultBranch: opt.DefaultBranch,
|
||||
TrustModel: repo_model.ToTrustModel(opt.TrustModel),
|
||||
@@ -700,6 +703,12 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
|
||||
}
|
||||
|
||||
visibilityChanged = repo.IsPrivate != *opts.Private
|
||||
// System repos (dot-prefixed) cannot be made public, regardless of user role.
|
||||
if visibilityChanged && !*opts.Private && repo.IsSystemRepo() {
|
||||
err := errors.New("system repositories (dot-prefixed) cannot be made public")
|
||||
ctx.APIError(http.StatusForbidden, err)
|
||||
return err
|
||||
}
|
||||
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
|
||||
if visibilityChanged && setting.Repository.ForcePrivate && !*opts.Private && !ctx.Doer.IsAdmin {
|
||||
err := errors.New("cannot change private repository to public")
|
||||
|
||||
@@ -210,10 +210,15 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
// FIXME: these options are not quite right, for example: changing visibility should do more works than just setting the is_private flag
|
||||
// These options should only be used for "push-to-create"
|
||||
if isPrivate.Has() && repo.IsPrivate != isPrivate.Value() {
|
||||
// TODO: it needs to do more work
|
||||
repo.IsPrivate = isPrivate.Value()
|
||||
if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to change visibility"})
|
||||
// System repos (dot-prefixed) are always private — ignore attempts to make them public.
|
||||
if !isPrivate.Value() && repo.IsSystemRepo() {
|
||||
log.Warn("Ignoring push option to make system repo %s public", repo.FullName())
|
||||
} else {
|
||||
// TODO: it needs to do more work
|
||||
repo.IsPrivate = isPrivate.Value()
|
||||
if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to change visibility"})
|
||||
}
|
||||
}
|
||||
}
|
||||
if isTemplate.Has() && repo.IsTemplate != isTemplate.Value() {
|
||||
|
||||
@@ -210,7 +210,7 @@ func MigratePost(ctx *context.Context) {
|
||||
CloneAddr: remoteAddr,
|
||||
RepoName: form.RepoName,
|
||||
Description: form.Description,
|
||||
Private: form.Private || setting.Repository.ForcePrivate,
|
||||
Private: form.Private || setting.Repository.ForcePrivate || strings.HasPrefix(form.RepoName, "."),
|
||||
Mirror: form.Mirror,
|
||||
LFS: form.LFS,
|
||||
LFSEndpoint: form.LFSEndpoint,
|
||||
|
||||
@@ -243,7 +243,7 @@ func CreatePost(ctx *context.Context) {
|
||||
opts := repo_service.GenerateRepoOptions{
|
||||
Name: form.RepoName,
|
||||
Description: form.Description,
|
||||
Private: form.Private || setting.Repository.ForcePrivate,
|
||||
Private: form.Private || setting.Repository.ForcePrivate || strings.HasPrefix(form.RepoName, "."),
|
||||
GitContent: form.GitContent,
|
||||
Topics: form.Topics,
|
||||
GitHooks: form.GitHooks,
|
||||
@@ -282,7 +282,7 @@ func CreatePost(ctx *context.Context) {
|
||||
IssueLabels: form.IssueLabels,
|
||||
License: form.License,
|
||||
Readme: form.Readme,
|
||||
IsPrivate: form.Private || setting.Repository.ForcePrivate,
|
||||
IsPrivate: form.Private || setting.Repository.ForcePrivate || strings.HasPrefix(form.RepoName, "."),
|
||||
DefaultBranch: form.DefaultBranch,
|
||||
AutoInit: form.AutoInit,
|
||||
IsTemplate: form.Template,
|
||||
|
||||
@@ -127,14 +127,22 @@ func repoUnitPublicAccesses(ctx *context.Context) []*repoUnitPublicAccess {
|
||||
func PublicAccess(ctx *context.Context) {
|
||||
ctx.Data["PageIsSettingsPublicAccess"] = true
|
||||
ctx.Data["RepoUnitPublicAccesses"] = repoUnitPublicAccesses(ctx)
|
||||
ctx.Data["GlobalForcePrivate"] = setting.Repository.ForcePrivate
|
||||
if setting.Repository.ForcePrivate {
|
||||
ctx.Data["GlobalForcePrivate"] = setting.Repository.ForcePrivate || ctx.Repo.Repository.IsSystemRepo()
|
||||
if ctx.Repo.Repository.IsSystemRepo() {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.visibility.system_repo_private"), true)
|
||||
} else if setting.Repository.ForcePrivate {
|
||||
ctx.Flash.Error(ctx.Tr("form.repository_force_private"), true)
|
||||
}
|
||||
ctx.HTML(http.StatusOK, tplRepoSettingsPublicAccess)
|
||||
}
|
||||
|
||||
func PublicAccessPost(ctx *context.Context) {
|
||||
// System repos (dot-prefixed) cannot have public access on any unit.
|
||||
if ctx.Repo.Repository.IsSystemRepo() {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.visibility.system_repo_private"), true)
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings/public_access")
|
||||
return
|
||||
}
|
||||
accesses := repoUnitPublicAccesses(ctx)
|
||||
for _, ua := range accesses {
|
||||
formVal := ctx.FormString(ua.FormKey)
|
||||
|
||||
@@ -54,7 +54,7 @@ const (
|
||||
func SettingsCtxData(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.settings.options")
|
||||
ctx.Data["PageIsSettingsOptions"] = true
|
||||
ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate
|
||||
ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate || ctx.Repo.Repository.IsSystemRepo()
|
||||
ctx.Data["MirrorsEnabled"] = setting.Mirror.Enabled
|
||||
ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull
|
||||
ctx.Data["DisableNewPushMirrors"] = setting.Mirror.DisableNewPush
|
||||
@@ -103,7 +103,7 @@ func Settings(ctx *context.Context) {
|
||||
|
||||
// SettingsPost response for changes of a repository
|
||||
func SettingsPost(ctx *context.Context) {
|
||||
ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate
|
||||
ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate || ctx.Repo.Repository.IsSystemRepo()
|
||||
ctx.Data["MirrorsEnabled"] = setting.Mirror.Enabled
|
||||
ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull
|
||||
ctx.Data["DisableNewPushMirrors"] = setting.Mirror.DisableNewPush
|
||||
@@ -1008,6 +1008,11 @@ func handleSettingsPostVisibility(ctx *context.Context) {
|
||||
|
||||
private := ctx.FormOptionalBool("private").ValueOrDefault(true) // default to true for privacy & safety
|
||||
|
||||
// System repos (dot-prefixed) cannot be made public, regardless of user role.
|
||||
if !private && repo.IsSystemRepo() {
|
||||
ctx.JSONError(ctx.Tr("repo.settings.visibility.system_repo_private"))
|
||||
return
|
||||
}
|
||||
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
|
||||
if !private && setting.Repository.ForcePrivate && !ctx.Doer.IsAdmin {
|
||||
ctx.JSONError(ctx.Tr("form.repository_force_private"))
|
||||
|
||||
@@ -123,6 +123,10 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili
|
||||
}
|
||||
|
||||
func MakeRepoPrivate(ctx context.Context, repo *repo_model.Repository, private bool) (err error) {
|
||||
// System repos (dot-prefixed) are always private and cannot be made public.
|
||||
if !private && repo.IsSystemRepo() {
|
||||
return errors.New("system repositories (dot-prefixed) cannot be made public")
|
||||
}
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
repo.IsPrivate = private
|
||||
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user