improvement (share): move out the authorization logic in a middleware

This commit is contained in:
Mickael KERJEAN 2019-01-09 20:00:41 +11:00
parent 43f00e12d6
commit 50506dcff9
5 changed files with 144 additions and 129 deletions

View file

@ -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);
}
}
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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 }

View file

@ -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
}
}