diff --git a/README.md b/README.md index 542e3b54..558e04bc 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Remark42 is a self-hosted, lightweight, and simple (yet functional) comment engi | --backup | BACKUP_PATH | `/tmp` | no | backups location | | --max-back | MAX_BACKUP_FILES | `10` | no | max backup files to keep | | --session | SESSION_STORE | `/tmp` | no | path to session store directory | -| --store-key | STORE_KEY | `secure-store-key` | no | session store encryption key | +| --secret | SECRET | | no | secret key, required | | --max-comment | MAX_COMMENT_SIZE | 2048 | no | comment's size limit | | --google-cid | REMARK_GOOGLE_CID | | no | Google OAuth client ID | | --google-csec | REMARK_GOOGLE_CSEC | | no | Google OAuth client secret | @@ -49,6 +49,8 @@ Remark42 is a self-hosted, lightweight, and simple (yet functional) comment engi | --dbg | DEBUG | `false` | no | debug mode | | --dev-password | DEV_PASSWD | | no | password for `dev` user | +**User has to provide secret key, can be any long and hard-to-guess string.** + _all multi parameters separated by `,` in environment or repeated with command line key, like `--site=s1 --site=s2 ...`_ #### Register oauth2 providers diff --git a/app/main.go b/app/main.go index afdf81f9..ffc23dc7 100644 --- a/app/main.go +++ b/app/main.go @@ -36,8 +36,8 @@ var opts struct { SessionStore string `long:"session" env:"SESSION_STORE" default:"./var/session" description:"session store location"` AvatarStore string `long:"avatars" env:"AVATAR_STORE" default:"./var/avatars" description:"avatars location"` - StoreKey string `long:"store-key" env:"STORE_KEY" default:"secure-store-key" description:"store key"` MaxCommentSize int `long:"max-comment" env:"MAX_COMMENT_SIZE" default:"2048" description:"max comment size"` + SecretKey string `long:"secret" env:"SECRET_KEY" required:"true" description:"secret key"` GoogleCID string `long:"google-cid" env:"REMARK_GOOGLE_CID" description:"Google OAuth client ID"` GoogleCSEC string `long:"google-csec" env:"REMARK_GOOGLE_CSEC" description:"Google OAuth client secret"` @@ -77,12 +77,12 @@ func main() { dataService := store.Service{ Interface: dataStore, EditDuration: 5 * time.Minute, - Secret: opts.StoreKey, + Secret: opts.SecretKey, MaxCommentSize: opts.MaxCommentSize, } sessionStore := func() sessions.Store { - sess := sessions.NewFilesystemStore(opts.SessionStore, []byte(opts.StoreKey)) + sess := sessions.NewFilesystemStore(opts.SessionStore, []byte(opts.SecretKey)) sess.Options.HttpOnly = true sess.Options.Secure = true sess.Options.MaxAge = 3600 * 24 * 365 @@ -100,6 +100,7 @@ func main() { Cache: cache, NativeImporter: &migrator.Remark{DataStore: &dataService}, DisqusImporter: &migrator.Disqus{DataStore: &dataService}, + SecretKey: opts.SecretKey, } go importSrv.Run(opts.Port + 1) diff --git a/app/rest/api/import.go b/app/rest/api/import.go index dde76fb5..6945bd23 100644 --- a/app/rest/api/import.go +++ b/app/rest/api/import.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "net/http" + "strings" "time" "github.com/didip/tollbooth" @@ -22,6 +23,7 @@ type Import struct { Cache rest.LoadingCache NativeImporter migrator.Importer DisqusImporter migrator.Importer + SecretKey string httpServer *http.Server } @@ -44,9 +46,17 @@ func (s *Import) Run(port int) { log.Printf("[WARN] http server terminated, %s", err) } -// POST /import?site=site-id&provider=disqus|remark +// POST /import?secret=key&site=site-id&provider=disqus|remark // imports comments from post body. func (s *Import) importCtrl(w http.ResponseWriter, r *http.Request) { + + secret := r.URL.Query().Get("secret") + if strings.TrimSpace(secret) == "" || secret != s.SecretKey { + render.Status(r, http.StatusForbidden) + render.JSON(w, r, JSON{"status": "error", "details": "secret key"}) + return + } + siteID := r.URL.Query().Get("site") importer := s.NativeImporter if r.URL.Query().Get("provider") == "disqus" { diff --git a/app/rest/api/import_test.go b/app/rest/api/import_test.go index e0544c5e..2f951652 100644 --- a/app/rest/api/import_test.go +++ b/app/rest/api/import_test.go @@ -3,6 +3,7 @@ package api import ( "context" "fmt" + "io/ioutil" "math/rand" "net/http" "os" @@ -26,10 +27,28 @@ func TestImport(t *testing.T) { r := strings.NewReader(`{"id":"2aa0478c-df1b-46b1-b561-03d507cf482c","pid":"","text":"

test test #1

