diff --git a/backend/app/cmd/backup.go b/backend/app/cmd/backup.go index 584dab33..c0cae341 100644 --- a/backend/app/cmd/backup.go +++ b/backend/app/cmd/backup.go @@ -15,17 +15,18 @@ import ( // BackupCommand set of flags and command for export // ExportPath used as a separate element to leverage BACKUP_PATH. If ExportFile has a path (i.e. with /) BACKUP_PATH ignored. type BackupCommand struct { - ExportPath string `short:"p" long:"path" env:"BACKUP_PATH" default:"./var/backup" description:"export path"` - ExportFile string `short:"f" long:"file" default:"userbackup-{{.SITE}}-{{.TS}}.gz" description:"file name"` - Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"` - Timeout time.Duration `long:"timeout" default:"15m" description:"export (backup) timeout"` + ExportPath string `short:"p" long:"path" env:"BACKUP_PATH" default:"./var/backup" description:"export path"` + ExportFile string `short:"f" long:"file" default:"userbackup-{{.SITE}}-{{.TS}}.gz" description:"file name"` + Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"` + Timeout time.Duration `long:"timeout" default:"15m" description:"export (backup) timeout"` + AdminPasswd string `long:"admin-passwd" env:"ADMIN_PASSWD" required:"true" description:"admin basic auth password"` CommonOpts } // Execute runs export with ExportCommand parameters, entry point for "export" command func (ec *BackupCommand) Execute(args []string) error { log.Printf("[INFO] export to %s, site %s", ec.ExportPath, ec.Site) - resetEnv("SECRET") + resetEnv("SECRET", "ADMIN_PASSWD") fp := fileParser{site: ec.Site, path: ec.ExportPath, file: ec.ExportFile} fname, err := fp.parse(time.Now()) @@ -39,11 +40,12 @@ func (ec *BackupCommand) Execute(args []string) error { client := http.Client{} ctx, cancel := context.WithTimeout(context.Background(), ec.Timeout) defer cancel() - exportURL := fmt.Sprintf("%s/api/v1/admin/export?mode=file&site=%s&secret=%s", ec.RemarkURL, ec.Site, ec.SharedSecret) + exportURL := fmt.Sprintf("%s/api/v1/admin/export?mode=file&site=%s", ec.RemarkURL, ec.Site) req, err := http.NewRequest(http.MethodGet, exportURL, nil) if err != nil { return errors.Wrapf(err, "can't make export request for %s", exportURL) } + req.SetBasicAuth("admin", ec.AdminPasswd) // get with timeout resp, err := client.Do(req.WithContext(ctx)) diff --git a/backend/app/cmd/backup_test.go b/backend/app/cmd/backup_test.go index e163d0f4..612db960 100644 --- a/backend/app/cmd/backup_test.go +++ b/backend/app/cmd/backup_test.go @@ -8,7 +8,7 @@ import ( "os" "testing" - "github.com/jessevdk/go-flags" + flags "github.com/jessevdk/go-flags" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -24,7 +24,7 @@ func TestBackup_Execute(t *testing.T) { cmd := BackupCommand{} cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) - _, err := p.ParseArgs([]string{"--site=remark", "--path=/tmp", "--file={{.SITE}}-test.export"}) + _, err := p.ParseArgs([]string{"--site=remark", "--path=/tmp", "--file={{.SITE}}-test.export", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) assert.NoError(t, err) @@ -48,7 +48,7 @@ func TestBackup_ExecuteFailedStatus(t *testing.T) { cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) - _, err := p.ParseArgs([]string{"--site=remark", "--path=/tmp", "--file={{.SITE}}-test.export"}) + _, err := p.ParseArgs([]string{"--site=remark", "--path=/tmp", "--file={{.SITE}}-test.export", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) assert.EqualError(t, err, `error response "400 Bad Request", some error`) @@ -66,7 +66,8 @@ func TestBackup_ExecuteFailedWrite(t *testing.T) { cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) - _, err := p.ParseArgs([]string{"--site=remark", "--path=/tmp", "--file=/tmp/no-such-dir/{{.SITE}}-test.export"}) + _, err := p.ParseArgs([]string{"--site=remark", "--path=/tmp", + "--file=/tmp/no-such-dir/{{.SITE}}-test.export", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) assert.EqualError(t, err, `can't create backup file /tmp/no-such-dir/remark-test.export: open /tmp/no-such-dir/remark-test.export: no such file or directory`) diff --git a/backend/app/cmd/cleanup.go b/backend/app/cmd/cleanup.go index f1211bad..cb06dad9 100644 --- a/backend/app/cmd/cleanup.go +++ b/backend/app/cmd/cleanup.go @@ -15,12 +15,13 @@ import ( // CleanupCommand set of flags and command for cleanup type CleanupCommand struct { - Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"` - Dry bool `long:"dry" description:"dry mode, will not remove comments"` - From string `long:"from" description:"from yyyymmdd"` - To string `long:"to" description:"from yyyymmdd"` - BadWords []string `short:"w" long:"bword" description:"bad word(s)"` - BadUsers []string `short:"u" long:"buser" description:"bad user(s)"` + Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"` + Dry bool `long:"dry" description:"dry mode, will not remove comments"` + From string `long:"from" description:"from yyyymmdd"` + To string `long:"to" description:"from yyyymmdd"` + BadWords []string `short:"w" long:"bword" description:"bad word(s)"` + BadUsers []string `short:"u" long:"buser" description:"bad user(s)"` + AdminPasswd string `long:"admin-passwd" env:"ADMIN_PASSWD" required:"true" description:"admin basic auth password"` CommonOpts } @@ -160,12 +161,13 @@ func (cc *CleanupCommand) listComments(postURL string) ([]store.Comment, error) // deleteComment with DELETE /admin/comment/{id}?site=siteID&url=post-url func (cc *CleanupCommand) deleteComment(c store.Comment) error { - deleteURL := fmt.Sprintf("%s/api/v1/admin/comment/%s?site=%s&url=%s&format=plain&secret=%s", - cc.RemarkURL, c.ID, cc.Site, c.Locator.URL, cc.SharedSecret) + deleteURL := fmt.Sprintf("%s/api/v1/admin/comment/%s?site=%s&url=%s&format=plain", cc.RemarkURL, c.ID, cc.Site, c.Locator.URL) req, err := http.NewRequest("DELETE", deleteURL, nil) if err != nil { return errors.Wrapf(err, "failed to make delete request for comment %s, %s", c.ID, c.Locator.URL) } + req.SetBasicAuth("admin", cc.AdminPasswd) + client := http.Client{} r, err := client.Do(req) if err != nil { diff --git a/backend/app/cmd/cleanup_test.go b/backend/app/cmd/cleanup_test.go index 9cc8154c..1603ab40 100644 --- a/backend/app/cmd/cleanup_test.go +++ b/backend/app/cmd/cleanup_test.go @@ -67,7 +67,7 @@ func TestCleanup_postsInRange(t *testing.T) { cmd := CleanupCommand{} cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) - _, err := p.ParseArgs([]string{"--site=remark", "--bword=bad1", "--bword=bad2", "--buser=bu_"}) + _, err := p.ParseArgs([]string{"--site=remark", "--bword=bad1", "--bword=bad2", "--buser=bu_", "--admin-passwd=secret"}) require.Nil(t, err) posts, err := cmd.postsInRange("20181218", "20181219") assert.NoError(t, err) @@ -90,7 +90,7 @@ func TestCleanup_listComments(t *testing.T) { cmd := CleanupCommand{} cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) - _, err := p.ParseArgs([]string{"--site=remark", "--bword=bad1", "--bword=bad2", "--buser=bu_"}) + _, err := p.ParseArgs([]string{"--site=remark", "--bword=bad1", "--bword=bad2", "--buser=bu_", "--admin-passwd=secret"}) require.Nil(t, err) comments, err := cmd.listComments("http://test.com/post1") @@ -117,7 +117,7 @@ func TestCleanup_Execute(t *testing.T) { cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) _, err := p.ParseArgs([]string{"--site=remark", "--bword=bad1", "--bword=bad2", "--buser=bu_", - "--from=20181217", "--to=20181218"}) + "--from=20181217", "--to=20181218", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) assert.NoError(t, err) diff --git a/backend/app/cmd/import.go b/backend/app/cmd/import.go index 2e34c445..3b3d85ee 100644 --- a/backend/app/cmd/import.go +++ b/backend/app/cmd/import.go @@ -17,17 +17,18 @@ import ( // ImportCommand set of flags and command for import type ImportCommand struct { - InputFile string `short:"f" long:"file" description:"input file name" required:"true"` - Provider string `short:"p" long:"provider" default:"disqus" choice:"disqus" choice:"wordpress" description:"import format"` - Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"` - Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"` + InputFile string `short:"f" long:"file" description:"input file name" required:"true"` + Provider string `short:"p" long:"provider" default:"disqus" choice:"disqus" choice:"wordpress" description:"import format"` + Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"` + Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"` + AdminPasswd string `long:"admin-passwd" env:"ADMIN_PASSWD" required:"true" description:"admin basic auth password"` CommonOpts } // Execute runs import with ImportCommand parameters, entry point for "import" command func (ic *ImportCommand) Execute(args []string) error { log.Printf("[INFO] import %s (%s), site %s", ic.InputFile, ic.Provider, ic.Site) - resetEnv("SECRET") + resetEnv("SECRET", "ADMIN_PASSWD") reader, err := ic.reader(ic.InputFile) if err != nil { @@ -37,12 +38,12 @@ func (ic *ImportCommand) Execute(args []string) error { client := http.Client{} ctx, cancel := context.WithTimeout(context.Background(), ic.Timeout) defer cancel() - importURL := fmt.Sprintf("%s/api/v1/admin/import?site=%s&provider=%s&secret=%s", - ic.RemarkURL, ic.Site, ic.Provider, ic.SharedSecret) + importURL := fmt.Sprintf("%s/api/v1/admin/import?site=%s&provider=%s", ic.RemarkURL, ic.Site, ic.Provider) req, err := http.NewRequest(http.MethodPost, importURL, reader) if err != nil { return errors.Wrapf(err, "can't make import request for %s", importURL) } + req.SetBasicAuth("admin", ic.AdminPasswd) resp, err := client.Do(req.WithContext(ctx)) // closes request's reader if err != nil { diff --git a/backend/app/cmd/import_test.go b/backend/app/cmd/import_test.go index 7e5a9ddb..9c50355b 100644 --- a/backend/app/cmd/import_test.go +++ b/backend/app/cmd/import_test.go @@ -33,7 +33,7 @@ func TestImport_Execute(t *testing.T) { cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) - _, err := p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt"}) + _, err := p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) assert.NoError(t, err) @@ -42,7 +42,7 @@ func TestImport_Execute(t *testing.T) { cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p = flags.NewParser(&cmd, flags.Default) - _, err = p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt.gz"}) + _, err = p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt.gz", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) assert.NoError(t, err) @@ -60,7 +60,7 @@ func TestImport_ExecuteFailed(t *testing.T) { cmd := ImportCommand{} cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) - _, err := p.ParseArgs([]string{"--site=remark", "--file=testdata/import-no.txt"}) + _, err := p.ParseArgs([]string{"--site=remark", "--file=testdata/import-no.txt", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) t.Log(err) @@ -70,7 +70,7 @@ func TestImport_ExecuteFailed(t *testing.T) { cmd = ImportCommand{} cmd.SetCommon(CommonOpts{RemarkURL: "http://127.0.0.1:12345", SharedSecret: "123456"}) p = flags.NewParser(&cmd, flags.Default) - _, err = p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt"}) + _, err = p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) t.Log(err) @@ -86,7 +86,7 @@ func TestImport_ExecuteFailed(t *testing.T) { cmd = ImportCommand{} cmd.SetCommon(CommonOpts{RemarkURL: ts2.URL, SharedSecret: "123456"}) p = flags.NewParser(&cmd, flags.Default) - _, err = p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt"}) + _, err = p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) t.Log(err) @@ -111,7 +111,7 @@ func TestImport_ExecuteTimeout(t *testing.T) { cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) - _, err := p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt", "--timeout=300ms"}) + _, err := p.ParseArgs([]string{"--site=remark", "--file=testdata/import.txt", "--timeout=300ms", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) assert.NotNil(t, err) diff --git a/backend/app/cmd/restore.go b/backend/app/cmd/restore.go index fe54ea33..708e1975 100644 --- a/backend/app/cmd/restore.go +++ b/backend/app/cmd/restore.go @@ -10,8 +10,9 @@ type RestoreCommand struct { ImportPath string `short:"p" long:"path" env:"BACKUP_PATH" default:"./var/backup" description:"export path"` ImportFile string `short:"f" long:"file" default:"userbackup-{{.SITE}}-{{.YYYYMMDD}}.gz" description:"file name" required:"true"` - Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"` - Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"` + Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"` + Timeout time.Duration `long:"timeout" default:"15m" description:"import timeout"` + AdminPasswd string `long:"admin-passwd" env:"ADMIN_PASSWD" required:"true" description:"admin basic auth password"` CommonOpts } @@ -19,7 +20,7 @@ type RestoreCommand struct { // uses ImportCommand with constructed full file name func (rc *RestoreCommand) Execute(args []string) error { log.Printf("[INFO] restore %s, site %s", rc.ImportFile, rc.Site) - resetEnv("SECRET") + resetEnv("SECRET", "ADMIN_PASSWD") fp := fileParser{site: rc.Site, path: rc.ImportPath, file: rc.ImportFile} fname, err := fp.parse(time.Now()) @@ -27,11 +28,12 @@ func (rc *RestoreCommand) Execute(args []string) error { return err } importer := ImportCommand{ - InputFile: fname, - Site: rc.Site, - Provider: "native", - Timeout: rc.Timeout, - CommonOpts: rc.CommonOpts, + InputFile: fname, + Site: rc.Site, + Provider: "native", + Timeout: rc.Timeout, + AdminPasswd: rc.AdminPasswd, + CommonOpts: rc.CommonOpts, } return importer.Execute(args) } diff --git a/backend/app/cmd/restore_test.go b/backend/app/cmd/restore_test.go index 5bd5e8fc..0e425476 100644 --- a/backend/app/cmd/restore_test.go +++ b/backend/app/cmd/restore_test.go @@ -31,7 +31,7 @@ func TestRestore_Execute(t *testing.T) { cmd.SetCommon(CommonOpts{RemarkURL: ts.URL, SharedSecret: "123456"}) p := flags.NewParser(&cmd, flags.Default) - _, err := p.ParseArgs([]string{"--site=remark", "--path=testdata", "--file=import.txt"}) + _, err := p.ParseArgs([]string{"--site=remark", "--path=testdata", "--file=import.txt", "--admin-passwd=secret"}) require.Nil(t, err) err = cmd.Execute(nil) assert.NoError(t, err) diff --git a/backend/app/cmd/server.go b/backend/app/cmd/server.go index 62951d2c..7a751c1c 100644 --- a/backend/app/cmd/server.go +++ b/backend/app/cmd/server.go @@ -161,7 +161,7 @@ type serverApp struct { // Execute is the entry point for "server" command, called by flag parser func (s *ServerCommand) Execute(args []string) error { log.Printf("[INFO] start server on port %d", s.Port) - resetEnv("SECRET", "AUTH_GOOGLE_CSEC", "AUTH_GITHUB_CSEC", "AUTH_FACEBOOK_CSEC", "AUTH_YANDEX_CSEC") + resetEnv("SECRET", "AUTH_GOOGLE_CSEC", "AUTH_GITHUB_CSEC", "AUTH_FACEBOOK_CSEC", "AUTH_YANDEX_CSEC", "ADMIN_PASSWD") ctx, cancel := context.WithCancel(context.Background()) go func() { // catch signal and invoke graceful termination