From 8fc8f7210364c4c8a71975d723de3491dbddd265 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Tue, 22 Sep 2020 17:52:46 -0600 Subject: [PATCH] attempt number 2 --- cmd/config.go | 1 + cmd/root.go | 4 + frontend/public/index.html | 4 +- frontend/src/components/Header.vue | 5 +- .../src/components/settings/Permissions.vue | 6 +- frontend/src/components/settings/UserForm.vue | 6 +- frontend/src/utils/constants.js | 4 +- frontend/src/views/Layout.vue | 6 +- frontend/src/views/settings/Global.vue | 14 ++- http/commands.go | 118 +++++++++--------- http/http.go | 2 +- http/static.go | 1 + settings/settings.go | 1 + 13 files changed, 97 insertions(+), 75 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index 8b69e7b7..4cdd3ee2 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -140,6 +140,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut fmt.Fprintf(w, "\tAddress:\t%s\n", ser.Address) fmt.Fprintf(w, "\tTLS Cert:\t%s\n", ser.TLSCert) fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey) + fmt.Fprintf(w, "\tExec Enabled:\t%t\n", ser.EnableExec) fmt.Fprintln(w, "\nDefaults:") fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope) fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale) diff --git a/cmd/root.go b/cmd/root.go index b096a7c0..8fe0393a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -64,6 +64,7 @@ func addServerFlags(flags *pflag.FlagSet) { flags.Int("img-processors", 4, "image processors count") flags.Bool("disable-thumbnails", false, "disable image thumbnails") flags.Bool("disable-preview-resize", false, "disable resize of image previews") + flags.Bool("disable-exec", false, "disables Command Runner feature") } var rootCmd = &cobra.Command{ @@ -241,6 +242,9 @@ func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server { _, disablePreviewResize := getParamB(flags, "disable-preview-resize") server.ResizePreview = !disablePreviewResize + _, disableExec := getParamB(flags, "disable-exec") + server.EnableExec = !disableExec + return server } diff --git a/frontend/public/index.html b/frontend/public/index.html index 7625aae3..6dd9d32a 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -13,7 +13,7 @@ - + @@ -31,7 +31,7 @@ diff --git a/frontend/src/components/settings/UserForm.vue b/frontend/src/components/settings/UserForm.vue index 35a872f8..8082d5e1 100644 --- a/frontend/src/components/settings/UserForm.vue +++ b/frontend/src/components/settings/UserForm.vue @@ -25,7 +25,7 @@

- +

{{ $t('settings.rules') }}

@@ -40,6 +40,7 @@ import Languages from './Languages' import Rules from './Rules' import Permissions from './Permissions' import Commands from './Commands' +import { enableExec } from '@/utils/constants' export default { name: 'user', @@ -53,7 +54,8 @@ export default { computed: { passwordPlaceholder () { return this.isNew ? '' : this.$t('settings.avoidChanges') - } + }, + isExecEnabled: () => enableExec }, watch: { 'user.perm.admin': function () { diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index e4689dae..d4a8fd79 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -13,6 +13,7 @@ const loginPage = window.FileBrowser.LoginPage const theme = window.FileBrowser.Theme const enableThumbs = window.FileBrowser.EnableThumbs const resizePreview = window.FileBrowser.ResizePreview +const enableExec = window.FileBrowser.EnableExec export { name, @@ -28,5 +29,6 @@ export { loginPage, theme, enableThumbs, - resizePreview + resizePreview, + enableExec } diff --git a/frontend/src/views/Layout.vue b/frontend/src/views/Layout.vue index c5e6c2ef..85991d48 100644 --- a/frontend/src/views/Layout.vue +++ b/frontend/src/views/Layout.vue @@ -7,7 +7,7 @@
- +
@@ -19,6 +19,7 @@ import Sidebar from '@/components/Sidebar' import Prompts from '@/components/prompts/Prompts' import SiteHeader from '@/components/Header' import Shell from '@/components/Shell' +import { enableExec } from '@/utils/constants' export default { name: 'layout', @@ -30,7 +31,8 @@ export default { }, computed: { ...mapGetters([ 'isLogged', 'progress' ]), - ...mapState([ 'user' ]) + ...mapState([ 'user' ]), + isExecEnabled: () => enableExec }, watch: { '$route': function () { diff --git a/frontend/src/views/settings/Global.vue b/frontend/src/views/settings/Global.vue index a0fbfa8a..fd72ae5d 100644 --- a/frontend/src/views/settings/Global.vue +++ b/frontend/src/views/settings/Global.vue @@ -14,9 +14,11 @@

{{ $t('settings.globalRules') }}

-

{{ $t('settings.executeOnShell') }}

-

{{ $t('settings.executeOnShellDescription') }}

- +
+

{{ $t('settings.executeOnShell') }}

+

{{ $t('settings.executeOnShellDescription') }}

+ +

{{ $t('settings.branding') }}

@@ -67,7 +69,7 @@ -
+

{{ $t('settings.commandRunner') }}

@@ -104,6 +106,7 @@ import { settings as api } from '@/api' import UserForm from '@/components/settings/UserForm' import Rules from '@/components/settings/Rules' import Themes from '@/components/settings/Themes' +import { enableExec } from '@/utils/constants' export default { name: 'settings', @@ -119,7 +122,8 @@ export default { } }, computed: { - ...mapState([ 'user' ]) + ...mapState([ 'user' ]), + isExecEnabled: () => enableExec }, async created () { try { diff --git a/http/commands.go b/http/commands.go index cdb5ea1e..5d15663d 100644 --- a/http/commands.go +++ b/http/commands.go @@ -37,74 +37,76 @@ func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) { //nolin } } -var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - return http.StatusInternalServerError, err - } - defer conn.Close() +func commandsHandler (enableExec bool) handleFunc { + return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return http.StatusInternalServerError, err + } + defer conn.Close() - var raw string + var raw string - for { - _, msg, err := conn.ReadMessage() //nolint:shadow + for { + _, msg, err := conn.ReadMessage() //nolint:shadow + if err != nil { + wsErr(conn, r, http.StatusInternalServerError, err) + return 0, nil + } + + raw = strings.TrimSpace(string(msg)) + if raw != "" { + break + } + } + + if !enableExec || !d.user.CanExecute(strings.Split(raw, " ")[0]) { + if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:shadow + wsErr(conn, r, http.StatusInternalServerError, err) + } + + return 0, nil + } + + command, err := runner.ParseCommand(d.settings, raw) + if err != nil { + if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil { //nolint:shadow + wsErr(conn, r, http.StatusInternalServerError, err) + } + return 0, nil + } + + cmd := exec.Command(command[0], command[1:]...) //nolint:gosec + cmd.Dir = d.user.FullPath(r.URL.Path) + + stdout, err := cmd.StdoutPipe() if err != nil { wsErr(conn, r, http.StatusInternalServerError, err) return 0, nil } - raw = strings.TrimSpace(string(msg)) - if raw != "" { - break + stderr, err := cmd.StderrPipe() + if err != nil { + wsErr(conn, r, http.StatusInternalServerError, err) + return 0, nil } - } - if !d.user.CanExecute(strings.Split(raw, " ")[0]) { - if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:shadow + if err := cmd.Start(); err != nil { + wsErr(conn, r, http.StatusInternalServerError, err) + return 0, nil + } + + s := bufio.NewScanner(io.MultiReader(stdout, stderr)) + for s.Scan() { + if err := conn.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { + log.Print(err) + } + } + + if err := cmd.Wait(); err != nil { wsErr(conn, r, http.StatusInternalServerError, err) } return 0, nil - } - - command, err := runner.ParseCommand(d.settings, raw) - if err != nil { - if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil { //nolint:shadow - wsErr(conn, r, http.StatusInternalServerError, err) - } - return 0, nil - } - - cmd := exec.Command(command[0], command[1:]...) //nolint:gosec - cmd.Dir = d.user.FullPath(r.URL.Path) - - stdout, err := cmd.StdoutPipe() - if err != nil { - wsErr(conn, r, http.StatusInternalServerError, err) - return 0, nil - } - - stderr, err := cmd.StderrPipe() - if err != nil { - wsErr(conn, r, http.StatusInternalServerError, err) - return 0, nil - } - - if err := cmd.Start(); err != nil { - wsErr(conn, r, http.StatusInternalServerError, err) - return 0, nil - } - - s := bufio.NewScanner(io.MultiReader(stdout, stderr)) - for s.Scan() { - if err := conn.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { - log.Print(err) - } - } - - if err := cmd.Wait(); err != nil { - wsErr(conn, r, http.StatusInternalServerError, err) - } - - return 0, nil -}) + }) +} diff --git a/http/http.go b/http/http.go index a4b3c2d2..8c70c558 100644 --- a/http/http.go +++ b/http/http.go @@ -61,7 +61,7 @@ func NewHandler(imgSvc ImgService, fileCache FileCache, store *storage.Storage, api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET") api.PathPrefix("/preview/{size}/{path:.*}"). Handler(monkey(previewHandler(imgSvc, fileCache, server.EnableThumbnails, server.ResizePreview), "/api/preview")).Methods("GET") - api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET") + api.PathPrefix("/command").Handler(monkey(commandsHandler(server.EnableExec), "/api/command")).Methods("GET") api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET") public := api.PathPrefix("/public").Subrouter() diff --git a/http/static.go b/http/static.go index 32ed3fa1..ab203b8c 100644 --- a/http/static.go +++ b/http/static.go @@ -41,6 +41,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, box * "Theme": d.settings.Branding.Theme, "EnableThumbs": d.server.EnableThumbnails, "ResizePreview": d.server.ResizePreview, + "EnableExec": d.server.EnableExec, } if d.settings.Branding.Files != "" { diff --git a/settings/settings.go b/settings/settings.go index c6dbb0f9..6bf4c4db 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -40,6 +40,7 @@ type Server struct { Log string `json:"log"` EnableThumbnails bool `json:"enableThumbnails"` ResizePreview bool `json:"resizePreview"` + EnableExec bool `json:"enableExec"` } // Clean cleans any variables that might need cleaning.