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
This commit is contained in:
Jakob Borg 2016-05-26 07:25:34 +00:00 committed by Audrius Butkevicius
parent e6b78e5d56
commit da5010d37a
4 changed files with 68 additions and 21 deletions

View File

@ -250,6 +250,7 @@ func (s *apiService) Serve() {
getRestMux.HandleFunc("/rest/svc/deviceid", s.getDeviceID) // id getRestMux.HandleFunc("/rest/svc/deviceid", s.getDeviceID) // id
getRestMux.HandleFunc("/rest/svc/lang", s.getLang) // - getRestMux.HandleFunc("/rest/svc/lang", s.getLang) // -
getRestMux.HandleFunc("/rest/svc/report", s.getReport) // - 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/browse", s.getSystemBrowse) // current
getRestMux.HandleFunc("/rest/system/config", s.getSystemConfig) // - getRestMux.HandleFunc("/rest/system/config", s.getSystemConfig) // -
getRestMux.HandleFunc("/rest/system/config/insync", s.getSystemConfigInsync) // - 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)) 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) { func (s *apiService) getDBIgnores(w http.ResponseWriter, r *http.Request) {
qs := r.URL.Query() qs := r.URL.Query()

View File

@ -9,6 +9,7 @@ package main
import ( import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
@ -570,3 +571,52 @@ func TestCSRFRequired(t *testing.T) {
t.Fatal("Getting /rest/system/config with API key should succeed, not", resp.Status) 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"]))
}
}

View File

@ -89,19 +89,6 @@ function decimals(val, num) {
return decs; 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) { function isEmptyObject(obj) {
var name; var name;
for (name in obj) { for (name in obj) {

View File

@ -1196,7 +1196,6 @@ angular.module('syncthing.core')
$scope.addFolder = function () { $scope.addFolder = function () {
$scope.currentFolder = { $scope.currentFolder = {
selectedDevices: {}, selectedDevices: {},
id: $scope.createRandomFolderId(),
type: "readwrite", type: "readwrite",
rescanIntervalS: 60, rescanIntervalS: 60,
minDiskFreePct: 1, minDiskFreePct: 1,
@ -1213,7 +1212,10 @@ angular.module('syncthing.core')
}; };
$scope.editingExisting = false; $scope.editingExisting = false;
$scope.folderEditor.$setPristine(); $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) { $scope.addFolderAndShare = function (folder, folderLabel, device) {
@ -1406,7 +1408,9 @@ angular.module('syncthing.core')
}; };
$scope.setAPIKey = function (cfg) { $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 () { $scope.showURPreview = function () {
@ -1544,11 +1548,6 @@ angular.module('syncthing.core')
return 'text'; return 'text';
}; };
$scope.createRandomFolderId = function(){
var charset = '2345679abcdefghijkmnopqrstuvwxyzACDEFGHJKLMNPQRSTUVWXYZ';
return randomStringFromCharset(5, charset) + "-" + randomStringFromCharset(5, charset);
};
$scope.themeName = function (theme) { $scope.themeName = function (theme) {
return theme.replace('-', ' ').replace(/(?:^|\s)\S/g, function (a) { return theme.replace('-', ' ').replace(/(?:^|\s)\S/g, function (a) {
return a.toUpperCase(); return a.toUpperCase();