fix(auth): reject invalid bearer tokens instead of falling back

Modify the authentication handler to return an unauthorized error when
an invalid or disabled bearer token is provided, rather than silently
falling back to an anonymous request.

This ensures that clients attempting to authenticate but failing (due to
expired, malformed, or disabled tokens) are explicitly notified of the
auth failure instead of proceeding anonymously. True anonymous requests
without any Authorization header remain supported.
This commit is contained in:
2026-05-31 13:02:58 +03:00
parent d99f8ee82a
commit 61b7c283a4
28 changed files with 3503 additions and 3300 deletions

View File

@@ -0,0 +1,176 @@
package services
import (
"context"
"io"
"net"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/hirochachacha/go-smb2"
)
type smbStorageBackend struct {
cfg StorageBackendConfig
}
func (b smbStorageBackend) ID() string { return b.cfg.ID }
func (b smbStorageBackend) Type() string { return StorageBackendSMB }
func (b smbStorageBackend) Put(ctx context.Context, key string, body io.Reader, _ int64, _ string) error {
share, closer, err := b.share()
if err != nil {
return err
}
defer closer()
if err := ctx.Err(); err != nil {
return err
}
remotePath := b.remotePath(key)
if err := share.MkdirAll(path.Dir(remotePath), 0o755); err != nil {
return err
}
target, err := share.OpenFile(remotePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
if err != nil {
return err
}
defer target.Close()
_, err = io.Copy(target, body)
return err
}
func (b smbStorageBackend) Get(ctx context.Context, key string) (StorageObject, error) {
share, closer, err := b.share()
if err != nil {
return StorageObject{}, err
}
if err := ctx.Err(); err != nil {
closer()
return StorageObject{}, err
}
source, err := share.Open(b.remotePath(key))
if err != nil {
closer()
return StorageObject{}, err
}
stat, err := source.Stat()
if err != nil {
source.Close()
closer()
return StorageObject{}, err
}
return StorageObject{Key: key, Size: stat.Size(), ModTime: stat.ModTime(), Body: closeWith(source, closer)}, nil
}
func (b smbStorageBackend) Delete(ctx context.Context, key string) error {
share, closer, err := b.share()
if err != nil {
return err
}
defer closer()
if err := ctx.Err(); err != nil {
return err
}
if err := share.Remove(b.remotePath(key)); err != nil && !os.IsNotExist(err) {
return err
}
return nil
}
func (b smbStorageBackend) DeletePrefix(ctx context.Context, prefix string) error {
share, closer, err := b.share()
if err != nil {
return err
}
defer closer()
if err := ctx.Err(); err != nil {
return err
}
err = share.RemoveAll(b.remotePath(prefix))
if err != nil && !os.IsNotExist(err) {
return err
}
return nil
}
func (b smbStorageBackend) Usage(ctx context.Context) (int64, error) {
share, closer, err := b.share()
if err != nil {
return 0, err
}
defer closer()
if err := ctx.Err(); err != nil {
return 0, err
}
return smbUsage(share, cleanRemoteRoot(b.cfg.RemotePath))
}
func (b smbStorageBackend) Test(ctx context.Context) error {
key := ".warpbox-storage-test-" + randomID(6)
if err := b.Put(ctx, key, strings.NewReader("ok"), 2, "text/plain"); err != nil {
return err
}
return b.Delete(ctx, key)
}
func (b smbStorageBackend) share() (*smb2.Share, func(), error) {
conn, err := net.DialTimeout("tcp", b.cfg.Host+":"+strconv.Itoa(b.cfg.Port), 15*time.Second)
if err != nil {
return nil, nil, err
}
dialer := &smb2.Dialer{
Initiator: &smb2.NTLMInitiator{
User: b.cfg.Username,
Password: b.cfg.Password,
Domain: b.cfg.Domain,
},
}
session, err := dialer.Dial(conn)
if err != nil {
conn.Close()
return nil, nil, err
}
share, err := session.Mount(b.cfg.Share)
if err != nil {
session.Logoff()
conn.Close()
return nil, nil, err
}
return share, func() {
share.Umount()
session.Logoff()
conn.Close()
}, nil
}
func (b smbStorageBackend) remotePath(key string) string {
return strings.TrimPrefix(path.Join(cleanRemoteRoot(b.cfg.RemotePath), cleanObjectKey(key)), "/")
}
func smbUsage(share *smb2.Share, root string) (int64, error) {
root = strings.TrimPrefix(root, "/")
entries, err := share.ReadDir(root)
if err != nil {
if os.IsNotExist(err) {
return 0, nil
}
return 0, err
}
var total int64
for _, entry := range entries {
item := path.Join(root, entry.Name())
if entry.IsDir() {
size, err := smbUsage(share, item)
if err != nil {
return 0, err
}
total += size
continue
}
total += entry.Size()
}
return total, nil
}