mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 08:22:24 +01:00
improvement (share): move out the authorization logic in a middleware
This commit is contained in:
parent
43f00e12d6
commit
50506dcff9
5 changed files with 144 additions and 129 deletions
|
|
@ -4,7 +4,7 @@ import {
|
|||
org_metadown, org_insert_todo_heading, org_shiftleft, org_shiftright, fold, unfold,
|
||||
isFold, org_set_fold, org_shiftmetaleft, org_shiftmetaright
|
||||
} from './emacs-org';
|
||||
import { pathBuilder, dirname } from '../../../helpers/';
|
||||
import { pathBuilder, dirname, currentShare } from '../../../helpers/';
|
||||
let CodeMirror = window.CodeMirror;
|
||||
|
||||
CodeMirror.__mode = 'orgmode';
|
||||
|
|
@ -272,8 +272,9 @@ function toggleHandler(cm, e){
|
|||
window.open(link);
|
||||
}else{
|
||||
const root_path = dirname(window.location.pathname.replace(/^\/view/, ''));
|
||||
const link_path = link;
|
||||
window.open("/view"+pathBuilder(root_path, link_path));
|
||||
const share = currentShare();
|
||||
const url = share ? "/view"+pathBuilder(root_path, link)+"?share="+share : "/view"+pathBuilder(root_path, link)
|
||||
window.open(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,12 @@ func SessionLogout(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
MaxAge: -1,
|
||||
Path: COOKIE_PATH_ADMIN,
|
||||
})
|
||||
http.SetCookie(res, &http.Cookie{
|
||||
Name: COOKIE_NAME_PROOF,
|
||||
Value: "",
|
||||
MaxAge: -1,
|
||||
Path: COOKIE_PATH,
|
||||
})
|
||||
SendSuccessResult(res, nil)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,66 +22,37 @@ func ShareList(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
SendSuccessResults(res, listOfSharedLinks)
|
||||
}
|
||||
|
||||
func ShareGet(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
share_id := mux.Vars(req)["share"]
|
||||
s, err := model.ShareGet(share_id);
|
||||
if err != nil {
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
SendSuccessResult(res, struct{
|
||||
Id string `json:"id"`
|
||||
Path string `json:"path"`
|
||||
}{
|
||||
Id: s.Id,
|
||||
Path: s.Path,
|
||||
})
|
||||
}
|
||||
|
||||
func ShareUpsert(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
share_target := mux.Vars(req)["share"]
|
||||
|
||||
// Make sure the current user is allowed to do that
|
||||
backend_id := ""
|
||||
path_from := "/"
|
||||
auth_cookie := ""
|
||||
|
||||
if ctx.Share.Id != "" {
|
||||
if ctx.Share.CanShare != true {
|
||||
SendErrorResult(res, ErrPermissionDenied)
|
||||
return
|
||||
}
|
||||
backend_id = ctx.Share.Backend
|
||||
|
||||
auth_cookie = ctx.Share.Auth
|
||||
path_from = ctx.Share.Path
|
||||
} else {
|
||||
backend_id = GenerateID(&ctx)
|
||||
auth_cookie = func() string {
|
||||
a, err := req.Cookie("auth")
|
||||
if err != nil {
|
||||
return "N/A"
|
||||
}
|
||||
return a.Value
|
||||
}()
|
||||
if ctx.Session["path"] != "" {
|
||||
path_from = ctx.Session["path"]
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.Share.Id != "" {
|
||||
if backend_id != ctx.Share.Backend {
|
||||
SendErrorResult(res, ErrPermissionDenied)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Perform upsert
|
||||
s := Share{
|
||||
Id: share_target,
|
||||
Auth: auth_cookie,
|
||||
Backend: backend_id,
|
||||
Path: path_from + strings.TrimPrefix(NewStringFromInterface(ctx.Body["path"]), "/"),
|
||||
Id: mux.Vars(req)["share"],
|
||||
Auth: func() string {
|
||||
if ctx.Share.Id == "" {
|
||||
a, err := req.Cookie(COOKIE_NAME_AUTH)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return a.Value
|
||||
}
|
||||
return ctx.Share.Auth
|
||||
}(),
|
||||
Backend: func () string {
|
||||
if ctx.Share.Id == "" {
|
||||
return GenerateID(&ctx)
|
||||
}
|
||||
return ctx.Share.Backend
|
||||
}(),
|
||||
Path: func () string {
|
||||
leftPath := "/"
|
||||
rightPath := strings.TrimPrefix(NewStringFromInterface(ctx.Body["path"]), "/")
|
||||
if ctx.Share.Id != "" {
|
||||
leftPath = ctx.Share.Path
|
||||
} else {
|
||||
if ctx.Session["path"] != "" {
|
||||
leftPath = ctx.Session["path"]
|
||||
}
|
||||
}
|
||||
return leftPath + rightPath
|
||||
}(),
|
||||
Password: NewStringpFromInterface(ctx.Body["password"]),
|
||||
Users: NewStringpFromInterface(ctx.Body["users"]),
|
||||
Expire: NewInt64pFromInterface(ctx.Body["expire"]),
|
||||
|
|
@ -101,30 +72,6 @@ func ShareUpsert(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
|
||||
func ShareDelete(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
share_target := mux.Vars(req)["share"]
|
||||
share_current := req.URL.Query().Get("share");
|
||||
|
||||
// Make sure the current user is allowed to do that
|
||||
backend_id := GenerateID(&ctx)
|
||||
if share_current != "" {
|
||||
share, err := model.ShareGet(share_current);
|
||||
if err != nil {
|
||||
SendErrorResult(res, ErrNotFound)
|
||||
return
|
||||
} else if share.CanShare != true {
|
||||
SendErrorResult(res, ErrPermissionDenied)
|
||||
return
|
||||
}
|
||||
backend_id = share.Backend
|
||||
}
|
||||
share, err := model.ShareGet(share_target);
|
||||
if err == nil {
|
||||
if backend_id != share.Backend {
|
||||
SendErrorResult(res, ErrPermissionDenied)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the share
|
||||
if err := model.ShareDelete(share_target); err != nil {
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
|
|
@ -156,6 +103,12 @@ func ShareVerifyProof(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
|
||||
// 2) validate the current context
|
||||
if len(verifiedProof) > 20 || len(requiredProof) > 20 {
|
||||
http.SetCookie(res, &http.Cookie{
|
||||
Name: COOKIE_NAME_PROOF,
|
||||
Value: "",
|
||||
MaxAge: -1,
|
||||
Path: COOKIE_PATH,
|
||||
})
|
||||
SendErrorResult(res, ErrNotValid)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,11 +83,12 @@ func Init(a *App) {
|
|||
share := r.PathPrefix("/api/share").Subrouter()
|
||||
middlewares = []Middleware{ ApiHeaders, SecureHeaders, SessionStart, LoggedInOnly }
|
||||
share.HandleFunc("", NewMiddlewareChain(ShareList, middlewares, *a)).Methods("GET")
|
||||
share.HandleFunc("/{share}", NewMiddlewareChain(ShareDelete, middlewares, *a)).Methods("DELETE")
|
||||
middlewares = []Middleware{ ApiHeaders, SecureHeaders, SessionStart, LoggedInOnly, BodyParser }
|
||||
share.HandleFunc("/{share}", NewMiddlewareChain(ShareUpsert, middlewares, *a)).Methods("POST")
|
||||
middlewares = []Middleware{ ApiHeaders, SecureHeaders, BodyParser }
|
||||
share.HandleFunc("/{share}/proof", NewMiddlewareChain(ShareVerifyProof, middlewares, *a)).Methods("POST")
|
||||
middlewares = []Middleware{ ApiHeaders, SecureHeaders, CanManageShare }
|
||||
share.HandleFunc("/{share}", NewMiddlewareChain(ShareDelete, middlewares, *a)).Methods("DELETE")
|
||||
middlewares = []Middleware{ ApiHeaders, SecureHeaders, BodyParser, CanManageShare }
|
||||
share.HandleFunc("/{share}", NewMiddlewareChain(ShareUpsert, middlewares, *a)).Methods("POST")
|
||||
|
||||
// Webdav server / Shared Link
|
||||
middlewares = []Middleware{ IndexHeaders, SecureHeaders }
|
||||
|
|
|
|||
|
|
@ -47,54 +47,17 @@ func AdminOnly(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, r
|
|||
}
|
||||
|
||||
func SessionStart (fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
extractSession := func(req *http.Request, ctx *App) (map[string]string, error) {
|
||||
var str string
|
||||
var err error
|
||||
var res map[string]string = make(map[string]string)
|
||||
|
||||
if ctx.Share.Id != "" {
|
||||
str, err = DecryptString(SECRET_KEY, ctx.Share.Auth)
|
||||
if err != nil {
|
||||
// This typically happen when changing the secret key
|
||||
return res, nil
|
||||
}
|
||||
err = json.Unmarshal([]byte(str), &res)
|
||||
if ctx.Share.Path[len(ctx.Share.Path)-1:] == "/" {
|
||||
res["path"] = ctx.Share.Path
|
||||
} else {
|
||||
path := req.URL.Query().Get("path")
|
||||
if strings.HasSuffix(ctx.Share.Path, path) == false {
|
||||
return res, ErrPermissionDenied
|
||||
}
|
||||
res["path"] = strings.TrimSuffix(ctx.Share.Path, path) + "/"
|
||||
}
|
||||
return res, err
|
||||
} else {
|
||||
cookie, err := req.Cookie(COOKIE_NAME_AUTH)
|
||||
if err != nil {
|
||||
return res, nil
|
||||
}
|
||||
str = cookie.Value
|
||||
str, err = DecryptString(SECRET_KEY, str)
|
||||
if err != nil {
|
||||
// This typically happen when changing the secret key
|
||||
return res, nil
|
||||
}
|
||||
err = json.Unmarshal([]byte(str), &res)
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
extractBackend := func(req *http.Request, ctx *App) (IBackend, error) {
|
||||
return model.NewBackend(ctx, ctx.Session)
|
||||
}
|
||||
|
||||
return func(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
var err error
|
||||
if ctx.Share, err = _findShare(req, _extractShareId(req)); err != nil {
|
||||
if ctx.Share, err = _extractShare(req); err != nil {
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
if ctx.Session, err = extractSession(req, &ctx); err != nil {
|
||||
if ctx.Session, err = _extractSession(req, &ctx); err != nil {
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
|
@ -114,7 +77,7 @@ func RedirectSharedLoginIfNeeded(fn func(App, http.ResponseWriter, *http.Request
|
|||
return
|
||||
}
|
||||
|
||||
share, err := _findShare(req, share_id);
|
||||
share, err := _extractShare(req);
|
||||
if err != nil || share_id != share.Id {
|
||||
http.Redirect(res, req, fmt.Sprintf("/s/%s?next=%s", share_id, req.URL.Path), http.StatusTemporaryRedirect)
|
||||
return
|
||||
|
|
@ -123,6 +86,58 @@ func RedirectSharedLoginIfNeeded(fn func(App, http.ResponseWriter, *http.Request
|
|||
}
|
||||
}
|
||||
|
||||
func CanManageShare(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
return func(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
share_id := mux.Vars(req)["share"]
|
||||
if share_id == "" {
|
||||
SendErrorResult(res, ErrNotValid)
|
||||
return
|
||||
}
|
||||
|
||||
// anyone can manage a share_id that's not been attributed yet
|
||||
s, err := model.ShareGet(share_id)
|
||||
if err != nil {
|
||||
if err == ErrNotFound {
|
||||
SessionStart(fn)(ctx, res, req)
|
||||
return
|
||||
}
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// In a scenario where the shared link has already been atributed, we need to make sure
|
||||
// the user that's currently logged in can manage the link. 2 scenarios here:
|
||||
// 1) scenario 1: the user is the very same one that generated the shared link in the first place
|
||||
if ctx.Session, err = _extractSession(req, &ctx); err != nil {
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
if s.Backend == GenerateID(&ctx) {
|
||||
fn(ctx, res, req)
|
||||
return
|
||||
}
|
||||
// 2) scenario 2: the user is different than the one that has generated the shared link
|
||||
// in this scenario, the link owner might have granted for user the right to reshare links
|
||||
if ctx.Share, err = _extractShare(req); err != nil {
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
if ctx.Session, err = _extractSession(req, &ctx); err != nil {
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
if s.Backend == GenerateID(&ctx) {
|
||||
if s.CanShare == true {
|
||||
fn(ctx, res, req)
|
||||
return
|
||||
}
|
||||
}
|
||||
SendErrorResult(res, ErrPermissionDenied)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func _extractShareId(req *http.Request) string {
|
||||
share := req.URL.Query().Get("share")
|
||||
if share != "" {
|
||||
|
|
@ -135,8 +150,9 @@ func _extractShareId(req *http.Request) string {
|
|||
return m
|
||||
}
|
||||
|
||||
func _findShare(req *http.Request, share_id string) (Share, error) {
|
||||
func _extractShare(req *http.Request) (Share, error) {
|
||||
var err error
|
||||
share_id := _extractShareId(req)
|
||||
if share_id == "" {
|
||||
return Share{}, nil
|
||||
}
|
||||
|
|
@ -162,3 +178,41 @@ func _findShare(req *http.Request, share_id string) (Share, error) {
|
|||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func _extractSession(req *http.Request, ctx *App) (map[string]string, error) {
|
||||
var str string
|
||||
var err error
|
||||
var res map[string]string = make(map[string]string)
|
||||
|
||||
if ctx.Share.Id != "" {
|
||||
str, err = DecryptString(SECRET_KEY, ctx.Share.Auth)
|
||||
if err != nil {
|
||||
// This typically happen when changing the secret key
|
||||
return res, nil
|
||||
}
|
||||
err = json.Unmarshal([]byte(str), &res)
|
||||
if ctx.Share.Path[len(ctx.Share.Path)-1:] == "/" {
|
||||
res["path"] = ctx.Share.Path
|
||||
} else {
|
||||
path := req.URL.Query().Get("path")
|
||||
if strings.HasSuffix(ctx.Share.Path, path) == false {
|
||||
return res, ErrPermissionDenied
|
||||
}
|
||||
res["path"] = strings.TrimSuffix(ctx.Share.Path, path) + "/"
|
||||
}
|
||||
return res, err
|
||||
} else {
|
||||
cookie, err := req.Cookie(COOKIE_NAME_AUTH)
|
||||
if err != nil {
|
||||
return res, nil
|
||||
}
|
||||
str = cookie.Value
|
||||
str, err = DecryptString(SECRET_KEY, str)
|
||||
if err != nil {
|
||||
// This typically happen when changing the secret key
|
||||
return res, nil
|
||||
}
|
||||
err = json.Unmarshal([]byte(str), &res)
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue