diff --git a/client/pages/viewerpage/editor/orgmode.js b/client/pages/viewerpage/editor/orgmode.js index 434004e4..76dddd8e 100644 --- a/client/pages/viewerpage/editor/orgmode.js +++ b/client/pages/viewerpage/editor/orgmode.js @@ -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); } } } diff --git a/server/ctrl/session.go b/server/ctrl/session.go index c9b43c74..9ccb1fc3 100644 --- a/server/ctrl/session.go +++ b/server/ctrl/session.go @@ -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) } diff --git a/server/ctrl/share.go b/server/ctrl/share.go index d7e929d4..78d6bca4 100644 --- a/server/ctrl/share.go +++ b/server/ctrl/share.go @@ -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 } diff --git a/server/main.go b/server/main.go index 277dda8a..fc055f7a 100644 --- a/server/main.go +++ b/server/main.go @@ -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 } diff --git a/server/middleware/session.go b/server/middleware/session.go index 2c9ea8ec..8c50c865 100644 --- a/server/middleware/session.go +++ b/server/middleware/session.go @@ -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 + } +}