mirror of
https://github.com/filebrowser/filebrowser.git
synced 2024-06-07 23:00:43 +00:00
d8f415f8ab
This changes allows to password protect shares. It works by: * Allowing to optionally pass a password when creating a share * If set, the password + salt that is configured via a new flag will be hashed via bcrypt and the hash stored together with the rest of the share * Additionally, a random 96 byte long token gets generated and stored as part of the share * When the backend retrieves an unauthenticated request for a share that has authentication configured, it will return a http 401 * The frontend detects this and will show a login prompt * The actual download links are protected via an url arg that contains the previously generated token. This allows us to avoid buffering the download in the browser and allows pasting the link without breaking it
109 lines
2.2 KiB
Go
109 lines
2.2 KiB
Go
package auth
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/filebrowser/filebrowser/v2/settings"
|
|
"github.com/filebrowser/filebrowser/v2/users"
|
|
)
|
|
|
|
// MethodJSONAuth is used to identify json auth.
|
|
const MethodJSONAuth settings.AuthMethod = "json"
|
|
|
|
type jsonCred struct {
|
|
Password string `json:"password"`
|
|
Username string `json:"username"`
|
|
ReCaptcha string `json:"recaptcha"`
|
|
}
|
|
|
|
// JSONAuth is a json implementation of an Auther.
|
|
type JSONAuth struct {
|
|
ReCaptcha *ReCaptcha `json:"recaptcha" yaml:"recaptcha"`
|
|
}
|
|
|
|
// Auth authenticates the user via a json in content body.
|
|
func (a JSONAuth) Auth(r *http.Request, sto users.Store, root string) (*users.User, error) {
|
|
var cred jsonCred
|
|
|
|
if r.Body == nil {
|
|
return nil, os.ErrPermission
|
|
}
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&cred)
|
|
if err != nil {
|
|
return nil, os.ErrPermission
|
|
}
|
|
|
|
// If ReCaptcha is enabled, check the code.
|
|
if a.ReCaptcha != nil && len(a.ReCaptcha.Secret) > 0 {
|
|
ok, err := a.ReCaptcha.Ok(cred.ReCaptcha) //nolint:shadow
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !ok {
|
|
return nil, os.ErrPermission
|
|
}
|
|
}
|
|
|
|
u, err := sto.Get(root, cred.Username)
|
|
if err != nil || !users.CheckPwd(cred.Password, u.Password) {
|
|
return nil, os.ErrPermission
|
|
}
|
|
|
|
return u, nil
|
|
}
|
|
|
|
// LoginPage tells that json auth doesn't require a login page.
|
|
func (a JSONAuth) LoginPage() bool {
|
|
return true
|
|
}
|
|
|
|
const reCaptchaAPI = "/recaptcha/api/siteverify"
|
|
|
|
// ReCaptcha identifies a recaptcha connection.
|
|
type ReCaptcha struct {
|
|
Host string `json:"host"`
|
|
Key string `json:"key"`
|
|
Secret string `json:"secret"`
|
|
}
|
|
|
|
// Ok checks if a reCaptcha responde is correct.
|
|
func (r *ReCaptcha) Ok(response string) (bool, error) {
|
|
body := url.Values{}
|
|
body.Set("secret", r.Secret)
|
|
body.Add("response", response)
|
|
|
|
client := &http.Client{}
|
|
|
|
resp, err := client.Post(
|
|
r.Host+reCaptchaAPI,
|
|
"application/x-www-form-urlencoded",
|
|
strings.NewReader(body.Encode()),
|
|
)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return false, nil
|
|
}
|
|
|
|
var data struct {
|
|
Success bool `json:"success"`
|
|
}
|
|
|
|
err = json.NewDecoder(resp.Body).Decode(&data)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return data.Success, nil
|
|
}
|