","user":{"name":"developer one","id":"dev","picture":"/api/v1/avatar/remark.image","profile":"https://remark42.com","admin":true,"ip":"ae12fe3b5f129b5cc4cdd2b136b7b7947c4d2741"},"locator":{"site":"radio-t","url":"https://radio-t.com/blah1"},"score":0,"votes":{},"time":"2018-04-30T01:37:00.849053725-05:00"} {"id":"83fd97fd-ff64-48d1-9fb7-ca7769c77037","pid":"p1","text":"

test test #2

","user":{"name":"developer one","id":"dev","picture":"/api/v1/avatar/remark.image","profile":"https://remark42.com","admin":true,"ip":"ae12fe3b5f129b5cc4cdd2b136b7b7947c4d2741"},"locator":{"site":"radio-t","url":"https://radio-t.com/blah2"},"score":0,"votes":{},"time":"2018-04-30T01:37:00.861387771-05:00"}`) - resp, err := http.Post(fmt.Sprintf("http://dev:password@127.0.0.1:%d/api/v1/admin/import?site=radio-t&provider=native", + resp, err := http.Post(fmt.Sprintf("http://dev:password@127.0.0.1:%d/api/v1/admin/import?site=radio-t&provider=native&secret=123456", port), "application/json", r) assert.Nil(t, err) assert.Equal(t, http.StatusCreated, resp.StatusCode) + + b, err := ioutil.ReadAll(resp.Body) + assert.Nil(t, err) + assert.Equal(t, `{"size":2,"status":"ok"}`+"\n", string(b)) +} + +func TestImportRejected(t *testing.T) { + srv, port := prepImportSrv(t) + assert.NotNil(t, srv) + defer cleanupImportSrv(srv) + + r := strings.NewReader(`{"id":"2aa0478c-df1b-46b1-b561-03d507cf482c","pid":"","text":"

test test #1

","user":{"name":"developer one","id":"dev","picture":"/api/v1/avatar/remark.image","profile":"https://remark42.com","admin":true,"ip":"ae12fe3b5f129b5cc4cdd2b136b7b7947c4d2741"},"locator":{"site":"radio-t","url":"https://radio-t.com/blah1"},"score":0,"votes":{},"time":"2018-04-30T01:37:00.849053725-05:00"} + {"id":"83fd97fd-ff64-48d1-9fb7-ca7769c77037","pid":"p1","text":"

test test #2

","user":{"name":"developer one","id":"dev","picture":"/api/v1/avatar/remark.image","profile":"https://remark42.com","admin":true,"ip":"ae12fe3b5f129b5cc4cdd2b136b7b7947c4d2741"},"locator":{"site":"radio-t","url":"https://radio-t.com/blah2"},"score":0,"votes":{},"time":"2018-04-30T01:37:00.861387771-05:00"}`) + + resp, err := http.Post(fmt.Sprintf("http://dev:password@127.0.0.1:%d/api/v1/admin/import?site=radio-t&provider=native&secret=badkey", + port), "application/json", r) + assert.Nil(t, err) + assert.Equal(t, http.StatusForbidden, resp.StatusCode) } func prepImportSrv(t *testing.T) (srv *Import, port int) { @@ -40,6 +59,7 @@ func prepImportSrv(t *testing.T) (srv *Import, port int) { DisqusImporter: &migrator.Disqus{DataStore: dataStore}, NativeImporter: &migrator.Remark{DataStore: dataStore}, Cache: &mockCache{}, + SecretKey: "123456", } portSetCh := make(chan bool) diff --git a/scripts/import-disqus.sh b/scripts/import-disqus.sh index e79164af..8217fb39 100644 --- a/scripts/import-disqus.sh +++ b/scripts/import-disqus.sh @@ -1,3 +1,3 @@ #!/bin/sh echo "import disqus file $1 to site $2" -curl -X POST -H "Content-Type: application/json" -d @/srv/var/$1 "http://127.0.0.1:8081/api/v1/admin/import?site=${2}&provider=disqus" +curl -X POST -H "Content-Type: application/json" -d @/srv/var/$1 "http://127.0.0.1:8081/api/v1/admin/import?site=${2}&provider=disqus&secret=${SECRET}" diff --git a/scripts/restore-backup.sh b/scripts/restore-backup.sh index 1d604c7c..228cabb4 100644 --- a/scripts/restore-backup.sh +++ b/scripts/restore-backup.sh @@ -4,8 +4,8 @@ echo "import backup file $1 to site $2" echo "unpack $1" gunzip -c /srv/var/backup/$1 >/tmp/backup.remark -size=${stat -c "%s" /tmp/backup.remark} +size=`stat -c "%s" /tmp/backup.remark` echo "source file size ${size}" -curl -X POST -H "Content-Type: application/json" -d @/tmp/backup.remark "http://127.0.0.1:8081/api/v1/admin/import?site=${2}&provider=native" +curl -X POST -H "Content-Type: application/json" -d @/tmp/backup.remark "http://127.0.0.1:8081/api/v1/admin/import?site=${2}&provider=native&secret=${SECRET}" rm -fq /tmp/backup.remark