mirror of
https://github.com/thomiceli/opengist.git
synced 2025-01-03 16:22:40 +00:00
Merge pull request #19 from thomiceli/feature/all-private
Restrict/unrestrict gists visibility to anonymous users
This commit is contained in:
commit
5ba90af04c
12 changed files with 77 additions and 23 deletions
|
@ -31,6 +31,7 @@ A self-hosted pastebin **powered by Git**. [Try it here](https://opengist.thomic
|
||||||
* Avatars
|
* Avatars
|
||||||
* Responsive UI
|
* Responsive UI
|
||||||
* Enable or disable signups
|
* Enable or disable signups
|
||||||
|
* Restrict or unrestrict snippets visibility to anonymous users
|
||||||
* Admin panel : delete users/gists; clean database/filesystem by syncing gists
|
* Admin panel : delete users/gists; clean database/filesystem by syncing gists
|
||||||
* SQLite database
|
* SQLite database
|
||||||
* Logging
|
* Logging
|
||||||
|
|
|
@ -11,6 +11,7 @@ type AdminSetting struct {
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SettingDisableSignup = "disable-signup"
|
SettingDisableSignup = "disable-signup"
|
||||||
|
SettingRequireLogin = "require-login"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetSetting(key string) (string, error) {
|
func GetSetting(key string) (string, error) {
|
||||||
|
@ -19,9 +20,24 @@ func GetSetting(key string) (string, error) {
|
||||||
return setting.Value, err
|
return setting.Value, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSettings() (map[string]string, error) {
|
||||||
|
var settings []AdminSetting
|
||||||
|
err := db.Find(&settings).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string]string)
|
||||||
|
for _, setting := range settings {
|
||||||
|
result[setting.Key] = setting.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateSetting(key string, value string) error {
|
func UpdateSetting(key string, value string) error {
|
||||||
return db.Clauses(clause.OnConflict{
|
return db.Clauses(clause.OnConflict{
|
||||||
Columns: []clause.Column{{Name: "key"}}, // key colume
|
Columns: []clause.Column{{Name: "key"}}, // key column
|
||||||
DoUpdates: clause.AssignmentColumns([]string{"value"}),
|
DoUpdates: clause.AssignmentColumns([]string{"value"}),
|
||||||
}).Create(&AdminSetting{
|
}).Create(&AdminSetting{
|
||||||
Key: key,
|
Key: key,
|
||||||
|
|
|
@ -28,6 +28,7 @@ func Setup(dbpath string) error {
|
||||||
// Default admin setting values
|
// Default admin setting values
|
||||||
return initAdminSettings(map[string]string{
|
return initAdminSettings(map[string]string{
|
||||||
SettingDisableSignup: "0",
|
SettingDisableSignup: "0",
|
||||||
|
SettingRequireLogin: "0",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,12 @@ func runGitCommand(ch ssh.Channel, gitCmd string, keyID uint, ip string) error {
|
||||||
return errors.New("gist not found")
|
return errors.New("gist not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if verb == "receive-pack" {
|
requireLogin, err := models.GetSetting(models.SettingRequireLogin)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("internal server error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if verb == "receive-pack" || requireLogin == "1" {
|
||||||
user, err := models.GetUserBySSHKeyID(keyID)
|
user, err := models.GetUserBySSHKeyID(keyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ func register(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func processRegister(ctx echo.Context) error {
|
func processRegister(ctx echo.Context) error {
|
||||||
if getData(ctx, "signupDisabled") == true {
|
if getData(ctx, "DisableSignup") == true {
|
||||||
return errorRes(403, "Signing up is disabled", nil)
|
return errorRes(403, "Signing up is disabled", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +148,10 @@ func oauthCallback(ctx echo.Context) error {
|
||||||
// if user is not in database, create it
|
// if user is not in database, create it
|
||||||
userDB, err := models.GetUserByProvider(user.UserID, user.Provider)
|
userDB, err := models.GetUserByProvider(user.UserID, user.Provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if getData(ctx, "DisableSignup") == true {
|
||||||
|
return errorRes(403, "Signing up is disabled", nil)
|
||||||
|
}
|
||||||
|
|
||||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return errorRes(500, "Cannot get user", err)
|
return errorRes(500, "Cannot get user", err)
|
||||||
}
|
}
|
||||||
|
@ -166,10 +170,6 @@ func oauthCallback(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = userDB.Create(); err != nil {
|
if err = userDB.Create(); err != nil {
|
||||||
if getData(ctx, "signupDisabled") == true {
|
|
||||||
return errorRes(403, "Signing up is disabled", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if models.IsUniqueConstraintViolation(err) {
|
if models.IsUniqueConstraintViolation(err) {
|
||||||
addFlash(ctx, "Username "+user.NickName+" already exists in Opengist", "error")
|
addFlash(ctx, "Username "+user.NickName+" already exists in Opengist", "error")
|
||||||
return redirect(ctx, "/login")
|
return redirect(ctx, "/login")
|
||||||
|
@ -281,7 +281,7 @@ func oauth(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxValue := context.WithValue(ctx.Request().Context(), providerKey, provider)
|
ctxValue := context.WithValue(ctx.Request().Context(), gothic.ProviderParamKey, provider)
|
||||||
ctx.SetRequest(ctx.Request().WithContext(ctxValue))
|
ctx.SetRequest(ctx.Request().WithContext(ctxValue))
|
||||||
if provider != "github" && provider != "gitea" {
|
if provider != "github" && provider != "gitea" {
|
||||||
return errorRes(400, "Unsupported provider", nil)
|
return errorRes(400, "Unsupported provider", nil)
|
||||||
|
|
|
@ -47,9 +47,10 @@ func gitHttp(ctx echo.Context) error {
|
||||||
|
|
||||||
gist := getData(ctx, "gist").(*models.Gist)
|
gist := getData(ctx, "gist").(*models.Gist)
|
||||||
|
|
||||||
noAuth := ctx.QueryParam("service") == "git-upload-pack" ||
|
noAuth := (ctx.QueryParam("service") == "git-upload-pack" ||
|
||||||
strings.HasSuffix(ctx.Request().URL.Path, "git-upload-pack") ||
|
strings.HasSuffix(ctx.Request().URL.Path, "git-upload-pack") ||
|
||||||
ctx.Request().Method == "GET"
|
ctx.Request().Method == "GET") &&
|
||||||
|
!getData(ctx, "RequireLogin").(bool)
|
||||||
|
|
||||||
repositoryPath := git.RepositoryPath(gist.User.Username, gist.Uuid)
|
repositoryPath := git.RepositoryPath(gist.User.Username, gist.Uuid)
|
||||||
|
|
||||||
|
|
|
@ -191,12 +191,12 @@ func Start() {
|
||||||
g2.PUT("/set-setting", adminSetSetting)
|
g2.PUT("/set-setting", adminSetSetting)
|
||||||
}
|
}
|
||||||
|
|
||||||
g1.GET("/all", allGists)
|
g1.GET("/all", allGists, checkRequireLogin)
|
||||||
g1.GET("/:user", allGists)
|
g1.GET("/:user", allGists, checkRequireLogin)
|
||||||
|
|
||||||
g3 := g1.Group("/:user/:gistname")
|
g3 := g1.Group("/:user/:gistname")
|
||||||
{
|
{
|
||||||
g3.Use(gistInit)
|
g3.Use(checkRequireLogin, gistInit)
|
||||||
g3.GET("", gistIndex)
|
g3.GET("", gistIndex)
|
||||||
g3.GET("/rev/:revision", gistIndex)
|
g3.GET("/rev/:revision", gistIndex)
|
||||||
g3.GET("/revisions", revisions)
|
g3.GET("/revisions", revisions)
|
||||||
|
@ -243,11 +243,9 @@ func dataInit(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
ctx.SetRequest(ctx.Request().WithContext(ctxValue))
|
ctx.SetRequest(ctx.Request().WithContext(ctxValue))
|
||||||
setData(ctx, "loadStartTime", time.Now())
|
setData(ctx, "loadStartTime", time.Now())
|
||||||
|
|
||||||
disableSignup, err := models.GetSetting(models.SettingDisableSignup)
|
if err := loadSettings(ctx); err != nil {
|
||||||
if err != nil {
|
return errorRes(500, "Cannot read settings from database", err)
|
||||||
return errorRes(500, "Cannot read setting from database", err)
|
|
||||||
}
|
}
|
||||||
setData(ctx, "signupDisabled", disableSignup == "1")
|
|
||||||
|
|
||||||
setData(ctx, "githubOauth", config.C.GithubClientKey != "" && config.C.GithubSecret != "")
|
setData(ctx, "githubOauth", config.C.GithubClientKey != "" && config.C.GithubSecret != "")
|
||||||
setData(ctx, "giteaOauth", config.C.GiteaClientKey != "" && config.C.GiteaSecret != "")
|
setData(ctx, "giteaOauth", config.C.GiteaClientKey != "" && config.C.GiteaSecret != "")
|
||||||
|
@ -318,6 +316,21 @@ func logged(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkRequireLogin(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(ctx echo.Context) error {
|
||||||
|
if user := getUserLogged(ctx); user != nil {
|
||||||
|
return next(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
require := getData(ctx, "RequireLogin")
|
||||||
|
if require == true {
|
||||||
|
addFlash(ctx, "You must be logged in to access gists", "error")
|
||||||
|
return redirect(ctx, "/login")
|
||||||
|
}
|
||||||
|
return next(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func cacheControl(next echo.HandlerFunc) echo.HandlerFunc {
|
func cacheControl(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
c.Response().Header().Set(echo.HeaderCacheControl, "public, max-age=31536000")
|
c.Response().Header().Set(echo.HeaderCacheControl, "public, max-age=31536000")
|
||||||
|
|
|
@ -18,10 +18,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type providerKeyType string
|
|
||||||
type dataTypeKey string
|
type dataTypeKey string
|
||||||
|
|
||||||
const providerKey providerKeyType = "provider"
|
|
||||||
const dataKey dataTypeKey = "data"
|
const dataKey dataTypeKey = "data"
|
||||||
|
|
||||||
func setData(ctx echo.Context, key string, value any) {
|
func setData(ctx echo.Context, key string, value any) {
|
||||||
|
@ -110,6 +108,20 @@ func deleteCsrfCookie(ctx echo.Context) {
|
||||||
ctx.SetCookie(&http.Cookie{Name: "_csrf", Path: "/", MaxAge: -1})
|
ctx.SetCookie(&http.Cookie{Name: "_csrf", Path: "/", MaxAge: -1})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadSettings(ctx echo.Context) error {
|
||||||
|
settings, err := models.GetSettings()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range settings {
|
||||||
|
s := strings.ReplaceAll(key, "-", " ")
|
||||||
|
s = title.String(s)
|
||||||
|
setData(ctx, strings.ReplaceAll(s, " ", ""), value == "1")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type OpengistValidator struct {
|
type OpengistValidator struct {
|
||||||
v *validator.Validate
|
v *validator.Validate
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
registerDomSetting(document.getElementById('disable-signup') as HTMLInputElement);
|
registerDomSetting(document.getElementById('disable-signup') as HTMLInputElement);
|
||||||
|
registerDomSetting(document.getElementById('require-login') as HTMLInputElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
const setSetting = (key: string, value: string) => {
|
const setSetting = (key: string, value: string) => {
|
||||||
|
|
2
templates/base/base_header.html
vendored
2
templates/base/base_header.html
vendored
|
@ -66,7 +66,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
{{ if not .signupDisabled }}
|
{{ if not .DisableSignup }}
|
||||||
<a href="/register" class="inline-flex text-slate-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">
|
<a href="/register" class="inline-flex text-slate-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">
|
||||||
<p class="text-slate-300 mr-1">Register</p>
|
<p class="text-slate-300 mr-1">Register</p>
|
||||||
</a>
|
</a>
|
||||||
|
|
6
templates/pages/admin_index.html
vendored
6
templates/pages/admin_index.html
vendored
|
@ -81,7 +81,11 @@
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<div>
|
<div>
|
||||||
<label for="disable-signup" class="text-sm text-slate-300">Disable signup</label>
|
<label for="disable-signup" class="text-sm text-slate-300">Disable signup</label>
|
||||||
<input type="checkbox" id="disable-signup" name="disable-signup" {{ if .signupDisabled }}checked="checked"{{ end }} class="ml-1 h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-600" />
|
<input type="checkbox" id="disable-signup" name="disable-signup" {{ if .DisableSignup }}checked="checked"{{ end }} class="ml-1 h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="disable-signup" class="text-sm text-slate-300">Login required</label>
|
||||||
|
<input type="checkbox" id="require-login" name="require-login" {{ if .RequireLogin }}checked="checked"{{ end }} class="ml-1 h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-600" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
4
templates/pages/auth_form.html
vendored
4
templates/pages/auth_form.html
vendored
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
<main class="mt-4">
|
<main class="mt-4">
|
||||||
{{ if and .signupDisabled (ne .title "Login") }}
|
{{ if and .DisableSignup (ne .title "Login") }}
|
||||||
<p class="italic">Administrator has disabled signing up</p>
|
<p class="italic">Administrator has disabled signing up</p>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<div class="sm:col-span-6">
|
<div class="sm:col-span-6">
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
<div class="flex-auto">
|
<div class="flex-auto">
|
||||||
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent border-gray-700 text-sm font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">Login</button>
|
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent border-gray-700 text-sm font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">Login</button>
|
||||||
</div>
|
</div>
|
||||||
{{ if not .signupDisabled }}
|
{{ if not .DisableSignup }}
|
||||||
<span class="float-right text-sm py-2 underline"><a href="/register">Register instead →</a></span>
|
<span class="float-right text-sm py-2 underline"><a href="/register">Register instead →</a></span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue