diff options
author | Kegsay <kegan@matrix.org> | 2020-07-13 16:02:35 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-13 16:02:35 +0100 |
commit | 396219ef534093b45ad02cccc8ca9cf0f9742c40 (patch) | |
tree | 4cf4eaf0a31cc2a3e42a9bb8be7f7e4d4a17e982 /keyserver | |
parent | 3178afde2c432d5886fa51e1eacfb1e770da39a6 (diff) |
Add boilerplate for key server APIs (#1196)
Also add a README which outilnes how things will work.
Diffstat (limited to 'keyserver')
-rw-r--r-- | keyserver/README.md | 19 | ||||
-rw-r--r-- | keyserver/api/api.go | 49 | ||||
-rw-r--r-- | keyserver/internal/internal.go | 19 | ||||
-rw-r--r-- | keyserver/inthttp/client.go | 103 | ||||
-rw-r--r-- | keyserver/inthttp/server.go | 61 | ||||
-rw-r--r-- | keyserver/keyserver.go | 21 | ||||
-rw-r--r-- | keyserver/routing/keys.go | 33 | ||||
-rw-r--r-- | keyserver/routing/routing.go | 54 |
8 files changed, 264 insertions, 95 deletions
diff --git a/keyserver/README.md b/keyserver/README.md new file mode 100644 index 00000000..fd9f37d2 --- /dev/null +++ b/keyserver/README.md @@ -0,0 +1,19 @@ +## Key Server + +This is an internal component which manages E2E keys from clients. It handles all the [Key Management APIs](https://matrix.org/docs/spec/client_server/r0.6.1#key-management-api) with the exception of `/keys/changes` which is handled by Sync API. This component is designed to shard by user ID. + +Keys are uploaded and stored in this component, and key changes are emitted to a Kafka topic for downstream components such as Sync API. + +### Internal APIs +- `PerformUploadKeys` stores identity keys and one-time public keys for given user(s). +- `PerformClaimKeys` acquires one-time public keys for given user(s). This may involve outbound federation calls. +- `QueryKeys` returns identity keys for given user(s). This may involve outbound federation calls. This component may then cache federated identity keys to avoid repeatedly hitting remote servers. +- A topic which emits identity keys every time there is a change (addition or deletion). + +### Endpoint mappings +- Client API maps `/keys/upload` to `PerformUploadKeys`. +- Client API maps `/keys/query` to `QueryKeys`. +- Client API maps `/keys/claim` to `PerformClaimKeys`. +- Federation API maps `/user/keys/query` to `QueryKeys`. +- Federation API maps `/user/keys/claim` to `PerformClaimKeys`. +- Sync API maps `/keys/changes` to consuming from the Kafka topic. diff --git a/keyserver/api/api.go b/keyserver/api/api.go new file mode 100644 index 00000000..d8b866f3 --- /dev/null +++ b/keyserver/api/api.go @@ -0,0 +1,49 @@ +// Copyright 2020 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package api + +import "context" + +type KeyInternalAPI interface { + PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse) + PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse) + QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse) +} + +// KeyError is returned if there was a problem performing/querying the server +type KeyError struct { + Error string +} + +type PerformUploadKeysRequest struct { +} + +type PerformUploadKeysResponse struct { + Error *KeyError +} + +type PerformClaimKeysRequest struct { +} + +type PerformClaimKeysResponse struct { + Error *KeyError +} + +type QueryKeysRequest struct { +} + +type QueryKeysResponse struct { + Error *KeyError +} diff --git a/keyserver/internal/internal.go b/keyserver/internal/internal.go new file mode 100644 index 00000000..40883cdd --- /dev/null +++ b/keyserver/internal/internal.go @@ -0,0 +1,19 @@ +package internal + +import ( + "context" + + "github.com/matrix-org/dendrite/keyserver/api" +) + +type KeyInternalAPI struct{} + +func (a *KeyInternalAPI) PerformUploadKeys(ctx context.Context, req *api.PerformUploadKeysRequest, res *api.PerformUploadKeysResponse) { + +} +func (a *KeyInternalAPI) PerformClaimKeys(ctx context.Context, req *api.PerformClaimKeysRequest, res *api.PerformClaimKeysResponse) { + +} +func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) { + +} diff --git a/keyserver/inthttp/client.go b/keyserver/inthttp/client.go new file mode 100644 index 00000000..f2d00c70 --- /dev/null +++ b/keyserver/inthttp/client.go @@ -0,0 +1,103 @@ +// Copyright 2020 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package inthttp + +import ( + "context" + "errors" + "net/http" + + "github.com/matrix-org/dendrite/internal/httputil" + "github.com/matrix-org/dendrite/keyserver/api" + "github.com/opentracing/opentracing-go" +) + +// HTTP paths for the internal HTTP APIs +const ( + PerformUploadKeysPath = "/keyserver/performUploadKeys" + PerformClaimKeysPath = "/keyserver/performClaimKeys" + QueryKeysPath = "/keyserver/queryKeys" +) + +// NewKeyServerClient creates a KeyInternalAPI implemented by talking to a HTTP POST API. +// If httpClient is nil an error is returned +func NewKeyServerClient( + apiURL string, + httpClient *http.Client, +) (api.KeyInternalAPI, error) { + if httpClient == nil { + return nil, errors.New("NewKeyServerClient: httpClient is <nil>") + } + return &httpKeyInternalAPI{ + apiURL: apiURL, + httpClient: httpClient, + }, nil +} + +type httpKeyInternalAPI struct { + apiURL string + httpClient *http.Client +} + +func (h *httpKeyInternalAPI) PerformClaimKeys( + ctx context.Context, + request *api.PerformClaimKeysRequest, + response *api.PerformClaimKeysResponse, +) { + span, ctx := opentracing.StartSpanFromContext(ctx, "PerformClaimKeys") + defer span.Finish() + + apiURL := h.apiURL + PerformClaimKeysPath + err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response) + if err != nil { + response.Error = &api.KeyError{ + Error: err.Error(), + } + } +} + +func (h *httpKeyInternalAPI) PerformUploadKeys( + ctx context.Context, + request *api.PerformUploadKeysRequest, + response *api.PerformUploadKeysResponse, +) { + span, ctx := opentracing.StartSpanFromContext(ctx, "PerformUploadKeys") + defer span.Finish() + + apiURL := h.apiURL + PerformUploadKeysPath + err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response) + if err != nil { + response.Error = &api.KeyError{ + Error: err.Error(), + } + } +} + +func (h *httpKeyInternalAPI) QueryKeys( + ctx context.Context, + request *api.QueryKeysRequest, + response *api.QueryKeysResponse, +) { + span, ctx := opentracing.StartSpanFromContext(ctx, "QueryKeys") + defer span.Finish() + + apiURL := h.apiURL + QueryKeysPath + err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response) + if err != nil { + response.Error = &api.KeyError{ + Error: err.Error(), + } + } +} diff --git a/keyserver/inthttp/server.go b/keyserver/inthttp/server.go new file mode 100644 index 00000000..ec78b613 --- /dev/null +++ b/keyserver/inthttp/server.go @@ -0,0 +1,61 @@ +// Copyright 2020 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package inthttp + +import ( + "encoding/json" + "net/http" + + "github.com/gorilla/mux" + "github.com/matrix-org/dendrite/internal/httputil" + "github.com/matrix-org/dendrite/keyserver/api" + "github.com/matrix-org/util" +) + +func AddRoutes(internalAPIMux *mux.Router, s api.KeyInternalAPI) { + internalAPIMux.Handle(PerformClaimKeysPath, + httputil.MakeInternalAPI("performClaimKeys", func(req *http.Request) util.JSONResponse { + request := api.PerformClaimKeysRequest{} + response := api.PerformClaimKeysResponse{} + if err := json.NewDecoder(req.Body).Decode(&request); err != nil { + return util.MessageResponse(http.StatusBadRequest, err.Error()) + } + s.PerformClaimKeys(req.Context(), &request, &response) + return util.JSONResponse{Code: http.StatusOK, JSON: &response} + }), + ) + internalAPIMux.Handle(PerformUploadKeysPath, + httputil.MakeInternalAPI("performUploadKeys", func(req *http.Request) util.JSONResponse { + request := api.PerformUploadKeysRequest{} + response := api.PerformUploadKeysResponse{} + if err := json.NewDecoder(req.Body).Decode(&request); err != nil { + return util.MessageResponse(http.StatusBadRequest, err.Error()) + } + s.PerformUploadKeys(req.Context(), &request, &response) + return util.JSONResponse{Code: http.StatusOK, JSON: &response} + }), + ) + internalAPIMux.Handle(QueryKeysPath, + httputil.MakeInternalAPI("queryKeys", func(req *http.Request) util.JSONResponse { + request := api.QueryKeysRequest{} + response := api.QueryKeysResponse{} + if err := json.NewDecoder(req.Body).Decode(&request); err != nil { + return util.MessageResponse(http.StatusBadRequest, err.Error()) + } + s.QueryKeys(req.Context(), &request, &response) + return util.JSONResponse{Code: http.StatusOK, JSON: &response} + }), + ) +} diff --git a/keyserver/keyserver.go b/keyserver/keyserver.go index bedc4dfb..3bb0e462 100644 --- a/keyserver/keyserver.go +++ b/keyserver/keyserver.go @@ -16,14 +16,19 @@ package keyserver import ( "github.com/gorilla/mux" - "github.com/matrix-org/dendrite/internal/config" - "github.com/matrix-org/dendrite/keyserver/routing" - userapi "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/dendrite/keyserver/api" + "github.com/matrix-org/dendrite/keyserver/internal" + "github.com/matrix-org/dendrite/keyserver/inthttp" ) -// AddPublicRoutes registers HTTP handlers for CS API calls -func AddPublicRoutes( - router *mux.Router, cfg *config.Dendrite, userAPI userapi.UserInternalAPI, -) { - routing.Setup(router, cfg, userAPI) +// AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions +// on the given input API. +func AddInternalRoutes(router *mux.Router, intAPI api.KeyInternalAPI) { + inthttp.AddRoutes(router, intAPI) +} + +// NewInternalAPI returns a concerete implementation of the internal API. Callers +// can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes. +func NewInternalAPI() api.KeyInternalAPI { + return &internal.KeyInternalAPI{} } diff --git a/keyserver/routing/keys.go b/keyserver/routing/keys.go deleted file mode 100644 index a279a747..00000000 --- a/keyserver/routing/keys.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package routing - -import ( - "net/http" - - "github.com/matrix-org/util" -) - -func QueryKeys( - req *http.Request, -) util.JSONResponse { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: map[string]interface{}{ - "failures": map[string]interface{}{}, - "device_keys": map[string]interface{}{}, - }, - } -} diff --git a/keyserver/routing/routing.go b/keyserver/routing/routing.go deleted file mode 100644 index dba43528..00000000 --- a/keyserver/routing/routing.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package routing - -import ( - "net/http" - - "github.com/gorilla/mux" - "github.com/matrix-org/dendrite/internal/config" - "github.com/matrix-org/dendrite/internal/httputil" - userapi "github.com/matrix-org/dendrite/userapi/api" - "github.com/matrix-org/util" -) - -const pathPrefixR0 = "/client/r0" - -// Setup registers HTTP handlers with the given ServeMux. It also supplies the given http.Client -// to clients which need to make outbound HTTP requests. -// -// Due to Setup being used to call many other functions, a gocyclo nolint is -// applied: -// nolint: gocyclo -func Setup( - publicAPIMux *mux.Router, cfg *config.Dendrite, userAPI userapi.UserInternalAPI, -) { - r0mux := publicAPIMux.PathPrefix(pathPrefixR0).Subrouter() - - r0mux.Handle("/keys/query", - httputil.MakeAuthAPI("queryKeys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { - return QueryKeys(req) - }), - ).Methods(http.MethodPost, http.MethodOptions) - - r0mux.Handle("/keys/upload/{keyID}", - httputil.MakeAuthAPI("keys_upload", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { - return util.JSONResponse{ - Code: 200, - JSON: map[string]interface{}{}, - } - }), - ).Methods(http.MethodPost, http.MethodOptions) -} |