+20
-8
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
// path to auth page in the client bundle
|
||||
@@ -42,6 +43,10 @@ func addAuthEndpoints(r chi.Router, config *ServerConfig) {
|
||||
// Auth page
|
||||
r.Get("/.auth", func(w http.ResponseWriter, r *http.Request) {
|
||||
spaceConfig := spaceConfigFromContext(r.Context())
|
||||
if spaceConfig.Auth == nil {
|
||||
http.Error(w, "Authentication not enabled", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if err := spaceConfig.InitAuth(); err != nil {
|
||||
http.Error(w, "Failed to initialize authentication", http.StatusInternalServerError)
|
||||
return
|
||||
@@ -55,12 +60,10 @@ func addAuthEndpoints(r chi.Router, config *ServerConfig) {
|
||||
|
||||
tpl := template.Must(template.New("auth").Parse(string(data)))
|
||||
|
||||
templateData := struct {
|
||||
HostPrefix string
|
||||
SpaceName string
|
||||
}{
|
||||
HostPrefix: config.HostURLPrefix,
|
||||
SpaceName: spaceConfig.SpaceName,
|
||||
templateData := map[string]string{
|
||||
"HostPrefix": config.HostURLPrefix,
|
||||
"SpaceName": spaceConfig.SpaceName,
|
||||
"EncryptionSalt": spaceConfig.JwtIssuer.Salt,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-type", "text/html")
|
||||
@@ -138,18 +141,27 @@ func addAuthEndpoints(r chi.Router, config *ServerConfig) {
|
||||
redirectPath = from
|
||||
}
|
||||
|
||||
http.Redirect(w, r, applyURLPrefix(redirectPath, config.HostURLPrefix), http.StatusFound)
|
||||
render.JSON(w, r, map[string]any{
|
||||
"status": "ok",
|
||||
"redirect": redirectPath,
|
||||
})
|
||||
} else {
|
||||
log.Println("Authentication failed, redirecting to auth page.")
|
||||
spaceConfig.LockoutTimer.AddCount()
|
||||
|
||||
http.Redirect(w, r, applyURLPrefix("/.auth?error=1", config.HostURLPrefix), http.StatusFound)
|
||||
render.JSON(w, r, map[string]any{
|
||||
"status": "error",
|
||||
"error": "Invalid username and/or password",
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (spaceConfig *SpaceConfig) InitAuth() error {
|
||||
if spaceConfig.JwtIssuer == nil {
|
||||
spaceConfig.authMutex.Lock()
|
||||
defer spaceConfig.authMutex.Unlock()
|
||||
|
||||
var err error
|
||||
// Need to do some initialization
|
||||
spaceConfig.JwtIssuer, err = CreateAuthenticator(path.Join(spaceConfig.SpaceFolderPath, ".silverbullet.auth.json"), spaceConfig.Auth)
|
||||
|
||||
@@ -20,6 +20,7 @@ type Authenticator struct {
|
||||
path string
|
||||
SecretKey string `json:"secret_key"`
|
||||
AuthHash string `json:"auth_hash"`
|
||||
Salt string `json:"salt"` // base64 encoded 16 byte randomized salt used for encryption
|
||||
}
|
||||
|
||||
func CreateAuthenticator(path string, authOptions *AuthOptions) (*Authenticator, error) {
|
||||
@@ -79,6 +80,12 @@ func (j *Authenticator) init(authConfig *AuthOptions) error {
|
||||
|
||||
j.AuthHash = newAuthHash
|
||||
|
||||
if j.Salt == "" {
|
||||
b := make([]byte, 16)
|
||||
rand.Read(b)
|
||||
j.Salt = base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
return j.save()
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ type BootConfig struct {
|
||||
|
||||
// Whether or not the client should push logs to the server
|
||||
LogPush bool `json:"logPush"`
|
||||
|
||||
// Encryption
|
||||
EnableClientEncryption bool `json:"enableClientEncryption"`
|
||||
}
|
||||
|
||||
func Router(config *ServerConfig) chi.Router {
|
||||
@@ -68,6 +71,8 @@ func Router(config *ServerConfig) chi.Router {
|
||||
IndexPage: spaceConfig.IndexPage,
|
||||
ReadOnly: spaceConfig.ReadOnlyMode,
|
||||
LogPush: spaceConfig.LogPush,
|
||||
// Client encryption is offered as an option when auth is enabled only
|
||||
EnableClientEncryption: spaceConfig.Auth != nil,
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
|
||||
@@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
@@ -43,6 +44,7 @@ type SpaceConfig struct {
|
||||
// Auth temporary objects
|
||||
JwtIssuer *Authenticator
|
||||
LockoutTimer *LockoutTimer
|
||||
authMutex sync.Mutex
|
||||
}
|
||||
|
||||
type ConfigResolver func(r *http.Request) (*SpaceConfig, error)
|
||||
|
||||
Reference in New Issue
Block a user