fix: address branch review issues (json tags, share lockout, session expiry, file unlink)
This commit is contained in:
@@ -80,9 +80,15 @@ func (s *Server) handleDownload(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (s *Server) handleDeleteFile(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handleDeleteFile(w http.ResponseWriter, r *http.Request) {
|
||||||
id := r.PathValue("id")
|
id := r.PathValue("id")
|
||||||
|
f, err := s.store.FileGet(id)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
if err := s.store.FileDelete(id); err != nil {
|
if err := s.store.FileDelete(id); err != nil {
|
||||||
http.Error(w, "Failed to delete", http.StatusInternalServerError)
|
http.Error(w, "Failed to delete", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
os.Remove(filepath.Join(s.workspace, f.Dir, f.StorageName))
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sessionMaxAge = 7 * 86400
|
||||||
|
|
||||||
func (s *Server) requireAuth(next http.HandlerFunc) http.HandlerFunc {
|
func (s *Server) requireAuth(next http.HandlerFunc) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
cookie, err := r.Cookie("ydropbox_session")
|
cookie, err := r.Cookie("ydropbox_session")
|
||||||
@@ -13,7 +16,14 @@ func (s *Server) requireAuth(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
_, exists := s.sessions[cookie.Value]
|
createdAt, exists := s.sessions[cookie.Value]
|
||||||
|
if exists && time.Now().Unix()-createdAt > sessionMaxAge {
|
||||||
|
delete(s.sessions, cookie.Value)
|
||||||
|
exists = false
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
s.sessions[cookie.Value] = time.Now().Unix()
|
||||||
|
}
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
@@ -34,7 +44,14 @@ func (s *Server) requireAuthPage(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
_, exists := s.sessions[cookie.Value]
|
createdAt, exists := s.sessions[cookie.Value]
|
||||||
|
if exists && time.Now().Unix()-createdAt > sessionMaxAge {
|
||||||
|
delete(s.sessions, cookie.Value)
|
||||||
|
exists = false
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
s.sessions[cookie.Value] = time.Now().Unix()
|
||||||
|
}
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
|
|||||||
@@ -11,14 +11,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
store *store.Store
|
store *store.Store
|
||||||
accessToken string
|
accessToken string
|
||||||
sessions map[string]int64
|
sessions map[string]int64
|
||||||
shareSessions map[string]string
|
shareSessions map[string]string
|
||||||
loginAttempts map[string]int
|
loginAttempts map[string]int
|
||||||
loginLockout map[string]time.Time
|
loginLockout map[string]time.Time
|
||||||
mu sync.Mutex
|
shareAttempts map[string]int
|
||||||
workspace string
|
shareLockout map[string]time.Time
|
||||||
|
mu sync.Mutex
|
||||||
|
workspace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(store *store.Store, accessToken string, workspace string) *Server {
|
func New(store *store.Store, accessToken string, workspace string) *Server {
|
||||||
@@ -29,6 +31,8 @@ func New(store *store.Store, accessToken string, workspace string) *Server {
|
|||||||
shareSessions: make(map[string]string),
|
shareSessions: make(map[string]string),
|
||||||
loginAttempts: make(map[string]int),
|
loginAttempts: make(map[string]int),
|
||||||
loginLockout: make(map[string]time.Time),
|
loginLockout: make(map[string]time.Time),
|
||||||
|
shareAttempts: make(map[string]int),
|
||||||
|
shareLockout: make(map[string]time.Time),
|
||||||
workspace: workspace,
|
workspace: workspace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,12 +108,35 @@ func (s *Server) handleSharePassword(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
key := share.ID
|
||||||
|
s.mu.Lock()
|
||||||
|
if lockTime, locked := s.shareLockout[key]; locked && time.Now().Before(lockTime) {
|
||||||
|
s.mu.Unlock()
|
||||||
|
http.Error(w, "Too many attempts, locked for 60s", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
password := r.FormValue("password")
|
password := r.FormValue("password")
|
||||||
if err := bcrypt.CompareHashAndPassword([]byte(share.PasswordHash), []byte(password)); err != nil {
|
if err := bcrypt.CompareHashAndPassword([]byte(share.PasswordHash), []byte(password)); err != nil {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.shareAttempts[key]++
|
||||||
|
if s.shareAttempts[key] >= 5 {
|
||||||
|
s.shareLockout[key] = time.Now().Add(60 * time.Second)
|
||||||
|
s.shareAttempts[key] = 0
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
http.Error(w, "Wrong password", http.StatusForbidden)
|
http.Error(w, "Wrong password", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
delete(s.shareAttempts, key)
|
||||||
|
delete(s.shareLockout, key)
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
cookieVal := generateSessionID()
|
cookieVal := generateSessionID()
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
if s.shareSessions == nil {
|
if s.shareSessions == nil {
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package store
|
|||||||
import "database/sql"
|
import "database/sql"
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
ID string
|
ID string `json:"id"`
|
||||||
OriginalName string
|
OriginalName string `json:"name"`
|
||||||
StorageName string
|
StorageName string `json:"-"`
|
||||||
Dir string
|
Dir string `json:"dir"`
|
||||||
Size int64
|
Size int64 `json:"size"`
|
||||||
CreatedAt int64
|
CreatedAt int64 `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) FileCreate(f *File) error {
|
func (s *Store) FileCreate(f *File) error {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func TestIntegration(t *testing.T) {
|
|||||||
var uploadResp map[string]any
|
var uploadResp map[string]any
|
||||||
json.NewDecoder(uploadRes.Body).Decode(&uploadResp)
|
json.NewDecoder(uploadRes.Body).Decode(&uploadResp)
|
||||||
file := uploadResp["file"].(map[string]any)
|
file := uploadResp["file"].(map[string]any)
|
||||||
fileID := file["ID"].(string)
|
fileID := file["id"].(string)
|
||||||
|
|
||||||
listReq, _ := http.NewRequest("GET", ts.URL+"/api/files?dir=inbox", nil)
|
listReq, _ := http.NewRequest("GET", ts.URL+"/api/files?dir=inbox", nil)
|
||||||
for _, cookie := range loginRes.Cookies() {
|
for _, cookie := range loginRes.Cookies() {
|
||||||
|
|||||||
Reference in New Issue
Block a user