Browse Source

cmd/syncthing: Use API to generate API Key and folder ID (fixes #3179)

Expose a random string generator in the API and use it when the GUI
needs random strings for API key and folder ID.

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3192
Jakob Borg 9 years ago
parent
commit
da5010d37a

+ 11 - 0
cmd/syncthing/gui.go

@@ -250,6 +250,7 @@ func (s *apiService) Serve() {
 	getRestMux.HandleFunc("/rest/svc/deviceid", s.getDeviceID)                   // id
 	getRestMux.HandleFunc("/rest/svc/lang", s.getLang)                           // -
 	getRestMux.HandleFunc("/rest/svc/report", s.getReport)                       // -
+	getRestMux.HandleFunc("/rest/svc/random/string", s.getRandomString)          // [length]
 	getRestMux.HandleFunc("/rest/system/browse", s.getSystemBrowse)              // current
 	getRestMux.HandleFunc("/rest/system/config", s.getSystemConfig)              // -
 	getRestMux.HandleFunc("/rest/system/config/insync", s.getSystemConfigInsync) // -
@@ -930,6 +931,16 @@ func (s *apiService) getReport(w http.ResponseWriter, r *http.Request) {
 	sendJSON(w, reportData(s.cfg, s.model))
 }
 
+func (s *apiService) getRandomString(w http.ResponseWriter, r *http.Request) {
+	length := 32
+	if val, _ := strconv.Atoi(r.URL.Query().Get("length")); val > 0 {
+		length = val
+	}
+	str := rand.String(length)
+
+	sendJSON(w, map[string]string{"random": str})
+}
+
 func (s *apiService) getDBIgnores(w http.ResponseWriter, r *http.Request) {
 	qs := r.URL.Query()
 

+ 50 - 0
cmd/syncthing/gui_test.go

@@ -9,6 +9,7 @@ package main
 import (
 	"bytes"
 	"compress/gzip"
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"net"
@@ -570,3 +571,52 @@ func TestCSRFRequired(t *testing.T) {
 		t.Fatal("Getting /rest/system/config with API key should succeed, not", resp.Status)
 	}
 }
+
+func TestRandomString(t *testing.T) {
+	const testAPIKey = "foobarbaz"
+	cfg := new(mockedConfig)
+	cfg.gui.APIKey = testAPIKey
+	baseURL, err := startHTTP(cfg)
+	if err != nil {
+		t.Fatal(err)
+	}
+	cli := &http.Client{
+		Timeout: time.Second,
+	}
+
+	// The default should be to return a 32 character random string
+
+	for _, url := range []string{"/rest/svc/random/string", "/rest/svc/random/string?length=-1", "/rest/svc/random/string?length=yo"} {
+		req, _ := http.NewRequest("GET", baseURL+url, nil)
+		req.Header.Set("X-API-Key", testAPIKey)
+		resp, err := cli.Do(req)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		var res map[string]string
+		if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
+			t.Fatal(err)
+		}
+		if len(res["random"]) != 32 {
+			t.Errorf("Expected 32 random characters, got %q of length %d", res["random"], len(res["random"]))
+		}
+	}
+
+	// We can ask for a different length if we like
+
+	req, _ := http.NewRequest("GET", baseURL+"/rest/svc/random/string?length=27", nil)
+	req.Header.Set("X-API-Key", testAPIKey)
+	resp, err := cli.Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res map[string]string
+	if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
+		t.Fatal(err)
+	}
+	if len(res["random"]) != 27 {
+		t.Errorf("Expected 27 random characters, got %q of length %d", res["random"], len(res["random"]))
+	}
+}

+ 0 - 13
gui/default/syncthing/app.js

@@ -89,19 +89,6 @@ function decimals(val, num) {
     return decs;
 }
 
-function randomString(len) {
-    var chars = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-';
-    return randomStringFromCharset(len, chars);
-}
-
-function randomStringFromCharset(len, charset) {
-    var result = '';
-    for (var i = 0; i < len; i++) {
-        result += charset[Math.round(Math.random() * (charset.length - 1))];
-    }
-    return result;
-}
-
 function isEmptyObject(obj) {
     var name;
     for (name in obj) {

+ 7 - 8
gui/default/syncthing/core/syncthingController.js

@@ -1196,7 +1196,6 @@ angular.module('syncthing.core')
         $scope.addFolder = function () {
             $scope.currentFolder = {
                 selectedDevices: {},
-                id: $scope.createRandomFolderId(),
                 type: "readwrite",
                 rescanIntervalS: 60,
                 minDiskFreePct: 1,
@@ -1213,7 +1212,10 @@ angular.module('syncthing.core')
             };
             $scope.editingExisting = false;
             $scope.folderEditor.$setPristine();
-            $('#editFolder').modal();
+            $http.get(urlbase + '/svc/random/string?length=10').success(function (data) {
+                $scope.currentFolder.id = data.random.substr(0, 5) + '-' + data.random.substr(5, 5);
+                $('#editFolder').modal();
+            });
         };
 
         $scope.addFolderAndShare = function (folder, folderLabel, device) {
@@ -1406,7 +1408,9 @@ angular.module('syncthing.core')
         };
 
         $scope.setAPIKey = function (cfg) {
-            cfg.apiKey = randomString(32);
+            $http.get(urlbase + '/svc/random/string?length=32').success(function (data) {
+                cfg.apiKey = data.random;
+            });
         };
 
         $scope.showURPreview = function () {
@@ -1544,11 +1548,6 @@ angular.module('syncthing.core')
             return 'text';
         };
 
-        $scope.createRandomFolderId = function(){
-            var charset = '2345679abcdefghijkmnopqrstuvwxyzACDEFGHJKLMNPQRSTUVWXYZ';
-            return randomStringFromCharset(5, charset) + "-" + randomStringFromCharset(5, charset);
-        };
-
         $scope.themeName = function (theme) {
             return theme.replace('-', ' ').replace(/(?:^|\s)\S/g, function (a) {
                 return a.toUpperCase();