Goit

Simple and lightweight Git web server
git clone http://git.omkov.net/Goit
Log | Tree | Refs | README | Download

AuthorJakob Wakeling <[email protected]>
Date2024-01-17 05:29:55
Commit778d467788ab9cebaa2c363dbafab8995ae70388
Parentc8b452e408c53b747de7f8bcc271cd34b8766d78

Add limited visibility option for repositories

Diffstat

M res/admin/repo_edit.html | 5 +++--
M res/repo/create.html | 5 +++--
M res/repo/edit.html | 5 +++--
M src/admin/repos.go | 18 ++++++++++--------
M src/goit/db.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
M src/goit/git.go | 6 +++---
M src/goit/goit.go | 8 ++++++--
M src/goit/index.go | 5 ++---
M src/goit/repo.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++------------------
M src/repo/commit.go | 2 +-
M src/repo/create.go | 16 ++++++++++------
M src/repo/download.go | 2 +-
M src/repo/edit.go | 16 +++++++++-------
M src/repo/file.go | 2 +-
M src/repo/log.go | 2 +-
M src/repo/raw.go | 2 +-
M src/repo/refs.go | 2 +-
M src/repo/tree.go | 2 +-

18 files changed, 155 insertions, 61 deletions

diff --git a/res/admin/repo_edit.html b/res/admin/repo_edit.html
index 610c1da..4d85d60 100644
--- a/res/admin/repo_edit.html
+++ b/res/admin/repo_edit.html
@@ -33,8 +33,9 @@
 						<td style="text-align: right;"><label for="visibility">Visibility:</label></td>
 						<td>
 							<select name="visibility">
-								<option value="public">Public</option>
-								<option value="private" {{if .Edit.IsPrivate}}selected{{end}}>Private</option>
+								<option value="public" {{if eq .Edit.Visibility "public"}}selected{{end}}>Public</option>
+								<option value="private" {{if eq .Edit.Visibility "private"}}selected{{end}}>Private</option>
+								<option value="limited" {{if eq .Edit.Visibility "limited"}}selected{{end}}>Limited</option>
 							</select>
 						</td>
 					</tr>
diff --git a/res/repo/create.html b/res/repo/create.html
index b06dada..40510d1 100644
--- a/res/repo/create.html
+++ b/res/repo/create.html
@@ -31,8 +31,9 @@
 						<td style="text-align: right;"><label for="visibility">Visibility</label></td>
 						<td>
 							<select name="visibility">
-								<option value="public">Public</option>
-								<option value="private" {{if .IsPrivate}}selected{{end}}>Private</option>
+								<option value="public" {{if eq .Visibility "public"}}selected{{end}}>Public</option>
+								<option value="private" {{if eq .Visibility "private"}}selected{{end}}>Private</option>
+								<option value="limited" {{if eq .Visibility "limited"}}selected{{end}}>Limited</option>
 							</select>
 						</td>
 					</tr>
diff --git a/res/repo/edit.html b/res/repo/edit.html
index a8537c9..fd43599 100644
--- a/res/repo/edit.html
+++ b/res/repo/edit.html
@@ -33,8 +33,9 @@
 						<td style="text-align: right;"><label for="visibility">Visibility</label></td>
 						<td>
 							<select name="visibility">
-								<option value="public">Public</option>
-								<option value="private" {{if .Edit.IsPrivate}}selected{{end}}>Private</option>
+								<option value="public" {{if eq .Edit.Visibility "public"}}selected{{end}}>Public</option>
+								<option value="private" {{if eq .Edit.Visibility "private"}}selected{{end}}>Private</option>
+								<option value="limited" {{if eq .Edit.Visibility "limited"}}selected{{end}}>Limited</option>
 							</select>
 						</td>
 					</tr>
diff --git a/src/admin/repos.go b/src/admin/repos.go
index 826d959..0ec15cb 100644
--- a/src/admin/repos.go
+++ b/src/admin/repos.go
@@ -58,7 +58,7 @@ func HandleRepos(w http.ResponseWriter, r *http.Request) {
 		}
 
 		data.Repos = append(data.Repos, row{
-			fmt.Sprint(r.Id), u.Name, r.Name, util.If(r.IsPrivate, "private", "public"), humanize.IBytes(size),
+			fmt.Sprint(r.Id), u.Name, r.Name, r.Visibility.String(), humanize.IBytes(size),
 		})
 	}
 
@@ -110,10 +110,10 @@ func HandleRepoEdit(w http.ResponseWriter, r *http.Request) {
 		Title, Name string
 
 		Edit struct {
-			Id, Owner, Name, Description string
-			DefaultBranch, Upstream      string
-			IsPrivate, IsMirror          bool
-			Message                      string
+			Id, Owner, Name, Description        string
+			DefaultBranch, Upstream, Visibility string
+			IsMirror                            bool
+			Message                             string
 		}
 
 		Transfer struct{ Owner, Message string }
@@ -133,7 +133,7 @@ func HandleRepoEdit(w http.ResponseWriter, r *http.Request) {
 	data.Edit.Description = repo.Description
 	data.Edit.DefaultBranch = repo.DefaultBranch
 	data.Edit.Upstream = repo.Upstream
-	data.Edit.IsPrivate = repo.IsPrivate
+	data.Edit.Visibility = repo.Visibility.String()
 	data.Edit.IsMirror = repo.IsMirror
 
 	if r.Method == http.MethodPost {
@@ -143,7 +143,7 @@ func HandleRepoEdit(w http.ResponseWriter, r *http.Request) {
 			data.Edit.Description = r.FormValue("description")
 			data.Edit.DefaultBranch = util.If(r.FormValue("branch") == "", "master", r.FormValue("branch"))
 			data.Edit.Upstream = r.FormValue("upstream")
-			data.Edit.IsPrivate = r.FormValue("visibility") == "private"
+			data.Edit.Visibility = r.FormValue("visibility")
 			data.Edit.IsMirror = r.FormValue("mirror") == "mirror"
 
 			if data.Edit.Name == "" {
@@ -158,9 +158,11 @@ func HandleRepoEdit(w http.ResponseWriter, r *http.Request) {
 				data.Edit.Message = "Name \"" + data.Edit.Name + "\" is taken"
 			} else if len(data.Edit.Description) > 256 {
 				data.Edit.Message = "Description cannot exceed 256 characters"
+			} else if visibility := goit.VisibilityFromString(data.Edit.Visibility); visibility == -1 {
+				data.Edit.Message = "Visibility \"" + data.Edit.Visibility + "\" is invalid"
 			} else if err := goit.UpdateRepo(repo.Id, goit.Repo{
 				Name: data.Edit.Name, Description: data.Edit.Description, DefaultBranch: data.Edit.DefaultBranch,
-				Upstream: data.Edit.Upstream, IsPrivate: data.Edit.IsPrivate, IsMirror: data.Edit.IsMirror,
+				Upstream: data.Edit.Upstream, Visibility: visibility, IsMirror: data.Edit.IsMirror,
 			}); err != nil {
 				log.Println("[/admin/repo/edit]", err.Error())
 				goit.HttpError(w, http.StatusInternalServerError)
diff --git a/src/goit/db.go b/src/goit/db.go
index e3abea7..2f88de6 100644
--- a/src/goit/db.go
+++ b/src/goit/db.go
@@ -4,6 +4,8 @@ import (
 	"database/sql"
 	"fmt"
 	"log"
+
+	"github.com/Jamozed/Goit/src/util"
 )
 
 /*
@@ -32,7 +34,7 @@ import (
 */
 
 func dbUpdate(db *sql.DB) error {
-	latestVersion := 2
+	latestVersion := 3
 
 	var version int
 	if err := db.QueryRow("PRAGMA user_version").Scan(&version); err != nil {
@@ -94,6 +96,51 @@ func dbUpdate(db *sql.DB) error {
 			}
 
 			version = 2
+
+		case 2: /* 2 -> 3 */
+			log.Println("Migrating database from version 2 to 3")
+
+			if _, err := db.Exec(
+				"ALTER TABLE repos ADD COLUMN visibility INTEGER NOT NULL DEFAULT 0",
+			); err != nil {
+				return err
+			}
+
+			/* Set values for each repo according to is_private */
+			var visibilities = map[int64]Visibility{}
+
+			if rows, err := db.Query("SELECT id, is_private FROM repos"); err != nil {
+				return err
+			} else {
+				for rows.Next() {
+					var id int64
+					var isPrivate bool
+
+					if err := rows.Scan(&id, &isPrivate); err != nil {
+						return err
+					}
+
+					visibilities[id] = util.If(isPrivate, Private, Public)
+				}
+
+				rows.Close()
+			}
+
+			for id, visibility := range visibilities {
+				if _, err := db.Exec(
+					"UPDATE repos SET visibility = ? WHERE id = ?", visibility, id,
+				); err != nil {
+					return err
+				}
+			}
+
+			/* Remove is_private column */
+			if _, err := db.Exec("ALTER TABLE repos DROP COLUMN is_private"); err != nil {
+				return err
+			}
+
+			version = 3
+
 		default: /* No required migrations */
 			goto done
 		}
diff --git a/src/goit/git.go b/src/goit/git.go
index d6b1737..5464296 100644
--- a/src/goit/git.go
+++ b/src/goit/git.go
@@ -103,7 +103,7 @@ func gitHttpBase(w http.ResponseWriter, r *http.Request, service string) *Repo {
 	}
 
 	/* Require authentication other than for public pull */
-	if repo == nil || repo.IsPrivate || service == "git-receive-pack" {
+	if repo == nil || repo.Visibility != Public || service == "git-receive-pack" {
 		username, password, ok := r.BasicAuth()
 		if !ok {
 			w.Header().Set("WWW-Authenticate", "Basic realm=\"git\"")
@@ -125,8 +125,8 @@ func gitHttpBase(w http.ResponseWriter, r *http.Request, service string) *Repo {
 			return nil
 		}
 
-		/* If the repo doesn't exist or isn't owned by the user */
-		if repo == nil || user.Id != repo.OwnerId {
+		/* If the repo doesn't exist or is private and not owned by the user */
+		if repo == nil || (repo.Visibility == Private && user.Id != repo.OwnerId) {
 			w.WriteHeader(http.StatusNotFound)
 			return nil
 		}
diff --git a/src/goit/goit.go b/src/goit/goit.go
index c96acb0..f2ed0c8 100644
--- a/src/goit/goit.go
+++ b/src/goit/goit.go
@@ -199,14 +199,18 @@ func Backup() error {
 	rows.Close()
 
 	/* Dump repositories */
-	rows, err = db.Query("SELECT id, owner_id, name, description, is_private FROM repos")
+	rows, err = db.Query(
+		"SELECT id, owner_id, name, description, default_branch, upstream, visibility, is_mirror FROM repos",
+	)
 	if err != nil {
 		return err
 	}
 
 	for rows.Next() {
 		r := Repo{}
-		if err := rows.Scan(&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.IsPrivate); err != nil {
+		if err := rows.Scan(
+			&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.Visibility, &r.IsMirror,
+		); err != nil {
 			return err
 		}
 
diff --git a/src/goit/index.go b/src/goit/index.go
index d2b7fcf..e127415 100644
--- a/src/goit/index.go
+++ b/src/goit/index.go
@@ -11,7 +11,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/Jamozed/Goit/src/util"
 	"github.com/go-git/go-git/v5"
 	"github.com/go-git/go-git/v5/plumbing"
 )
@@ -47,7 +46,7 @@ func HandleIndex(w http.ResponseWriter, r *http.Request) {
 
 	rtemp := repos[:0]
 	for _, repo := range repos {
-		if !repo.IsPrivate || (auth && user.Id == repo.OwnerId) {
+		if IsVisible(&repo, auth, user) {
 			rtemp = append(rtemp, repo)
 		}
 	}
@@ -84,7 +83,7 @@ func HandleIndex(w http.ResponseWriter, r *http.Request) {
 
 		data.Repos = append(data.Repos, row{
 			Name: repo.Name, Description: repo.Description, Owner: owner.Name,
-			Visibility: util.If(repo.IsPrivate, "private", "public"), LastCommit: lastCommit,
+			Visibility: repo.Visibility.String(), LastCommit: lastCommit,
 		})
 	}
 
diff --git a/src/goit/repo.go b/src/goit/repo.go
index 32efa11..6a7bf86 100644
--- a/src/goit/repo.go
+++ b/src/goit/repo.go
@@ -18,21 +18,46 @@ import (
 )
 
 type Repo struct {
-	Id            int64  `json:"id"`
-	OwnerId       int64  `json:"owner_id"`
-	Name          string `json:"name"`
-	Description   string `json:"description"`
-	DefaultBranch string `json:"default_branch"`
-	Upstream      string `json:"upstream"`
-	IsPrivate     bool   `json:"is_private"`
-	IsMirror      bool   `json:"is_mirror"`
+	Id            int64      `json:"id"`
+	OwnerId       int64      `json:"owner_id"`
+	Name          string     `json:"name"`
+	Description   string     `json:"description"`
+	DefaultBranch string     `json:"default_branch"`
+	Upstream      string     `json:"upstream"`
+	Visibility    Visibility `json:"visibility"`
+	IsMirror      bool       `json:"is_mirror"`
+}
+
+type Visibility int32
+
+const (
+	Public  Visibility = 0
+	Private Visibility = 1
+	Limited Visibility = 2
+)
+
+func VisibilityFromString(s string) Visibility {
+	switch strings.ToLower(s) {
+	case "public":
+		return Public
+	case "private":
+		return Private
+	case "limited":
+		return Limited
+	default:
+		return -1
+	}
+}
+
+func (v Visibility) String() string {
+	return [...]string{"public", "private", "limited"}[v]
 }
 
 func GetRepos() ([]Repo, error) {
 	repos := []Repo{}
 
 	rows, err := db.Query(
-		"SELECT id, owner_id, name, description, default_branch, upstream, is_private, is_mirror FROM repos",
+		"SELECT id, owner_id, name, description, default_branch, upstream, visibility, is_mirror FROM repos",
 	)
 	if err != nil {
 		return nil, err
@@ -43,7 +68,7 @@ func GetRepos() ([]Repo, error) {
 	for rows.Next() {
 		r := Repo{}
 		if err := rows.Scan(
-			&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.IsPrivate, &r.IsMirror,
+			&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.Visibility, &r.IsMirror,
 		); err != nil {
 			return nil, err
 		}
@@ -62,10 +87,10 @@ func GetRepo(rid int64) (*Repo, error) {
 	r := &Repo{}
 
 	if err := db.QueryRow(
-		`SELECT id, owner_id, name, description, default_branch, upstream, is_private, is_mirror FROM repos
+		`SELECT id, owner_id, name, description, default_branch, upstream, visibility, is_mirror FROM repos
 		WHERE id = ?`, rid,
 	).Scan(
-		&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.IsPrivate, &r.IsMirror,
+		&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.Visibility, &r.IsMirror,
 	); err != nil {
 		if !errors.Is(err, sql.ErrNoRows) {
 			return nil, err
@@ -81,10 +106,10 @@ func GetRepoByName(name string) (*Repo, error) {
 	r := &Repo{}
 
 	if err := db.QueryRow(
-		`SELECT id, owner_id, name, description, default_branch, upstream, is_private, is_mirror FROM repos
+		`SELECT id, owner_id, name, description, default_branch, upstream, visibility, is_mirror FROM repos
 		WHERE name = ?`, name,
 	).Scan(
-		&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.IsPrivate, &r.IsMirror,
+		&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.Visibility, &r.IsMirror,
 	); err != nil {
 		if !errors.Is(err, sql.ErrNoRows) {
 			return nil, err
@@ -103,9 +128,9 @@ func CreateRepo(repo Repo) (int64, error) {
 	}
 
 	res, err := tx.Exec(
-		`INSERT INTO repos (owner_id, name, name_lower, description, default_branch, upstream, is_private, is_mirror)
+		`INSERT INTO repos (owner_id, name, name_lower, description, default_branch, upstream, visibility, is_mirror)
 		VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, repo.OwnerId, repo.Name, strings.ToLower(repo.Name), repo.Description,
-		repo.DefaultBranch, repo.Upstream, repo.IsPrivate, repo.IsMirror,
+		repo.DefaultBranch, repo.Upstream, repo.Visibility, repo.IsMirror,
 	)
 	if err != nil {
 		tx.Rollback()
@@ -193,9 +218,9 @@ func UpdateRepo(rid int64, repo Repo) error {
 	}
 
 	if _, err := tx.Exec(
-		`UPDATE repos SET name = ?, name_lower = ?, description = ?, default_branch = ?, upstream = ?, is_private = ?,
+		`UPDATE repos SET name = ?, name_lower = ?, description = ?, default_branch = ?, upstream = ?, visibility = ?,
 		is_mirror = ? WHERE id = ?`, repo.Name, strings.ToLower(repo.Name), repo.Description, repo.DefaultBranch,
-		repo.Upstream, repo.IsPrivate, repo.IsMirror, rid,
+		repo.Upstream, repo.Visibility, repo.IsMirror, rid,
 	); err != nil {
 		tx.Rollback()
 		return err
@@ -305,3 +330,11 @@ func Pull(rid int64) error {
 
 	return nil
 }
+
+func IsVisible(repo *Repo, auth bool, user *User) bool {
+	if repo.Visibility == Public || (repo.Visibility == Limited && auth) || (auth && user.Id == repo.OwnerId) {
+		return true
+	}
+
+	return false
+}
diff --git a/src/repo/commit.go b/src/repo/commit.go
index 6b353aa..4dd0e79 100644
--- a/src/repo/commit.go
+++ b/src/repo/commit.go
@@ -32,7 +32,7 @@ func HandleCommit(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		goit.HttpError(w, http.StatusInternalServerError)
 		return
-	} else if repo == nil || (repo.IsPrivate && (!auth || repo.OwnerId != user.Id)) {
+	} else if repo == nil || !goit.IsVisible(repo, auth, user) {
 		goit.HttpError(w, http.StatusNotFound)
 		return
 	}
diff --git a/src/repo/create.go b/src/repo/create.go
index 6f380ea..9774fe3 100644
--- a/src/repo/create.go
+++ b/src/repo/create.go
@@ -29,10 +29,10 @@ func HandleCreate(w http.ResponseWriter, r *http.Request) {
 	}
 
 	data := struct {
-		Title, Message      string
-		Name, Description   string
-		DefaultBranch, Url  string
-		IsPrivate, IsMirror bool
+		Title, Message                 string
+		Name, Description              string
+		DefaultBranch, Url, Visibility string
+		IsMirror                       bool
 
 		CsrfField template.HTML
 	}{
@@ -46,7 +46,7 @@ func HandleCreate(w http.ResponseWriter, r *http.Request) {
 		data.Description = r.FormValue("description")
 		data.DefaultBranch = util.If(r.FormValue("branch") == "", "master", r.FormValue("branch"))
 		data.Url = r.FormValue("url")
-		data.IsPrivate = r.FormValue("visibility") == "private"
+		data.Visibility = r.FormValue("visibility")
 		data.IsMirror = r.FormValue("mirror") == "mirror"
 
 		if data.Name == "" {
@@ -59,9 +59,13 @@ func HandleCreate(w http.ResponseWriter, r *http.Request) {
 			return
 		} else if exists {
 			data.Message = "Name \"" + data.Name + "\" is taken"
+		} else if len(data.Description) > 256 {
+			data.Message = "Description cannot exceed 256 characters"
+		} else if visibility := goit.VisibilityFromString(data.Visibility); visibility == -1 {
+			data.Message = "Visibility \"" + data.Visibility + "\" is invalid"
 		} else if rid, err := goit.CreateRepo(goit.Repo{
 			OwnerId: user.Id, Name: data.Name, Description: data.Description, DefaultBranch: data.DefaultBranch,
-			Upstream: data.Url, IsPrivate: data.IsPrivate, IsMirror: data.IsMirror,
+			Upstream: data.Url, Visibility: visibility, IsMirror: data.IsMirror,
 		}); err != nil {
 			log.Println("[/repo/create]", err.Error())
 			goit.HttpError(w, http.StatusInternalServerError)
diff --git a/src/repo/download.go b/src/repo/download.go
index 6c24c0b..e7f91d8 100644
--- a/src/repo/download.go
+++ b/src/repo/download.go
@@ -35,7 +35,7 @@ func HandleDownload(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		goit.HttpError(w, http.StatusInternalServerError)
 		return
-	} else if repo == nil || (repo.IsPrivate && (!auth || user.Id != repo.OwnerId)) {
+	} else if repo == nil || !goit.IsVisible(repo, auth, user) {
 		goit.HttpError(w, http.StatusNotFound)
 		return
 	}
diff --git a/src/repo/edit.go b/src/repo/edit.go
index a709f73..f738409 100644
--- a/src/repo/edit.go
+++ b/src/repo/edit.go
@@ -61,10 +61,10 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) {
 		Title string
 
 		Edit struct {
-			Id, Owner, Name, Description string
-			DefaultBranch, Upstream      string
-			IsPrivate, IsMirror          bool
-			Message                      string
+			Id, Owner, Name, Description        string
+			DefaultBranch, Upstream, Visibility string
+			IsMirror                            bool
+			Message                             string
 		}
 
 		Transfer struct{ Owner, Message string }
@@ -84,7 +84,7 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) {
 	data.Edit.Description = repo.Description
 	data.Edit.DefaultBranch = repo.DefaultBranch
 	data.Edit.Upstream = repo.Upstream
-	data.Edit.IsPrivate = repo.IsPrivate
+	data.Edit.Visibility = repo.Visibility.String()
 	data.Edit.IsMirror = repo.IsMirror
 
 	gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true))
@@ -117,7 +117,7 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) {
 			data.Edit.Description = r.FormValue("description")
 			data.Edit.DefaultBranch = util.If(r.FormValue("branch") == "", "master", r.FormValue("branch"))
 			data.Edit.Upstream = r.FormValue("upstream")
-			data.Edit.IsPrivate = r.FormValue("visibility") == "private"
+			data.Edit.Visibility = r.FormValue("visibility")
 			data.Edit.IsMirror = r.FormValue("mirror") == "mirror"
 
 			if data.Edit.Name == "" {
@@ -132,9 +132,11 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) {
 				data.Edit.Message = "Name \"" + data.Edit.Name + "\" is taken"
 			} else if len(data.Edit.Description) > 256 {
 				data.Edit.Message = "Description cannot exceed 256 characters"
+			} else if visibility := goit.VisibilityFromString(data.Edit.Visibility); visibility == -1 {
+				data.Edit.Message = "Visibility \"" + data.Edit.Visibility + "\" is invalid"
 			} else if err := goit.UpdateRepo(repo.Id, goit.Repo{
 				Name: data.Edit.Name, Description: data.Edit.Description, DefaultBranch: data.Edit.DefaultBranch,
-				Upstream: data.Edit.Upstream, IsPrivate: data.Edit.IsPrivate, IsMirror: data.Edit.IsMirror,
+				Upstream: data.Edit.Upstream, Visibility: visibility, IsMirror: data.Edit.IsMirror,
 			}); err != nil {
 				log.Println("[/repo/edit]", err.Error())
 				goit.HttpError(w, http.StatusInternalServerError)
diff --git a/src/repo/file.go b/src/repo/file.go
index 5a4ba8d..01badea 100644
--- a/src/repo/file.go
+++ b/src/repo/file.go
@@ -35,7 +35,7 @@ func HandleFile(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		goit.HttpError(w, http.StatusInternalServerError)
 		return
-	} else if repo == nil || (repo.IsPrivate && (!auth || repo.OwnerId != user.Id)) {
+	} else if repo == nil || !goit.IsVisible(repo, auth, user) {
 		goit.HttpError(w, http.StatusNotFound)
 		return
 	}
diff --git a/src/repo/log.go b/src/repo/log.go
index 4c14f4a..248b9ba 100644
--- a/src/repo/log.go
+++ b/src/repo/log.go
@@ -37,7 +37,7 @@ func HandleLog(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		goit.HttpError(w, http.StatusInternalServerError)
 		return
-	} else if repo == nil || (repo.IsPrivate && (!auth || repo.OwnerId != user.Id)) {
+	} else if repo == nil || !goit.IsVisible(repo, auth, user) {
 		goit.HttpError(w, http.StatusNotFound)
 		return
 	}
diff --git a/src/repo/raw.go b/src/repo/raw.go
index c46fab6..788dd0f 100644
--- a/src/repo/raw.go
+++ b/src/repo/raw.go
@@ -30,7 +30,7 @@ func HandleRaw(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		goit.HttpError(w, http.StatusInternalServerError)
 		return
-	} else if repo == nil || (repo.IsPrivate && (!auth || user.Id != repo.OwnerId)) {
+	} else if repo == nil || !goit.IsVisible(repo, auth, user) {
 		goit.HttpError(w, http.StatusNotFound)
 		return
 	}
diff --git a/src/repo/refs.go b/src/repo/refs.go
index f76afb9..024c40a 100644
--- a/src/repo/refs.go
+++ b/src/repo/refs.go
@@ -30,7 +30,7 @@ func HandleRefs(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		goit.HttpError(w, http.StatusInternalServerError)
 		return
-	} else if repo == nil || (repo.IsPrivate && (!auth || repo.OwnerId != user.Id)) {
+	} else if repo == nil || !goit.IsVisible(repo, auth, user) {
 		goit.HttpError(w, http.StatusNotFound)
 		return
 	}
diff --git a/src/repo/tree.go b/src/repo/tree.go
index 08ef072..22e307f 100644
--- a/src/repo/tree.go
+++ b/src/repo/tree.go
@@ -34,7 +34,7 @@ func HandleTree(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		goit.HttpError(w, http.StatusInternalServerError)
 		return
-	} else if repo == nil || (repo.IsPrivate && (!auth || repo.OwnerId != user.Id)) {
+	} else if repo == nil || !goit.IsVisible(repo, auth, user) {
 		goit.HttpError(w, http.StatusNotFound)
 		return
 	}