From a83ae9ceb31d4991f30584abb4685ad0f93f37fb Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Wed, 18 Oct 2023 12:34:02 +0200 Subject: [PATCH 001/212] Add basic-auth / header addition to OTS-CLI closes #134 Signed-off-by: Knut Ahlers --- README.md | 4 +++ cmd/ots-cli/cmd_create.go | 71 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f187694..1db58f8 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,10 @@ Both commands can be used in scripts: - `fetch` prints the secret to `STDOUT` and stores files to the given directory - both sends logs to `STDERR` which you can disable (`--log-level=fatal`) or ignore in your script +In case your instance needs credentials to use the `/api/create` endpoint you can pass them to OTS-CLI like you would do with curl: +- `ots-cli create --instance ... -u myuser:mypass` for basic-auth +- `ots-cli create --instance ... -H 'Authorization: Token abcde'` for token-auth (you can set any header you need, just repeat `-H ...`) + ### Bash: Sharing an encrypted secret (strongly recommended!) This is slightly more complex as you first need to encrypt your secret before sending it to the API but in this case you can be sure the server will in no case be able to access the secret. Especially if you are using ots.fyi (my public hosted instance) you should not trust me with your secret but use an encrypted secret: diff --git a/cmd/ots-cli/cmd_create.go b/cmd/ots-cli/cmd_create.go index e0f3018..bc7e5d9 100644 --- a/cmd/ots-cli/cmd_create.go +++ b/cmd/ots-cli/cmd_create.go @@ -4,14 +4,25 @@ import ( "fmt" "io" "mime" + "net/http" "os" "path" + "strings" "github.com/Luzifer/ots/pkg/client" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) +type ( + authRoundTripper struct { + http.RoundTripper + + headers http.Header + user, pass string + } +) + var createCmd = &cobra.Command{ Use: "create [-f file]... [--instance url] [--secret-from file]", Short: "Create a new encrypted secret in the given OTS instance", @@ -23,15 +34,21 @@ var createCmd = &cobra.Command{ func init() { createCmd.Flags().Duration("expire", 0, "When to expire the secret (0 to use server-default)") + createCmd.Flags().StringSliceP("header", "H", nil, "Headers to include in the request (i.e. 'Authorization: Token ...')") createCmd.Flags().String("instance", "https://ots.fyi/", "Instance to create the secret with") createCmd.Flags().StringSliceP("file", "f", nil, "File(s) to attach to the secret") createCmd.Flags().String("secret-from", "-", `File to read the secret content from ("-" for STDIN)`) + createCmd.Flags().StringP("user", "u", "", "Username / Password for basic auth, specified as 'user:pass'") rootCmd.AddCommand(createCmd) } -func createRunE(cmd *cobra.Command, _ []string) error { +func createRunE(cmd *cobra.Command, _ []string) (err error) { var secret client.Secret + if client.HTTPClient, err = constructHTTPClient(cmd); err != nil { + return fmt.Errorf("constructing authorized HTTP client: %w", err) + } + // Read the secret content logrus.Info("reading secret content...") secretSourceName, err := cmd.Flags().GetString("secret-from") @@ -103,3 +120,55 @@ func createRunE(cmd *cobra.Command, _ []string) error { return nil } + +func constructHTTPClient(cmd *cobra.Command) (*http.Client, error) { + basic, _ := cmd.Flags().GetString("user") + headers, _ := cmd.Flags().GetStringSlice("header") + + if basic == "" && headers == nil { + // No authorization needed + return http.DefaultClient, nil + } + + t := authRoundTripper{RoundTripper: http.DefaultTransport, headers: http.Header{}} + + // Set basic auth if available + user, pass, ok := strings.Cut(basic, ":") + if ok { + t.user = user + t.pass = pass + } + + // Parse and set headers if available + for _, hdr := range headers { + key, value, ok := strings.Cut(hdr, ":") + if !ok { + logrus.WithField("header", hdr).Warn("invalid header format, skipping") + continue + } + t.headers.Add(key, strings.TrimSpace(value)) + } + + return &http.Client{Transport: t}, nil +} + +func (a authRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { + if a.user != "" { + r.SetBasicAuth(a.user, a.pass) + } + + for key, values := range a.headers { + if r.Header == nil { + r.Header = http.Header{} + } + for _, value := range values { + r.Header.Add(key, value) + } + } + + resp, err := a.RoundTripper.RoundTrip(r) + if err != nil { + return nil, fmt.Errorf("executing round-trip: %w", err) + } + return resp, nil +} From 9322acfcb586842090655ace69a58979e59c32a9 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Wed, 18 Oct 2023 15:09:52 +0200 Subject: [PATCH 002/212] Fix: Remove path from filename if given fixes #135 Signed-off-by: Knut Ahlers --- cmd/ots-cli/cmd_create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ots-cli/cmd_create.go b/cmd/ots-cli/cmd_create.go index bc7e5d9..4d4d40d 100644 --- a/cmd/ots-cli/cmd_create.go +++ b/cmd/ots-cli/cmd_create.go @@ -87,7 +87,7 @@ func createRunE(cmd *cobra.Command, _ []string) (err error) { } secret.Attachments = append(secret.Attachments, client.SecretAttachment{ - Name: f, + Name: path.Base(f), Type: mime.TypeByExtension(path.Ext(f)), Content: content, }) From 3651636a06c7bd4dbae9777e89b0459d9c7ade53 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Wed, 18 Oct 2023 15:31:54 +0200 Subject: [PATCH 003/212] prepare release v1.9.2 --- History.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 964c636..1d71a35 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +# 1.9.2 / 2023-10-18 + + * Add basic-auth / header addition to OTS-CLI + * Fix: Remove path from filename if given + # 1.9.1 / 2023-10-18 * Fix: Customize to disable powered by was ignored @@ -315,4 +320,4 @@ Many thanks to [@sorcix](https://github.com/sorcix) for the contributions to thi # 0.1.0 / 2017-08-03 - * Initial Version \ No newline at end of file + * Initial Version From e3f790e92ec02addbf378b416e1ae24cd29341be Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Wed, 18 Oct 2023 19:14:13 +0200 Subject: [PATCH 004/212] Fix: Clean error on component navigation fixes #136 Signed-off-by: Knut Ahlers --- src/main.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.js b/src/main.js index 6c88c44..74feb9e 100644 --- a/src/main.js +++ b/src/main.js @@ -24,6 +24,14 @@ const i18n = new VueI18n({ messages, }) +Vue.mixin({ + beforeRouteLeave(_to, _from, next) { + // Before leaving the component, reset the errors the component displayed + this.$emit('error', null) + next() + }, +}) + new Vue({ components: { app }, From 8a7b9afaa3c33ee125a79af652df9eb505f0b88f Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Fri, 20 Oct 2023 19:01:32 +0200 Subject: [PATCH 005/212] Add frontend check for invalid attached files (#139) --- i18n.yaml | 2 ++ src/components/create.vue | 58 +++++++++++++++++++++++++++++++++++---- src/langs/langs.js | 6 ++-- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/i18n.yaml b/i18n.yaml index 668a8ab..b433f1a 100644 --- a/i18n.yaml +++ b/i18n.yaml @@ -32,6 +32,7 @@ reference: text-burn-hint: Please remember not to go to this URL yourself as that would destroy the secret. Just pass it to someone else! text-burn-time: 'If not viewed before, this secret will automatically be deleted:' text-hint-burned: Attention: You're only seeing this once. As soon as you reload the page the secret will be gone so maybe copy it now… + text-invalid-files-selected: At least one of the selected files is not allowed as an attachment. text-max-filesize: 'Maximum size: {maxSize}' text-max-filesize-exceeded: 'The file(s) you chose are too big to attach: {curSize} / {maxSize}' text-powered-by: Powered by @@ -120,6 +121,7 @@ translations: text-burn-hint: Bitte rufe die URL nicht selbst auf, da das Secret dadurch zerstört würde. Gib sie einfach weiter! text-burn-time: 'Wenn es vorher nicht eingesehen wurde, wird dieses Secret automatisch gelöscht:' text-hint-burned: Achtung: Du kannst das nur einmal ansehen! Sobald du die Seite neu lädst, ist das Secret verschwunden, also besser direkt kopieren und sicher abspeichern… + text-invalid-files-selected: Mindestens eine der ausgewählten Dateien ist nicht als Anhang erlaubt. text-max-filesize: 'Maximale Größe: {maxSize}' text-max-filesize-exceeded: 'Die ausgewählten Dateien übersteigen die maximale Größe: {curSize} / {maxSize}' text-powered-by: Läuft mit diff --git a/src/components/create.vue b/src/components/create.vue index 111ce75..8ea4d8e 100644 --- a/src/components/create.vue +++ b/src/components/create.vue @@ -50,13 +50,19 @@ type="file" multiple :accept="$root.customize.acceptedFileTypes" - @change="updateFileSize" + @change="updateFileMeta" >
{{ $t('text-max-filesize', { maxSize: bytesToHuman(maxFileSize) }) }}
+ {{ $t('text-invalid-files-selected') }} +
+
{{ $t('text-max-filesize-exceeded', { curSize: bytesToHuman(fileSize), maxSize: bytesToHuman(maxFileSize) }) }} @@ -66,7 +72,7 @@