Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a setting to allow anonymous access to individual gists while still RequireLogin everywhere else #229

Merged
merged 7 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions internal/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package auth

type AuthInfoProvider interface {
RequireLogin() (bool, error)
AllowGistsWithoutLogin() (bool, error)
}

func ShouldAllowUnauthenticatedGistAccess(prov AuthInfoProvider, isSingleGistAccess bool) (bool, error) {
require, err := prov.RequireLogin()
if err != nil {
return false, err
}
allow, err := prov.AllowGistsWithoutLogin()
if err != nil {
return false, err
}
return !require || (isSingleGistAccess && allow), nil
}
27 changes: 23 additions & 4 deletions internal/db/admin_setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ type AdminSetting struct {
}

const (
SettingDisableSignup = "disable-signup"
SettingRequireLogin = "require-login"
SettingDisableLoginForm = "disable-login-form"
SettingDisableGravatar = "disable-gravatar"
SettingDisableSignup = "disable-signup"
SettingRequireLogin = "require-login"
SettingAllowGistsWithoutLogin = "allow-gists-without-login"
SettingDisableLoginForm = "disable-login-form"
SettingDisableGravatar = "disable-gravatar"
)

func GetSetting(key string) (string, error) {
Expand Down Expand Up @@ -62,3 +63,21 @@ func initAdminSettings(settings map[string]string) error {

return nil
}

type DBAuthInfo struct{}

func (auth DBAuthInfo) RequireLogin() (bool, error) {
s, err := GetSetting(SettingRequireLogin)
if err != nil {
return true, err
}
return s == "1", nil
}

func (auth DBAuthInfo) AllowGistsWithoutLogin() (bool, error) {
s, err := GetSetting(SettingAllowGistsWithoutLogin)
if err != nil {
return false, err
}
return s == "1", nil
}
9 changes: 5 additions & 4 deletions internal/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ func Setup(dbPath string, sharedCache bool) error {

// Default admin setting values
return initAdminSettings(map[string]string{
SettingDisableSignup: "0",
SettingRequireLogin: "0",
SettingDisableLoginForm: "0",
SettingDisableGravatar: "0",
SettingDisableSignup: "0",
SettingRequireLogin: "0",
SettingAllowGistsWithoutLogin: "0",
SettingDisableLoginForm: "0",
SettingDisableGravatar: "0",
})
}

Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/cs-CZ.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ admin.disable-login: Zakázat přihlášení
admin.disable-login_help: Zakázat přihlašování pomocí formuláře pro přihlášení a vynutit používání OAuth poskytovatele.
admin.disable-gravatar: Zakázat Gravatar
admin.disable-gravatar_help: Zakázat použití Gravataru jako poskytovatele avatara.

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: Opravdu chcete smazat tohoto uživatele?

admin.gists.title: Titulek
Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/de-DE.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ admin.disable-login: 'Login-Maske deaktivieren'
admin.disable-login_help: 'Login über Login-Maske verbieten und Benutzung von OAuth Providern erzwingen.'
admin.disable-gravatar: 'Gravatar deaktivieren'
admin.disable-gravatar_help: 'Gravatar als Avatar-Anbieter deaktivieren.'

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: 'Willst du diesen Benutzer löschen?'

admin.gists.title: 'Titel'
Expand Down
2 changes: 2 additions & 0 deletions internal/i18n/locales/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ admin.disable-signup: Disable signup
admin.disable-signup_help: Forbid the creation of new accounts.
admin.require-login: Require login
admin.require-login_help: Enforce users to be logged in to see gists.
admin.allow-gists-without-login: Allow individual gists without login
admin.allow-gists-without-login_help: Allow individual gists to be viewed and downloaded without login, while requiring login for discovering gists.
admin.disable-login: Disable login form
admin.disable-login_help: Forbid logging in via the login form to force using OAuth providers instead.
admin.disable-gravatar: Disable Gravatar
Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/es-ES.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ admin.disable-login: Deshabilitar formulario de inicio de sesión
admin.disable-login_help: Prohibir el inicio de sesión a través del formulario de inicio de sesión para forzar el uso de proveedores de OAuth en su lugar.
admin.disable-gravatar: Deshabilitar Gravatar
admin.disable-gravatar_help: Deshabilitar el uso de Gravatar como proveedor de avatar.

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: ¿Quieres eliminar a este usuario?

admin.gists.title: Título
Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/fr-FR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ admin.disable-login: Désactiver le formulaire de connexion
admin.disable-login_help: Interdire la connexion via le formulaire de connexion pour forcer l'utilisation des fournisseurs OAuth à la place.
admin.disable-gravatar: Désactiver Gravatar
admin.disable-gravatar_help: Désactiver l'utilisation de Gravatar comme fournisseur d'avatar.

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: Voulez-vous supprimer cet utilisateur ?

admin.gists.title: Titre
Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/hu-HU.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ admin.disable-login: Bejelentkezés űrlap letiltása
admin.disable-login_help: Letiltja a bejelentkezés űrlapon keresztüli bejelentkezéseket, OAuth szolgáltatókat ajánlva helyette.
admin.disable-gravatar: Gravatar kikapcsolása
admin.disable-gravatar_help: Tiltsd le a Gravatar-t mint profilkép szolgáltató.

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: Biztosan törlöd ezt a felhasználót?

admin.gists.title: Cím
Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/pt-BR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ admin.disable-login: Desabilitar formulário de login
admin.disable-login_help: Proibir o login através do formulário de login para forçar o uso de provedores de OAuth no lugar.
admin.disable-gravatar: Desabilitar Gravatar
admin.disable-gravatar_help: Desabilitar o uso do Gravatar como provedor de avatar.

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: Quer excluir este usuário?

admin.gists.title: Título
Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/ru-RU.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ admin.disable-login: Запретить авторизацию по паролю
admin.disable-login_help: Запретить авторизацию с вводом пароля, форсировать внешнюю авторизацию через Gitea/GitHub.
admin.disable-gravatar: Запретить Gravatar
admin.disable-gravatar_help: Запретить использование Gravatar как провайдера изображений профиля.

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: Вы уверены что хотите удалить этого пользователя?

admin.gists.title: Название
Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/tr-TR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ admin.disable-login: Disable login form
admin.disable-login_help: Forbid logging in via the login form to force using OAuth providers instead.
admin.disable-gravatar: Disable Gravatar
admin.disable-gravatar_help: Disable the usage of Gravatar as an avatar provider.

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: Do you want to delete this user ?

admin.gists.title: Title
Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/zh-CN.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ admin.disable-login: 禁用登录表单
admin.disable-login_help: 禁止使用登录表单进行登录以强制通过 OAuth 提供方登录。
admin.disable-gravatar: 禁用 Gravatar
admin.disable-gravatar_help: 停止使用 Gravatar 作为头像提供方。

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: 你想要删除此用户吗?

admin.gists.title: 标题
Expand Down
3 changes: 2 additions & 1 deletion internal/i18n/locales/zh-TW.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ admin.disable-login: 關閉登錄頁面
admin.disable-login_help: 關閉通過登錄頁面登錄,強制使用 OAuth 提供者。
admin.disable-gravatar: 禁用 Gravatar
admin.disable-gravatar_help: 禁止使用 Gravatar 作為頭像提供者。

admin.allow-gists-without-login:
admin.allow-gists-without-login_help:
admin.users.delete_confirm: 您要刪除這個使用者嗎?

admin.gists.title: 標題
Expand Down
12 changes: 7 additions & 5 deletions internal/ssh/git_ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ package ssh

import (
"errors"
"io"
"os/exec"
"strings"

"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/auth"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
"golang.org/x/crypto/ssh"
"gorm.io/gorm"
"io"
"os/exec"
"strings"
)

func runGitCommand(ch ssh.Channel, gitCmd string, key string, ip string) error {
Expand Down Expand Up @@ -37,7 +39,7 @@ func runGitCommand(ch ssh.Channel, gitCmd string, key string, ip string) error {
return errors.New("gist not found")
}

requireLogin, err := db.GetSetting(db.SettingRequireLogin)
allowUnauthenticated, err := auth.ShouldAllowUnauthenticatedGistAccess(db.DBAuthInfo{}, true)
if err != nil {
return errors.New("internal server error")
}
Expand All @@ -50,7 +52,7 @@ func runGitCommand(ch ssh.Channel, gitCmd string, key string, ip string) error {
if verb == "receive-pack" ||
gist.Private == 2 ||
gist.ID == 0 ||
requireLogin == "1" {
!allowUnauthenticated {

pubKey, err := db.SSHKeyExistsForUser(key, gist.UserID)
if err != nil {
Expand Down
12 changes: 12 additions & 0 deletions internal/web/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,15 @@ func getAvatarUrlFromProvider(provider string, identifier string) string {
}
return ""
}

type ContextAuthInfo struct {
context echo.Context
}

func (auth ContextAuthInfo) RequireLogin() (bool, error) {
return getData(auth.context, "RequireLogin") == true, nil
}

func (auth ContextAuthInfo) AllowGistsWithoutLogin() (bool, error) {
return getData(auth.context, "AllowGistsWithoutLogin") == true, nil
}
8 changes: 7 additions & 1 deletion internal/web/git_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/auth"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
"github.com/thomiceli/opengist/internal/memdb"
Expand Down Expand Up @@ -70,12 +71,17 @@ func gitHttp(ctx echo.Context) error {

setData(ctx, "repositoryPath", repositoryPath)

allow, err := auth.ShouldAllowUnauthenticatedGistAccess(ContextAuthInfo{ctx}, true)
if err != nil {
panic("impossible")
}

// Shows basic auth if :
// - user wants to push the gist
// - user wants to clone/pull a private gist
// - gist is not found (obfuscation)
// - admin setting to require login is set to true
if isPull && gist.Private != db.PrivateVisibility && gist.ID != 0 && !getData(ctx, "RequireLogin").(bool) {
if isPull && gist.Private != db.PrivateVisibility && gist.ID != 0 && allow {
return route.handler(ctx)
}

Expand Down
37 changes: 24 additions & 13 deletions internal/web/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/labstack/echo/v4/middleware"
"github.com/markbates/goth/gothic"
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/auth"
"github.com/thomiceli/opengist/internal/config"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
Expand Down Expand Up @@ -309,7 +310,7 @@ func NewServer(isDev bool) *Server {

g3 := g1.Group("/:user/:gistname")
{
g3.Use(checkRequireLogin, gistInit)
g3.Use(makeCheckRequireLogin(true), gistInit)
g3.GET("", gistIndex)
g3.GET("/rev/:revision", gistIndex)
g3.GET("/revisions", revisions)
Expand All @@ -321,9 +322,9 @@ func NewServer(isDev bool) *Server {
g3.GET("/edit", edit, logged, writePermission)
g3.POST("/edit", processCreate, logged, writePermission)
g3.POST("/like", like, logged)
g3.GET("/likes", likes)
g3.GET("/likes", likes, checkRequireLogin)
g3.POST("/fork", fork, logged)
g3.GET("/forks", forks)
g3.GET("/forks", forks, checkRequireLogin)
g3.PUT("/checkbox", checkbox, logged, writePermission)
}
}
Expand Down Expand Up @@ -516,21 +517,31 @@ 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)
}
func makeCheckRequireLogin(isSingleGistAccess bool) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
if user := getUserLogged(ctx); user != nil {
return next(ctx)
}

allow, err := auth.ShouldAllowUnauthenticatedGistAccess(ContextAuthInfo{ctx}, isSingleGistAccess)
if err != nil {
panic("impossible")
}

require := getData(ctx, "RequireLogin")
if require == true {
addFlash(ctx, tr(ctx, "flash.auth.must-be-logged-in"), "error")
return redirect(ctx, "/login")
if !allow {
addFlash(ctx, tr(ctx, "flash.auth.must-be-logged-in"), "error")
return redirect(ctx, "/login")
}
return next(ctx)
}
return next(ctx)
}
}

func checkRequireLogin(next echo.HandlerFunc) echo.HandlerFunc {
return makeCheckRequireLogin(false)(next)
}

func noRouteFound(echo.Context) error {
return notFound("Page not found")
}
Expand Down
58 changes: 58 additions & 0 deletions internal/web/test/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,61 @@ func login(t *testing.T, s *testServer, user db.UserDTO) {
err := s.request("POST", "/login", user, 302)
require.NoError(t, err)
}

type settingSet struct {
key string `form:"key"`
value string `form:"value"`
}

func TestAnonymous(t *testing.T) {
setup(t)
s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")
defer teardown(t, s)

user := db.UserDTO{Username: "thomas", Password: "azeaze"}
register(t, s, user)

err = s.request("PUT", "/admin-panel/set-config", settingSet{"require-login", "1"}, 200)
require.NoError(t, err)

gist1 := db.GistDTO{
Title: "gist1",
Description: "my first gist",
VisibilityDTO: db.VisibilityDTO{
Private: 0,
},
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
}
err = s.request("POST", "/", gist1, 302)
require.NoError(t, err)

gist1db, err := db.GetGistByID("1")
require.NoError(t, err)

err = s.request("GET", "/all", nil, 200)
require.NoError(t, err)

cookie := s.sessionCookie
s.sessionCookie = ""

err = s.request("GET", "/all", nil, 302)
require.NoError(t, err)

// Should redirect to login if RequireLogin
err = s.request("GET", "/"+gist1db.User.Username+"/"+gist1db.Uuid, nil, 302)
require.NoError(t, err)

s.sessionCookie = cookie

err = s.request("PUT", "/admin-panel/set-config", settingSet{"allow-gists-without-login", "1"}, 200)
require.NoError(t, err)

s.sessionCookie = ""

// Should return results
err = s.request("GET", "/"+gist1db.User.Username+"/"+gist1db.Uuid, nil, 200)
require.NoError(t, err)

}
Loading
Loading