From 7678fb226d1b00308f7736a1032b07f696e71922 Mon Sep 17 00:00:00 2001 From: Maciej Wereski Date: Fri, 29 Sep 2017 12:22:28 +0200 Subject: [PATCH] HTTP API: Implement acquiring worker AccessInfo structure provides key in golang rsa.PrivateKey format, so handler repacks it and replies with AccessInfo2 structure, which has key in PEM format. This is temporary solution - private keys will be removed when proper user support is added (user public key will be used). Change-Id: Ia4b8bc4a0ed007f0a6a0c0ff8e3ef48750646e51 Signed-off-by: Maciej Wereski --- server/api/v1/api.go | 14 ++++++++ server/api/v1/handlers.go | 23 ++++++++++++- server/api/v1/handlers_test.go | 38 +++++++++++++++++++--- server/api/v1/testdata/acquire-worker-POST.json | 1 - .../v1/testdata/acquire-worker-bad-id-POST.json | 1 + .../v1/testdata/acquire-worker-missing-POST.json | 1 + .../api/v1/testdata/acquire-worker-valid-POST.json | 1 + 7 files changed, 72 insertions(+), 7 deletions(-) delete mode 100644 server/api/v1/testdata/acquire-worker-POST.json create mode 100644 server/api/v1/testdata/acquire-worker-bad-id-POST.json create mode 100644 server/api/v1/testdata/acquire-worker-missing-POST.json create mode 100644 server/api/v1/testdata/acquire-worker-valid-POST.json diff --git a/server/api/v1/api.go b/server/api/v1/api.go index 9022419..5cff344 100644 --- a/server/api/v1/api.go +++ b/server/api/v1/api.go @@ -22,6 +22,7 @@ package v1 import ( "encoding/json" "fmt" + "net" "net/http" "strconv" @@ -38,6 +39,19 @@ type reqIDPack struct { ReqID } +// AccessInfo2 structure is used by HTTP instead of AccessInfo when acquiring +// worker. The only difference is that key field is in PEM format instead of +// rsa.PrivateKey. It is temporary solution - session private keys will be +// replaces with users' public keys when proper user support is added. +type AccessInfo2 struct { + // Addr is necessary information to connect to a tunnel to Dryad. + Addr net.Addr + // Key is private RSA key in PEM format. + Key string + // Username is a login name for the job session. + Username string +} + // reqHandler denotes function that parses HTTP request and returns responseData. type reqHandler func(*http.Request, map[string]string) responseData diff --git a/server/api/v1/handlers.go b/server/api/v1/handlers.go index 80aa825..8ab22b6 100644 --- a/server/api/v1/handlers.go +++ b/server/api/v1/handlers.go @@ -19,7 +19,9 @@ package v1 import ( + "crypto/x509" "encoding/json" + "encoding/pem" "io" "net/http" @@ -111,7 +113,26 @@ func (api *API) listRequestsHandler(r *http.Request, ps map[string]string) respo // acquireWorkerHandler parses HTTP request for acquiring worker for Boruta // request and calls AcquireWorker(). func (api *API) acquireWorkerHandler(r *http.Request, ps map[string]string) responseData { - return newServerError(ErrNotImplemented, "acquire worker") + defer r.Body.Close() + + reqid, err := parseReqID(ps["id"]) + if err != nil { + return newServerError(ErrBadID) + } + + accessInfo, err := api.reqs.AcquireWorker(reqid) + if err != nil { + return newServerError(err) + } + key := string(pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(&accessInfo.Key), + })) + return AccessInfo2{ + Addr: accessInfo.Addr, + Key: key, + Username: accessInfo.Username, + } } // prolongAccessHandler parses HTTP request for prolonging previously acquired diff --git a/server/api/v1/handlers_test.go b/server/api/v1/handlers_test.go index 9d8a556..3fcf486 100644 --- a/server/api/v1/handlers_test.go +++ b/server/api/v1/handlers_test.go @@ -16,9 +16,12 @@ package v1 import ( + "crypto/x509" "encoding/json" + "encoding/pem" "errors" "fmt" + "net" "net/http" "testing" "time" @@ -269,15 +272,40 @@ func TestAcquireWorkerHandler(t *testing.T) { assert, m, api := initTest(t) defer m.finish() + methods := []string{http.MethodPost} + prefix := "acquire-worker-" + pathfmt := "/api/v1/reqs/%s/acquire_worker" + + keyPem := "-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQCyBgKbrwKh75BDoigwltbazFGDLdlxf9YLpFj5v+4ieKgsaN+W\n+kRvamSuB5CC2tqFql5x7kPt1U+vVMwkzVRewF/HHzRYxgLHlge6d1ZALpCWywaz\nslt5pNCmF7NoZ//WTSrafufDI4IRoNgkHtEKvnWdBaPPnY4Cf+PCbZOYNQIDAQAB\nAoGBAJvoz5fxKekQmdPhzDjhocF1d13fZbQVNSx0/seb476k1QQvxMHA5PZ+wzX2\nwgUYDpFJp/U3qp48VtFC/pasjNoG7zLPLLUJcg15eOoh4Ld7I1e4lRkLl3CwnqMk\nbc6UoKQRLli4O3cmaMxVHXal0o72s3o0qnHlRlZXLekwi6aBAkEA69j3bnbAybsF\n/NHelRYDH8bou+LCX2d/p6ReUR0bJ4yCAWRi/9Ld0ng482xinxGSpfovbIplBMFx\nH2eT2Cw0OQJBAME8LLz/3zb/vLG/t8Lfsequ1ZhVca/LlVR4yJLlyaVcywT9SJlO\nmKCy13SpKl8TY7czyufYrY4lobZjYaIsm90CQQCKhkRGWG/BzRymMyp2DJjHKFB4\nUqbx3FuJPqy7HcpeP1P4t1rCgbsSLNTefRGr9mlZHYqPSPYuheQImxCmTshZAkEA\nwAp5u+vfft1yPoT2r+l4/G99P8PLFJcTdbwEOlm8qWcrLW47dIE0FqEml3536b1v\nYGdMxFYHRjoIGSdzpKUI0QJAAqPdDp+y7kWaeIbKkp3Z3bLrj5wii2QAy2YlBDKe\npXrvruWJvL75OCYcxRju3DpVaoYqEmso+UEiQEDRB42YYg==\n-----END RSA PRIVATE KEY-----\n" + block, _ := pem.Decode([]byte(keyPem)) + assert.NotNil(block) + key, _ := x509.ParsePKCS1PrivateKey(block.Bytes) + + access := AccessInfo{ + Addr: &net.TCPAddr{ + IP: net.IPv4(127, 0, 0, 1), + Port: 22, + }, + Key: *key, + Username: "wołchw", + } + + m.rq.EXPECT().AcquireWorker(ReqID(1)).Return(access, nil) + notFoundTest := testFromTempl(notFoundTestTempl, prefix, fmt.Sprintf(pathfmt, "2"), methods...) + m.rq.EXPECT().AcquireWorker(ReqID(2)).Return(AccessInfo{}, NotFoundError("Request")) + invalidIDTest := testFromTempl(invalidIDTestTempl, prefix, fmt.Sprintf(pathfmt, invalidID), methods...) + tests := []requestTest{ { - name: "acquire-worker", - path: "/api/v1/reqs/8/acquire_worker", - methods: []string{http.MethodPost}, - json: ``, + name: prefix + "valid", + path: fmt.Sprintf(pathfmt, "1"), + methods: methods, + json: string(jsonMustMarshal(access)), contentType: contentTypeJSON, - status: http.StatusNotImplemented, + status: http.StatusOK, }, + notFoundTest, + invalidIDTest, } runTests(assert, api, tests) diff --git a/server/api/v1/testdata/acquire-worker-POST.json b/server/api/v1/testdata/acquire-worker-POST.json deleted file mode 100644 index 6cada1f..0000000 --- a/server/api/v1/testdata/acquire-worker-POST.json +++ /dev/null @@ -1 +0,0 @@ -{"error":"not implemented yet: acquire worker"} \ No newline at end of file diff --git a/server/api/v1/testdata/acquire-worker-bad-id-POST.json b/server/api/v1/testdata/acquire-worker-bad-id-POST.json new file mode 100644 index 0000000..9a1b539 --- /dev/null +++ b/server/api/v1/testdata/acquire-worker-bad-id-POST.json @@ -0,0 +1 @@ +{"error":"invalid request: ID provided in URL isn't valid"} \ No newline at end of file diff --git a/server/api/v1/testdata/acquire-worker-missing-POST.json b/server/api/v1/testdata/acquire-worker-missing-POST.json new file mode 100644 index 0000000..bb60840 --- /dev/null +++ b/server/api/v1/testdata/acquire-worker-missing-POST.json @@ -0,0 +1 @@ +{"error":"Request not found"} \ No newline at end of file diff --git a/server/api/v1/testdata/acquire-worker-valid-POST.json b/server/api/v1/testdata/acquire-worker-valid-POST.json new file mode 100644 index 0000000..c8ee7f4 --- /dev/null +++ b/server/api/v1/testdata/acquire-worker-valid-POST.json @@ -0,0 +1 @@ +{"Addr":{"IP":"127.0.0.1","Port":22,"Zone":""},"Key":"-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQCyBgKbrwKh75BDoigwltbazFGDLdlxf9YLpFj5v+4ieKgsaN+W\n+kRvamSuB5CC2tqFql5x7kPt1U+vVMwkzVRewF/HHzRYxgLHlge6d1ZALpCWywaz\nslt5pNCmF7NoZ//WTSrafufDI4IRoNgkHtEKvnWdBaPPnY4Cf+PCbZOYNQIDAQAB\nAoGBAJvoz5fxKekQmdPhzDjhocF1d13fZbQVNSx0/seb476k1QQvxMHA5PZ+wzX2\nwgUYDpFJp/U3qp48VtFC/pasjNoG7zLPLLUJcg15eOoh4Ld7I1e4lRkLl3CwnqMk\nbc6UoKQRLli4O3cmaMxVHXal0o72s3o0qnHlRlZXLekwi6aBAkEA69j3bnbAybsF\n/NHelRYDH8bou+LCX2d/p6ReUR0bJ4yCAWRi/9Ld0ng482xinxGSpfovbIplBMFx\nH2eT2Cw0OQJBAME8LLz/3zb/vLG/t8Lfsequ1ZhVca/LlVR4yJLlyaVcywT9SJlO\nmKCy13SpKl8TY7czyufYrY4lobZjYaIsm90CQQCKhkRGWG/BzRymMyp2DJjHKFB4\nUqbx3FuJPqy7HcpeP1P4t1rCgbsSLNTefRGr9mlZHYqPSPYuheQImxCmTshZAkEA\nwAp5u+vfft1yPoT2r+l4/G99P8PLFJcTdbwEOlm8qWcrLW47dIE0FqEml3536b1v\nYGdMxFYHRjoIGSdzpKUI0QJAAqPdDp+y7kWaeIbKkp3Z3bLrj5wii2QAy2YlBDKe\npXrvruWJvL75OCYcxRju3DpVaoYqEmso+UEiQEDRB42YYg==\n-----END RSA PRIVATE KEY-----\n","Username":"wołchw"} \ No newline at end of file -- 2.7.4